blob: 81d29d686a87ab8f2da252e506fec8e2eaf83e12 [file] [log] [blame]
uwe7df6dda2011-09-03 18:37:52 +00001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2011 Sven Schnelle <svens@stackframe.org>
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 *
uwe7df6dda2011-09-03 18:37:52 +000015 */
16
Stefan Tauneree310a42012-03-13 00:18:19 +000017#if CONFIG_LINUX_SPI == 1
18
uwe7df6dda2011-09-03 18:37:52 +000019#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
Nico Huber4b941a92018-12-22 00:08:50 +010022#include <stdint.h>
23#include <inttypes.h>
Gwenhael Goavec-Merouf3f39282015-11-14 02:55:12 +000024#include <fcntl.h>
uwe7df6dda2011-09-03 18:37:52 +000025#include <errno.h>
26#include <ctype.h>
27#include <unistd.h>
Gwenhael Goavec-Merouf3f39282015-11-14 02:55:12 +000028#include <sys/ioctl.h>
Stefan Tauneree310a42012-03-13 00:18:19 +000029#include <linux/types.h>
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +080030#include <sys/stat.h>
31#include <sys/types.h>
David Hendricks98bbc262013-10-30 21:38:58 -070032#include "file.h"
uwe7df6dda2011-09-03 18:37:52 +000033#include "flash.h"
34#include "chipdrivers.h"
35#include "programmer.h"
36#include "spi.h"
Fabrice Fontaine16cb6812019-07-17 19:04:12 +020037/*
38 * Linux versions prior to v4.14-rc7 may need linux/ioctl.h included here due
39 * to missing from linux/spi/spidev.h. This was fixed in the following commit:
40 * a2b4a79b88b2 spi: uapi: spidev: add missing ioctl header
41 */
42#include <linux/ioctl.h>
43#include <linux/spi/spidev.h>
uwe7df6dda2011-09-03 18:37:52 +000044
Louis Yung-Chieh Lo469707f2012-05-18 16:38:37 +080045
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +080046
Louis Yung-Chieh Lo469707f2012-05-18 16:38:37 +080047/* TODO: this information should come from the SPI master driver. */
48#define SPI_DMA_SIZE 64
49
Louis Yung-Chieh Lo282c2412012-07-27 18:24:37 +080050#ifndef SPIDEV_MAJOR
51#define SPIDEV_MAJOR 153 /* refer to kernel/files/drivers/spi/spidev.c */
52#endif
53
David Hendricks98bbc262013-10-30 21:38:58 -070054#define MODALIAS_FILE "modalias"
55#define LINUX_SPI_SYSFS_ROOT "/sys/bus/spi/devices"
Louis Yung-Chieh Lo469707f2012-05-18 16:38:37 +080056
uwe7df6dda2011-09-03 18:37:52 +000057static int fd = -1;
Keno Fischer556e4782015-11-15 14:58:25 +000058#define BUF_SIZE_FROM_SYSFS "/sys/module/spidev/parameters/bufsiz"
59static size_t max_kernel_buf_size;
uwe7df6dda2011-09-03 18:37:52 +000060
David Hendricks93784b42016-08-09 17:00:38 -070061static int linux_spi_shutdown(void *data);
Souvik Ghoshd75cd672016-06-17 14:21:39 -070062static int linux_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
uwe7df6dda2011-09-03 18:37:52 +000063 const unsigned char *txbuf, unsigned char *rxbuf);
Souvik Ghoshd75cd672016-06-17 14:21:39 -070064static int linux_spi_read(struct flashctx *flash, uint8_t *buf,
stefanctc5eb8a92011-11-23 09:13:48 +000065 unsigned int start, unsigned int len);
Patrick Georgiab8353e2017-02-03 18:32:01 +010066static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf,
stefanctc5eb8a92011-11-23 09:13:48 +000067 unsigned int start, unsigned int len);
uwe7df6dda2011-09-03 18:37:52 +000068
Patrick Georgif4f1e2f2017-03-10 17:38:40 +010069static const struct spi_master spi_master_linux = {
Edward O'Callaghana6673bd2019-06-24 15:22:28 +100070 .features = SPI_MASTER_4BA,
uwe7df6dda2011-09-03 18:37:52 +000071 .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */
72 .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */
73 .command = linux_spi_send_command,
74 .multicommand = default_spi_send_multicommand,
75 .read = linux_spi_read,
76 .write_256 = linux_spi_write_256,
77};
78
David Hendricks98bbc262013-10-30 21:38:58 -070079static char devfs_path[32]; /* at least big enough to fit /dev/spidevX.Y */
David Hendricks2d069182015-10-30 22:52:57 -070080static char *check_sysfs(void)
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +080081{
David Hendricks98bbc262013-10-30 21:38:58 -070082 int i;
83 const char *sysfs_path = NULL;
84 char *p;
85 char *modalias[] = {
David Hendricksdca1adf2013-11-01 13:31:57 -070086 "spi:spidev", /* raw access over SPI bus (newer kernels) */
David Hendricks6ed26852013-11-12 12:53:40 -080087 "spidev", /* raw access over SPI bus (older kernels) */
88 "m25p80", /* generic MTD device */
David Hendricks98bbc262013-10-30 21:38:58 -070089 };
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +080090
David Hendricks98bbc262013-10-30 21:38:58 -070091 for (i = 0; i < ARRAY_SIZE(modalias); i++) {
92 int major, minor;
David Hendricks0c0a7e62012-06-12 23:41:05 +090093
David Hendricks98bbc262013-10-30 21:38:58 -070094 /* Path should look like: /sys/blah/spiX.Y/modalias */
95 sysfs_path = scanft(LINUX_SPI_SYSFS_ROOT,
David Hendrickse94c7a82013-11-12 14:22:36 -080096 MODALIAS_FILE, modalias[i], 1);
David Hendricks98bbc262013-10-30 21:38:58 -070097 if (!sysfs_path)
Louis Yung-Chieh Loeaa44fc2012-05-24 15:43:50 +080098 continue;
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +080099
David Hendricks98bbc262013-10-30 21:38:58 -0700100 p = (char *)sysfs_path + strlen(LINUX_SPI_SYSFS_ROOT);
101 if (p[0] == '/')
102 p++;
David Hendricks0c0a7e62012-06-12 23:41:05 +0900103
David Hendricks98bbc262013-10-30 21:38:58 -0700104 if (sscanf(p, "spi%u.%u", &major, &minor) == 2) {
David Hendricksd02ee0f2013-11-01 13:18:48 -0700105 msg_pdbg("Found SPI device %s on spi%u.%u\n",
David Hendricks98bbc262013-10-30 21:38:58 -0700106 modalias[i], major, minor);
107 sprintf(devfs_path, "/dev/spidev%u.%u", major, minor);
108 free((void *)sysfs_path);
109 break;
110 }
111 free((void *)sysfs_path);
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +0800112 }
113
David Hendricks98bbc262013-10-30 21:38:58 -0700114 if (i == ARRAY_SIZE(modalias))
115 return NULL;
116 return devfs_path;
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +0800117}
118
David Hendricks2d069182015-10-30 22:52:57 -0700119static char *check_fdt(void)
120{
121 unsigned int bus, cs;
122
123 if (fdt_find_spi_nor_flash(&bus, &cs) < 0)
124 return NULL;
125
126 sprintf(devfs_path, "/dev/spidev%u.%u", bus, cs);
127 return devfs_path;
128}
129
130static char *linux_spi_probe(void)
131{
132 char *ret;
133
134 ret = check_fdt();
135 if (ret)
136 return ret;
137
138 ret = check_sysfs();
139 if (ret)
140 return ret;
141
142 return NULL;
143}
144
David Hendricksac1d25c2016-08-09 17:00:58 -0700145int linux_spi_init(void)
uwe7df6dda2011-09-03 18:37:52 +0000146{
David Hendricks98b3c572016-11-30 01:50:08 +0000147 char *p, *endp, *dev;
Nico Huber4b941a92018-12-22 00:08:50 +0100148 uint32_t speed_hz = 2 * 1000 * 1000;
Stefan Tauner180d8992012-03-03 18:09:33 +0000149 /* FIXME: make the following configurable by CLI options. */
150 /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */
151 const uint8_t mode = SPI_MODE_0;
152 const uint8_t bits = 8;
uwe7df6dda2011-09-03 18:37:52 +0000153
David Hendricksba0827a2013-05-03 20:25:40 -0700154 /*
155 * FIXME: There might be other programmers with flash memory (such as
156 * an EC) connected via SPI. For now we rely on the device's driver to
157 * distinguish it and assume generic SPI implies host.
158 */
159 if (alias && alias->type != ALIAS_HOST)
160 return 1;
161
uwe7df6dda2011-09-03 18:37:52 +0000162 dev = extract_programmer_param("dev");
David Hendricks98bbc262013-10-30 21:38:58 -0700163 if (!dev)
Louis Yung-Chieh Loa20b3932012-05-22 22:14:00 +0800164 dev = linux_spi_probe();
uwe7df6dda2011-09-03 18:37:52 +0000165 if (!dev || !strlen(dev)) {
David Hendricks98bbc262013-10-30 21:38:58 -0700166 msg_perr("No SPI device found. Use flashrom -p "
uwe7df6dda2011-09-03 18:37:52 +0000167 "linux_spi:dev=/dev/spidevX.Y\n");
David Hendricks98b3c572016-11-30 01:50:08 +0000168 return 1;
uwe7df6dda2011-09-03 18:37:52 +0000169 }
170
171 p = extract_programmer_param("speed");
172 if (p && strlen(p)) {
Alexandru Gagniuc18fe18f2014-03-19 17:17:06 +0000173 speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000;
Nico Huber4b941a92018-12-22 00:08:50 +0100174 if (p == endp || speed_hz == 0) {
uwe7df6dda2011-09-03 18:37:52 +0000175 msg_perr("%s: invalid clock: %s kHz\n", __func__, p);
David Hendricks98b3c572016-11-30 01:50:08 +0000176 return 1;
uwe7df6dda2011-09-03 18:37:52 +0000177 }
Nico Huber4b941a92018-12-22 00:08:50 +0100178 } else {
179 msg_pinfo("Using default %"PRIu32
180 "kHz clock. Use 'spispeed' parameter to override.\n",
181 speed_hz / 1000);
uwe7df6dda2011-09-03 18:37:52 +0000182 }
183
uwe6b716f02011-09-07 20:48:34 +0000184 msg_pdbg("Using device %s\n", dev);
uwe7df6dda2011-09-03 18:37:52 +0000185 if ((fd = open(dev, O_RDWR)) == -1) {
Nikolai Artemiev648f1272020-05-19 15:02:46 +1000186 msg_perr("%s: failed to open %s: %s\n", __func__,
uwe7df6dda2011-09-03 18:37:52 +0000187 dev, strerror(errno));
Louis Yung-Chieh Lo282c2412012-07-27 18:24:37 +0800188
Nikolai Artemiev648f1272020-05-19 15:02:46 +1000189 return 1;
uwe7df6dda2011-09-03 18:37:52 +0000190 }
191
Stefan Tauner180d8992012-03-03 18:09:33 +0000192 if (register_shutdown(linux_spi_shutdown, NULL))
193 return 1;
194 /* We rely on the shutdown function for cleanup from here on. */
195
Nico Huber4b941a92018-12-22 00:08:50 +0100196 if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) {
197 msg_perr("%s: failed to set speed to %"PRIu32"Hz: %s\n",
198 __func__, speed_hz, strerror(errno));
199 return 1;
uwe7df6dda2011-09-03 18:37:52 +0000200 }
Nico Huber4b941a92018-12-22 00:08:50 +0100201 msg_pdbg("Using %"PRIu32"kHz clock\n", speed_hz / 1000);
uwe7df6dda2011-09-03 18:37:52 +0000202
Stefan Tauner180d8992012-03-03 18:09:33 +0000203 if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
204 msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n",
205 __func__, mode, strerror(errno));
David Hendricks98b3c572016-11-30 01:50:08 +0000206 return 1;
Stefan Tauner180d8992012-03-03 18:09:33 +0000207 }
208
209 if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) {
210 msg_perr("%s: failed to set the number of bits per SPI word to %u: %s\n",
211 __func__, bits == 0 ? 8 : bits, strerror(errno));
212 return 1;
213 }
uwe7df6dda2011-09-03 18:37:52 +0000214
Keno Fischer556e4782015-11-15 14:58:25 +0000215 /* Read max buffer size from sysfs, or use page size as fallback. */
216 FILE *fp;
217 fp = fopen(BUF_SIZE_FROM_SYSFS, "r");
218 if (!fp) {
219 msg_pwarn("Cannot open %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno));
220 goto out;
221 }
uwe7df6dda2011-09-03 18:37:52 +0000222
Keno Fischer556e4782015-11-15 14:58:25 +0000223 char buf[10];
Jacob Garber02df5592019-08-12 14:14:40 -0600224 if (!fgets(buf, sizeof(buf), fp)) {
Keno Fischer556e4782015-11-15 14:58:25 +0000225 if (feof(fp))
226 msg_pwarn("Cannot read %s: file is empty.\n", BUF_SIZE_FROM_SYSFS);
227 else
228 msg_pwarn("Cannot read %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno));
229 goto out;
230 }
231
232 long int tmp;
233 errno = 0;
234 tmp = strtol(buf, NULL, 0);
235 if ((tmp < 0) || errno) {
236 msg_pwarn("Buffer size %ld from %s seems wrong.\n", tmp, BUF_SIZE_FROM_SYSFS);
237 } else {
238 msg_pdbg("%s: Using value from %s as max buffer size.\n", __func__, BUF_SIZE_FROM_SYSFS);
239 max_kernel_buf_size = (size_t)tmp;
240 }
241
242out:
243 if (fp)
244 fclose(fp);
245
246 if (!max_kernel_buf_size) {
247 msg_pdbg("%s: Using page size as max buffer size.\n", __func__);
248 max_kernel_buf_size = (size_t)getpagesize();
249 }
250
251 msg_pdbg("%s: max_kernel_buf_size: %zu\n", __func__, max_kernel_buf_size);
252 register_spi_master(&spi_master_linux);
David Hendricks98b3c572016-11-30 01:50:08 +0000253 return 0;
uwe7df6dda2011-09-03 18:37:52 +0000254}
255
David Hendricks93784b42016-08-09 17:00:38 -0700256static int linux_spi_shutdown(void *data)
uwe7df6dda2011-09-03 18:37:52 +0000257{
258 if (fd != -1) {
259 close(fd);
260 fd = -1;
261 }
262 return 0;
263}
264
Souvik Ghoshd75cd672016-06-17 14:21:39 -0700265static int linux_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
uwe7df6dda2011-09-03 18:37:52 +0000266 const unsigned char *txbuf, unsigned char *rxbuf)
267{
Louis Yung-Chieh Lo327c9e92012-05-21 12:02:45 +0800268 int msg_start = 0, msg_count = 0;
uwe7df6dda2011-09-03 18:37:52 +0000269 struct spi_ioc_transfer msg[2] = {
270 {
David Riley13343b82014-07-25 11:37:56 -0700271 .tx_buf = (uint64_t)(uintptr_t)txbuf,
uwe7df6dda2011-09-03 18:37:52 +0000272 .len = writecnt,
273 },
274 {
David Riley13343b82014-07-25 11:37:56 -0700275 .rx_buf = (uint64_t)(uintptr_t)rxbuf,
uwe7df6dda2011-09-03 18:37:52 +0000276 .len = readcnt,
277 },
278 };
279
280 if (fd == -1)
281 return -1;
282
Louis Yung-Chieh Lo327c9e92012-05-21 12:02:45 +0800283 /* Only pass necessary msg[] to ioctl() to avoid the empty message
Louis Yung-Chieh Lob121eff2012-05-22 23:31:03 +0800284 * which drives an un-expected CS line and clocks. */
Louis Yung-Chieh Lo327c9e92012-05-21 12:02:45 +0800285 if (writecnt) {
286 msg_start = 0; /* tx: msg[0] */
287 msg_count++;
288 if (readcnt) {
289 msg_count++;
290 }
Louis Yung-Chieh Lob121eff2012-05-22 23:31:03 +0800291 } else if (readcnt) {
292 msg_start = 1; /* rx: msg[1] */
293 msg_count++;
Louis Yung-Chieh Lo327c9e92012-05-21 12:02:45 +0800294 } else {
Louis Yung-Chieh Lob121eff2012-05-22 23:31:03 +0800295 msg_cerr("%s: both writecnt and readcnt are 0.\n",
296 __func__);
297 return -1;
Louis Yung-Chieh Lo327c9e92012-05-21 12:02:45 +0800298 }
299
300 if (ioctl(fd, SPI_IOC_MESSAGE(msg_count), &msg[msg_start]) == -1) {
uwe7df6dda2011-09-03 18:37:52 +0000301 msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno));
302 return -1;
303 }
304 return 0;
305}
306
Keno Fischer556e4782015-11-15 14:58:25 +0000307static int linux_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
uwe7df6dda2011-09-03 18:37:52 +0000308{
Nico Huberb5ebaaf2018-03-08 16:14:15 +0100309 /* Older kernels use a single buffer for combined input and output
310 data. So account for longest possible command + address, too. */
311 return spi_read_chunked(flash, buf, start, len, max_kernel_buf_size - 5);
uwe7df6dda2011-09-03 18:37:52 +0000312}
313
Patrick Georgiab8353e2017-02-03 18:32:01 +0100314static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf,
stefanctc5eb8a92011-11-23 09:13:48 +0000315 unsigned int start, unsigned int len)
uwe7df6dda2011-09-03 18:37:52 +0000316{
Keno Fischer556e4782015-11-15 14:58:25 +0000317 /* 5 bytes must be reserved for longest possible command + address. */
318 return spi_write_chunked(flash, buf, start, len, max_kernel_buf_size - 5);
uwe7df6dda2011-09-03 18:37:52 +0000319}
Stefan Tauneree310a42012-03-13 00:18:19 +0000320
321#endif // CONFIG_LINUX_SPI == 1