|
|
@@ -1,5 +1,6 @@
|
|
|
/*
|
|
|
* Copyright 2013 bitfury
|
|
|
+ * Copyright 2013 Luke Dashjr
|
|
|
*
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
@@ -22,8 +23,8 @@
|
|
|
|
|
|
#include "spidevc.h"
|
|
|
|
|
|
-#include <stdbool.h>
|
|
|
#include <sys/mman.h>
|
|
|
+#include <stdbool.h>
|
|
|
#include <stdint.h>
|
|
|
#include <unistd.h>
|
|
|
#include <stdio.h>
|
|
|
@@ -44,27 +45,42 @@
|
|
|
|
|
|
#include "logging.h"
|
|
|
|
|
|
+#define HAVE_LINUX_SPI
|
|
|
+
|
|
|
+#ifdef HAVE_LINUX_SPI
|
|
|
+static bool sys_spi_txrx(struct spi_port *port);
|
|
|
static volatile unsigned *gpio;
|
|
|
+#endif
|
|
|
|
|
|
-bool spi_init(void)
|
|
|
+struct spi_port *sys_spi;
|
|
|
+
|
|
|
+void spi_init(void)
|
|
|
{
|
|
|
+#ifdef HAVE_LINUX_SPI
|
|
|
int fd;
|
|
|
fd = open("/dev/mem",O_RDWR|O_SYNC);
|
|
|
if (fd < 0)
|
|
|
{
|
|
|
perror("/dev/mem trouble");
|
|
|
- return false;
|
|
|
+ return;
|
|
|
}
|
|
|
gpio = mmap(0,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0x20200000);
|
|
|
if (gpio == MAP_FAILED)
|
|
|
{
|
|
|
perror("gpio mmap trouble");
|
|
|
- return false;
|
|
|
+ return;
|
|
|
}
|
|
|
close(fd);
|
|
|
- return true;
|
|
|
+
|
|
|
+ sys_spi = malloc(sizeof(*sys_spi));
|
|
|
+ *sys_spi = (struct spi_port){
|
|
|
+ .txrx = sys_spi_txrx,
|
|
|
+ };
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
+#ifdef HAVE_LINUX_SPI
|
|
|
+
|
|
|
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
|
|
#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
|
|
|
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
|
|
@@ -73,6 +89,7 @@ bool spi_init(void)
|
|
|
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
|
|
|
|
|
|
// Bit-banging reset, to reset more chips in chain - toggle for longer period... Each 3 reset cycles reset first chip in chain
|
|
|
+static
|
|
|
void spi_reset(void)
|
|
|
{
|
|
|
int i;
|
|
|
@@ -115,8 +132,18 @@ void spi_reset(void)
|
|
|
SET_GPIO_ALT(9,0);
|
|
|
}
|
|
|
|
|
|
-int spi_txrx(const void *wrbuf, void *rdbuf, size_t bufsz)
|
|
|
+#define BAILOUT(s) do{ \
|
|
|
+ perror(s); \
|
|
|
+ close(fd); \
|
|
|
+ return false; \
|
|
|
+}while(0)
|
|
|
+
|
|
|
+static
|
|
|
+bool sys_spi_txrx(struct spi_port *port)
|
|
|
{
|
|
|
+ const void *wrbuf = spi_gettxbuf(port);
|
|
|
+ void *rdbuf = spi_getrxbuf(port);
|
|
|
+ size_t bufsz = spi_getbufsz(port);
|
|
|
int fd;
|
|
|
int mode, bits, speed, rv, i, j;
|
|
|
struct spi_ioc_transfer tr[16];
|
|
|
@@ -126,13 +153,22 @@ int spi_txrx(const void *wrbuf, void *rdbuf, size_t bufsz)
|
|
|
|
|
|
spi_reset();
|
|
|
fd = open("/dev/spidev0.0", O_RDWR);
|
|
|
- if (fd < 0) { perror("Unable to open SPI device"); exit(1); }
|
|
|
- if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { perror("Unable to set WR MODE"); close(fd); return -1; }
|
|
|
- if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0) { perror("Unable to set RD MODE"); close(fd); return -1; }
|
|
|
- if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) { perror("Unable to set WR_BITS_PER_WORD"); close(fd); return -1; }
|
|
|
- if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) { perror("Unable to set RD_BITS_PER_WORD"); close(fd); return -1; }
|
|
|
- if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) { perror("Unable to set WR_MAX_SPEED_HZ"); close(fd); return -1; }
|
|
|
- if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) { perror("Unable to set RD_MAX_SPEED_HZ"); close(fd); return -1; }
|
|
|
+ if (fd < 0) {
|
|
|
+ perror("Unable to open SPI device");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0)
|
|
|
+ BAILOUT("Unable to set WR MODE");
|
|
|
+ if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0)
|
|
|
+ BAILOUT("Unable to set RD MODE");
|
|
|
+ if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0)
|
|
|
+ BAILOUT("Unable to set WR_BITS_PER_WORD");
|
|
|
+ if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0)
|
|
|
+ BAILOUT("Unable to set RD_BITS_PER_WORD");
|
|
|
+ if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
|
|
|
+ BAILOUT("Unable to set WR_MAX_SPEED_HZ");
|
|
|
+ if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0)
|
|
|
+ BAILOUT("Unable to set RD_MAX_SPEED_HZ");
|
|
|
|
|
|
rv = 0;
|
|
|
while (bufsz >= 4096) {
|
|
|
@@ -158,61 +194,69 @@ int spi_txrx(const void *wrbuf, void *rdbuf, size_t bufsz)
|
|
|
i = rv;
|
|
|
for (j = 0; j < i; j++) {
|
|
|
rv = (int)ioctl(fd, SPI_IOC_MESSAGE(1), (intptr_t)&tr[j]);
|
|
|
- if (rv < 0) { perror("WTF!"); close(fd); return -1; }
|
|
|
+ if (rv < 0)
|
|
|
+ BAILOUT("WTF!");
|
|
|
}
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
- return 0;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
-#define SPIMAXSZ 256*1024
|
|
|
-static unsigned char spibuf[SPIMAXSZ], spibuf_rx[SPIMAXSZ];
|
|
|
-static unsigned spibufsz;
|
|
|
-
|
|
|
-void spi_clear_buf(void) { spibufsz = 0; }
|
|
|
-unsigned char *spi_getrxbuf(void) { return spibuf_rx; }
|
|
|
-unsigned char *spi_gettxbuf(void) { return spibuf; }
|
|
|
-unsigned spi_getbufsz(void) { return spibufsz; }
|
|
|
+#endif
|
|
|
|
|
|
-void spi_emit_buf_reverse(const char *str, unsigned sz)
|
|
|
+static
|
|
|
+void spi_emit_buf_reverse(struct spi_port *port, void *p, size_t sz)
|
|
|
{
|
|
|
- unsigned i;
|
|
|
- if (spibufsz + sz >= SPIMAXSZ) return;
|
|
|
- for (i = 0; i < sz; i++) { // Reverse bit order in each byte!
|
|
|
+ unsigned char *str = p;
|
|
|
+ if (port->spibufsz + sz >= SPIMAXSZ)
|
|
|
+ return;
|
|
|
+ for (size_t i = 0; i < sz; ++i)
|
|
|
+ {
|
|
|
+ // Reverse bit order in each byte!
|
|
|
unsigned char p = str[i];
|
|
|
p = ((p & 0xaa)>>1) | ((p & 0x55) << 1);
|
|
|
p = ((p & 0xcc)>>2) | ((p & 0x33) << 2);
|
|
|
p = ((p & 0xf0)>>4) | ((p & 0x0f) << 4);
|
|
|
- spibuf[spibufsz+i] = p;
|
|
|
+ port->spibuf[port->spibufsz++] = p;
|
|
|
}
|
|
|
- spibufsz += sz;
|
|
|
}
|
|
|
|
|
|
-void spi_emit_buf(void *str, unsigned sz)
|
|
|
+static
|
|
|
+void spi_emit_buf(struct spi_port *port, void *str, size_t sz)
|
|
|
{
|
|
|
- if (spibufsz + sz >= SPIMAXSZ) return;
|
|
|
- memcpy(&spibuf[spibufsz], str, sz); spibufsz += sz;
|
|
|
+ if (port->spibufsz + sz >= SPIMAXSZ)
|
|
|
+ return;
|
|
|
+ memcpy(&port->spibuf[port->spibufsz], str, sz);
|
|
|
+ port->spibufsz += sz;
|
|
|
}
|
|
|
|
|
|
/* TODO: in production, emit just bit-sequences! Eliminate padding to byte! */
|
|
|
-void spi_emit_break(void) { spi_emit_buf("\x4", 1); }
|
|
|
-void spi_emit_fsync(void) { spi_emit_buf("\x6", 1); }
|
|
|
+void spi_emit_break(struct spi_port *port)
|
|
|
+{
|
|
|
+ spi_emit_buf(port, "\x4", 1);
|
|
|
+}
|
|
|
|
|
|
-void spi_emit_fasync(int n) {
|
|
|
+void spi_emit_fsync(struct spi_port *port)
|
|
|
+{
|
|
|
+ spi_emit_buf(port, "\x6", 1);
|
|
|
+}
|
|
|
+
|
|
|
+void spi_emit_fasync(struct spi_port *port, int n)
|
|
|
+{
|
|
|
int i;
|
|
|
for (i = 0; i < n; i++) {
|
|
|
- spi_emit_buf("\x5", 1);
|
|
|
+ spi_emit_buf(port, "\x5", 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void spi_emit_data(unsigned addr, const char *buf, unsigned len)
|
|
|
+void spi_emit_data(struct spi_port *port, uint16_t addr, void *buf, size_t len)
|
|
|
{
|
|
|
unsigned char otmp[3];
|
|
|
if (len < 4 || len > 128) return; /* This cannot be programmed in single frame! */
|
|
|
len /= 4; /* Strip */
|
|
|
otmp[0] = (len - 1) | 0xE0;
|
|
|
otmp[1] = (addr >> 8)&0xFF; otmp[2] = addr & 0xFF;
|
|
|
- spi_emit_buf(otmp, 3);
|
|
|
- spi_emit_buf_reverse(buf, len*4);
|
|
|
+ spi_emit_buf(port, otmp, 3);
|
|
|
+ spi_emit_buf_reverse(port, buf, len*4);
|
|
|
}
|