blob: e4ab6f6fd8bc910778331fada3e4f8b9cb8f8aad [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>
Daniel Thompson45e91a22018-06-04 13:46:29 +010037#include <libusb.h>
38#include "programmer.h"
39#include "spi.h"
40
41/* Bit positions for each pin. */
42#define DEVELOPERBOX_SPI_SCK 0
43#define DEVELOPERBOX_SPI_CS 1
44#define DEVELOPERBOX_SPI_MISO 2
45#define DEVELOPERBOX_SPI_MOSI 3
46
47/* Config request types */
48#define REQTYPE_HOST_TO_DEVICE 0x40
49#define REQTYPE_DEVICE_TO_HOST 0xc0
50
51/* Config request codes */
52#define CP210X_VENDOR_SPECIFIC 0xff
53
54/* CP210X_VENDOR_SPECIFIC */
55#define CP210X_WRITE_LATCH 0x37e1
56#define CP210X_READ_LATCH 0x00c2
57
58const struct dev_entry devs_developerbox_spi[] = {
59 {0x10c4, 0xea60, OK, "Silicon Labs", "CP2102N USB to UART Bridge Controller"},
60 {0},
61};
62
Anastasia Klimchuk62734722021-05-26 12:50:59 +100063struct devbox_spi_data {
64 struct libusb_context *usb_ctx;
65 libusb_device_handle *cp210x_handle;
66};
Daniel Thompson45e91a22018-06-04 13:46:29 +010067
Anastasia Klimchuk62734722021-05-26 12:50:59 +100068static int cp210x_gpio_get(void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +010069{
70 int res;
71 uint8_t gpio;
Anastasia Klimchuk62734722021-05-26 12:50:59 +100072 struct devbox_spi_data *data = spi_data;
Daniel Thompson45e91a22018-06-04 13:46:29 +010073
Anastasia Klimchuk62734722021-05-26 12:50:59 +100074 res = libusb_control_transfer(data->cp210x_handle, REQTYPE_DEVICE_TO_HOST,
Daniel Thompson45e91a22018-06-04 13:46:29 +010075 CP210X_VENDOR_SPECIFIC, CP210X_READ_LATCH,
76 0, &gpio, 1, 0);
77 if (res < 0) {
78 msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
79 return 0;
80 }
81
82 return gpio;
83}
84
Anastasia Klimchuk62734722021-05-26 12:50:59 +100085static void cp210x_gpio_set(uint8_t val, uint8_t mask, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +010086{
87 int res;
88 uint16_t gpio;
Anastasia Klimchuk62734722021-05-26 12:50:59 +100089 struct devbox_spi_data *data = spi_data;
Daniel Thompson45e91a22018-06-04 13:46:29 +010090
91 gpio = ((val & 0xf) << 8) | (mask & 0xf);
92
93 /* Set relay state on the card */
Anastasia Klimchuk62734722021-05-26 12:50:59 +100094 res = libusb_control_transfer(data->cp210x_handle, REQTYPE_HOST_TO_DEVICE,
Daniel Thompson45e91a22018-06-04 13:46:29 +010095 CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH,
96 gpio, NULL, 0, 0);
97 if (res < 0)
98 msg_perr("Failed to read GPIO pins (%s)\n", libusb_error_name(res));
99}
100
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +1000101static void cp210x_bitbang_set_cs(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100102{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000103 cp210x_gpio_set(val << DEVELOPERBOX_SPI_CS, 1 << DEVELOPERBOX_SPI_CS, spi_data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100104}
105
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +1000106static void cp210x_bitbang_set_sck(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100107{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000108 cp210x_gpio_set(val << DEVELOPERBOX_SPI_SCK, 1 << DEVELOPERBOX_SPI_SCK, spi_data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100109}
110
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +1000111static void cp210x_bitbang_set_mosi(int val, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100112{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000113 cp210x_gpio_set(val << DEVELOPERBOX_SPI_MOSI, 1 << DEVELOPERBOX_SPI_MOSI, spi_data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100114}
115
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +1000116static int cp210x_bitbang_get_miso(void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100117{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000118 return !!(cp210x_gpio_get(spi_data) & (1 << DEVELOPERBOX_SPI_MISO));
Daniel Thompson45e91a22018-06-04 13:46:29 +0100119}
120
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +1000121static void cp210x_bitbang_set_sck_set_mosi(int sck, int mosi, void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100122{
123 cp210x_gpio_set(sck << DEVELOPERBOX_SPI_SCK | mosi << DEVELOPERBOX_SPI_MOSI,
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000124 1 << DEVELOPERBOX_SPI_SCK | 1 << DEVELOPERBOX_SPI_MOSI,
125 spi_data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100126}
127
128static const struct bitbang_spi_master bitbang_spi_master_cp210x = {
Daniel Thompson45e91a22018-06-04 13:46:29 +0100129 .set_cs = cp210x_bitbang_set_cs,
130 .set_sck = cp210x_bitbang_set_sck,
131 .set_mosi = cp210x_bitbang_set_mosi,
132 .get_miso = cp210x_bitbang_get_miso,
133 .set_sck_set_mosi = cp210x_bitbang_set_sck_set_mosi,
134};
135
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000136static int developerbox_spi_shutdown(void *spi_data)
Daniel Thompson45e91a22018-06-04 13:46:29 +0100137{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000138 struct devbox_spi_data *data = spi_data;
Daniel Thompson45e91a22018-06-04 13:46:29 +0100139
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000140 libusb_close(data->cp210x_handle);
141 libusb_exit(data->usb_ctx);
142
143 free(data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100144 return 0;
145}
146
147int developerbox_spi_init(void)
148{
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000149 struct libusb_context *usb_ctx;
150 libusb_device_handle *cp210x_handle;
151
Daniel Thompson45e91a22018-06-04 13:46:29 +0100152 libusb_init(&usb_ctx);
153 if (!usb_ctx) {
154 msg_perr("Could not initialize libusb!\n");
155 return 1;
156 }
157
158 char *serialno = extract_programmer_param("serial");
159 if (serialno)
160 msg_pdbg("Looking for serial number commencing %s\n", serialno);
Daniel Thompson1d507a02018-07-12 11:02:28 +0100161 cp210x_handle = usb_dev_get_by_vid_pid_serial(usb_ctx,
Daniel Thompson45e91a22018-06-04 13:46:29 +0100162 devs_developerbox_spi[0].vendor_id, devs_developerbox_spi[0].device_id, serialno);
163 free(serialno);
164 if (!cp210x_handle) {
165 msg_perr("Could not find a Developerbox programmer on USB.\n");
166 goto err_exit;
167 }
168
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000169 struct devbox_spi_data *data = calloc(1, sizeof(*data));
170 if (!data) {
171 msg_perr("Unable to allocate space for SPI master data\n");
Daniel Thompson45e91a22018-06-04 13:46:29 +0100172 goto err_exit;
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000173 }
174 data->usb_ctx = usb_ctx;
175 data->cp210x_handle = cp210x_handle;
Daniel Thompson45e91a22018-06-04 13:46:29 +0100176
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000177 if (register_shutdown(developerbox_spi_shutdown, data)) {
178 free(data);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100179 goto err_exit;
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000180 }
181
182 if (register_spi_bitbang_master(&bitbang_spi_master_cp210x, data))
183 return 1; /* shutdown function does the cleanup */
Daniel Thompson45e91a22018-06-04 13:46:29 +0100184
185 return 0;
186
187err_exit:
Anastasia Klimchuk62734722021-05-26 12:50:59 +1000188 if (cp210x_handle)
189 libusb_close(cp210x_handle);
Daniel Thompson45e91a22018-06-04 13:46:29 +0100190 libusb_exit(usb_ctx);
191 return 1;
192}