blob: 4efa309e96fae6d7f24d4e8b9943799966e82066 [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
Daniel Thompson45e91a22018-06-04 13:46:29 +010034#include <stdlib.h>
Daniel Thompson45e91a22018-06-04 13:46:29 +010035#include <libusb.h>
36#include "programmer.h"
Nico Huberd16a9112024-01-07 00:11:44 +010037#include "bitbang_spi.h"
Daniel Thompson45e91a22018-06-04 13:46:29 +010038#include "spi.h"
39
40/* Bit positions for each pin. */
41#define DEVELOPERBOX_SPI_SCK 0
42#define DEVELOPERBOX_SPI_CS 1
43#define DEVELOPERBOX_SPI_MISO 2
44#define DEVELOPERBOX_SPI_MOSI 3
45
46/* Config request types */
47#define REQTYPE_HOST_TO_DEVICE 0x40
48#define REQTYPE_DEVICE_TO_HOST 0xc0
49
50/* Config request codes */
51#define CP210X_VENDOR_SPECIFIC 0xff
52
53/* CP210X_VENDOR_SPECIFIC */
54#define CP210X_WRITE_LATCH 0x37e1
55#define CP210X_READ_LATCH 0x00c2
56
Thomas Heijligencc853d82021-05-04 15:32:17 +020057static const struct dev_entry devs_developerbox_spi[] = {
Daniel Thompson45e91a22018-06-04 13:46:29 +010058 {0x10c4, 0xea60, OK, "Silicon Labs", "CP2102N USB to UART Bridge Controller"},
59 {0},
60};
61
Jacob Garberafc3ad62019-06-24 16:05:28 -060062static struct libusb_context *usb_ctx;
Daniel Thompson45e91a22018-06-04 13:46:29 +010063static libusb_device_handle *cp210x_handle;
64
65static int cp210x_gpio_get(void)
66{
67 int res;
68 uint8_t gpio;
69
70 res = libusb_control_transfer(cp210x_handle, REQTYPE_DEVICE_TO_HOST,
71 CP210X_VENDOR_SPECIFIC, CP210X_READ_LATCH,
72 0, &gpio, 1, 0);
73 if (res < 0) {
74 msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
75 return 0;
76 }
77
78 return gpio;
79}
80
81static void cp210x_gpio_set(uint8_t val, uint8_t mask)
82{
83 int res;
84 uint16_t gpio;
85
86 gpio = ((val & 0xf) << 8) | (mask & 0xf);
87
88 /* Set relay state on the card */
89 res = libusb_control_transfer(cp210x_handle, REQTYPE_HOST_TO_DEVICE,
90 CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH,
91 gpio, NULL, 0, 0);
92 if (res < 0)
93 msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
94}
95
Anastasia Klimchuk0e788182021-05-26 09:54:08 +100096static void cp210x_bitbang_set_cs(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +010097{
98 cp210x_gpio_set(val << DEVELOPERBOX_SPI_CS, 1 << DEVELOPERBOX_SPI_CS);
99}
100
Anastasia Klimchuk0e788182021-05-26 09:54:08 +1000101static void cp210x_bitbang_set_sck(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100102{
103 cp210x_gpio_set(val << DEVELOPERBOX_SPI_SCK, 1 << DEVELOPERBOX_SPI_SCK);
104}
105
Anastasia Klimchuk0e788182021-05-26 09:54:08 +1000106static void cp210x_bitbang_set_mosi(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100107{
108 cp210x_gpio_set(val << DEVELOPERBOX_SPI_MOSI, 1 << DEVELOPERBOX_SPI_MOSI);
109}
110
Anastasia Klimchuk0e788182021-05-26 09:54:08 +1000111static int cp210x_bitbang_get_miso(void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100112{
113 return !!(cp210x_gpio_get() & (1 << DEVELOPERBOX_SPI_MISO));
114}
115
Anastasia Klimchuk0e788182021-05-26 09:54:08 +1000116static void cp210x_bitbang_set_sck_set_mosi(int sck, int mosi, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100117{
118 cp210x_gpio_set(sck << DEVELOPERBOX_SPI_SCK | mosi << DEVELOPERBOX_SPI_MOSI,
119 1 << DEVELOPERBOX_SPI_SCK | 1 << DEVELOPERBOX_SPI_MOSI);
120}
121
122static const struct bitbang_spi_master bitbang_spi_master_cp210x = {
Thomas Heijligen43040f22022-06-23 14:38:35 +0200123 .set_cs = cp210x_bitbang_set_cs,
124 .set_sck = cp210x_bitbang_set_sck,
125 .set_mosi = cp210x_bitbang_set_mosi,
126 .get_miso = cp210x_bitbang_get_miso,
127 .set_sck_set_mosi = cp210x_bitbang_set_sck_set_mosi,
Daniel Thompson45e91a22018-06-04 13:46:29 +0100128};
129
Daniel Thompson45e91a22018-06-04 13:46:29 +0100130static int developerbox_spi_shutdown(void *data)
131{
132 libusb_close(cp210x_handle);
133 libusb_exit(usb_ctx);
134
135 return 0;
136}
137
Nico Hubere3a26882023-01-11 21:45:51 +0100138static int developerbox_spi_init(struct flashprog_programmer *const prog)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100139{
Thomas Heijligen88e87c52022-08-05 17:56:20 +0200140 if (libusb_init(&usb_ctx)) {
Daniel Thompson45e91a22018-06-04 13:46:29 +0100141 msg_perr("Could not initialize libusb!\n");
142 return 1;
143 }
144
145 char *serialno = extract_programmer_param("serial");
146 if (serialno)
147 msg_pdbg("Looking for serial number commencing %s\n", serialno);
Daniel Thompson1d507a02018-07-12 11:02:28 +0100148 cp210x_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
Daniel Thompson45e91a22018-06-04 13:46:29 +0100149 devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno);
150 free(serialno);
151 if (!cp210x_handle) {
152 msg_perr("Could not find a Developerbox programmer on USB.\n");
153 goto err_exit;
154 }
155
156 if (register_shutdown(developerbox_spi_shutdown, NULL))
157 goto err_exit;
158
Anastasia Klimchuka447c122021-05-31 11:20:01 +1000159 if (register_spi_bitbang_master(&bitbang_spi_master_cp210x, NULL))
Daniel Thompson45e91a22018-06-04 13:46:29 +0100160 goto err_exit;
161
162 return 0;
163
164err_exit:
165 libusb_exit(usb_ctx);
166 return 1;
167}
Thomas Heijligencc853d82021-05-04 15:32:17 +0200168
169const struct programmer_entry programmer_developerbox = {
170 .name = "developerbox",
171 .type = USB,
172 .devs.dev = devs_developerbox_spi,
173 .init = developerbox_spi_init,
Thomas Heijligencc853d82021-05-04 15:32:17 +0200174};