Merge remote-tracking branches 'spi/topic/overlay', 'spi/topic/pxa2xx', 'spi/topic/s3c64xx', 'spi/topic/sh-msiof' and 'spi/topic/spidev' into spi-next

This commit is contained in:
Mark Brown 2016-01-11 16:48:35 +00:00
16 changed files with 160 additions and 80 deletions

View File

@ -1,4 +1,4 @@
subdir-y := accounting auxdisplay blackfin connector \ subdir-y := accounting auxdisplay blackfin connector \
filesystems filesystems ia64 laptops mic misc-devices \ filesystems filesystems ia64 laptops mic misc-devices \
networking pcmcia prctl ptp spi timers vDSO video4linux \ networking pcmcia prctl ptp timers vDSO video4linux \
watchdog watchdog

View File

@ -10,6 +10,7 @@ Required properties:
"renesas,msiof-r8a7792" (R-Car V2H) "renesas,msiof-r8a7792" (R-Car V2H)
"renesas,msiof-r8a7793" (R-Car M2-N) "renesas,msiof-r8a7793" (R-Car M2-N)
"renesas,msiof-r8a7794" (R-Car E2) "renesas,msiof-r8a7794" (R-Car E2)
"renesas,msiof-sh73a0" (SH-Mobile AG5)
- reg : A list of offsets and lengths of the register sets for - reg : A list of offsets and lengths of the register sets for
the device. the device.
If only one register set is present, it is to be used If only one register set is present, it is to be used

View File

@ -10,13 +10,9 @@ pxa2xx
- PXA2xx SPI master controller build by spi_message fifo wq - PXA2xx SPI master controller build by spi_message fifo wq
spidev spidev
- Intro to the userspace API for spi devices - Intro to the userspace API for spi devices
spidev_fdx.c
- spidev example file
spi-lm70llp spi-lm70llp
- Connecting an LM70-LLP sensor to the kernel via the SPI subsys. - Connecting an LM70-LLP sensor to the kernel via the SPI subsys.
spi-sc18is602 spi-sc18is602
- NXP SC18IS602/603 I2C-bus to SPI bridge - NXP SC18IS602/603 I2C-bus to SPI bridge
spi-summary spi-summary
- (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here. - (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here.
spidev_test.c
- SPI testing utility.

View File

@ -1,8 +0,0 @@
# List of programs to build
hostprogs-y := spidev_test spidev_fdx
# Tell kbuild to always build the programs
always := $(hostprogs-y)
HOSTCFLAGS_spidev_test.o += -I$(objtree)/usr/include
HOSTCFLAGS_spidev_fdx.o += -I$(objtree)/usr/include

View File

@ -1100,9 +1100,7 @@ struct platform_device s3c_device_wdt = {
#ifdef CONFIG_S3C64XX_DEV_SPI0 #ifdef CONFIG_S3C64XX_DEV_SPI0
static struct resource s3c64xx_spi0_resource[] = { static struct resource s3c64xx_spi0_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI0_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI0),
[2] = DEFINE_RES_DMA(DMACH_SPI0_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI0),
}; };
struct platform_device s3c64xx_device_spi0 = { struct platform_device s3c64xx_device_spi0 = {
@ -1130,6 +1128,8 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI0_TX;
pd.dma_rx = (void *)DMACH_SPI0_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)
@ -1145,9 +1145,7 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI1 #ifdef CONFIG_S3C64XX_DEV_SPI1
static struct resource s3c64xx_spi1_resource[] = { static struct resource s3c64xx_spi1_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI1_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI1),
[2] = DEFINE_RES_DMA(DMACH_SPI1_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI1),
}; };
struct platform_device s3c64xx_device_spi1 = { struct platform_device s3c64xx_device_spi1 = {
@ -1175,12 +1173,15 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI1_TX;
pd.dma_rx = (void *)DMACH_SPI1_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)
pd.filter = pl08x_filter_id; pd.filter = pl08x_filter_id;
#endif #endif
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1); s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1);
} }
#endif /* CONFIG_S3C64XX_DEV_SPI1 */ #endif /* CONFIG_S3C64XX_DEV_SPI1 */
@ -1188,9 +1189,7 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI2 #ifdef CONFIG_S3C64XX_DEV_SPI2
static struct resource s3c64xx_spi2_resource[] = { static struct resource s3c64xx_spi2_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI2_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI2),
[2] = DEFINE_RES_DMA(DMACH_SPI2_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI2),
}; };
struct platform_device s3c64xx_device_spi2 = { struct platform_device s3c64xx_device_spi2 = {
@ -1218,6 +1217,8 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI2_TX;
pd.dma_rx = (void *)DMACH_SPI2_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)

View File

@ -1567,9 +1567,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
if (!is_quark_x1000_ssp(drv_data)) if (!is_quark_x1000_ssp(drv_data))
pxa2xx_spi_write(drv_data, SSPSP, 0); pxa2xx_spi_write(drv_data, SSPSP, 0);
if (is_lpss_ssp(drv_data))
lpss_ssp_setup(drv_data);
if (is_lpss_ssp(drv_data)) { if (is_lpss_ssp(drv_data)) {
lpss_ssp_setup(drv_data); lpss_ssp_setup(drv_data);
config = lpss_get_config(drv_data); config = lpss_get_config(drv_data);

View File

@ -133,7 +133,6 @@
struct s3c64xx_spi_dma_data { struct s3c64xx_spi_dma_data {
struct dma_chan *ch; struct dma_chan *ch;
enum dma_transfer_direction direction; enum dma_transfer_direction direction;
unsigned int dmach;
}; };
/** /**
@ -325,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
/* Acquire DMA channels */ /* Acquire DMA channels */
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
(void *)(long)sdd->rx_dma.dmach, dev, "rx"); sdd->cntrlr_info->dma_rx, dev, "rx");
if (!sdd->rx_dma.ch) { if (!sdd->rx_dma.ch) {
dev_err(dev, "Failed to get RX DMA channel\n"); dev_err(dev, "Failed to get RX DMA channel\n");
ret = -EBUSY; ret = -EBUSY;
@ -334,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
spi->dma_rx = sdd->rx_dma.ch; spi->dma_rx = sdd->rx_dma.ch;
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
(void *)(long)sdd->tx_dma.dmach, dev, "tx"); sdd->cntrlr_info->dma_tx, dev, "tx");
if (!sdd->tx_dma.ch) { if (!sdd->tx_dma.ch) {
dev_err(dev, "Failed to get TX DMA channel\n"); dev_err(dev, "Failed to get TX DMA channel\n");
ret = -EBUSY; ret = -EBUSY;
@ -1028,7 +1027,6 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
static int s3c64xx_spi_probe(struct platform_device *pdev) static int s3c64xx_spi_probe(struct platform_device *pdev)
{ {
struct resource *mem_res; struct resource *mem_res;
struct resource *res;
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
struct spi_master *master; struct spi_master *master;
@ -1087,20 +1085,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cur_bpw = 8; sdd->cur_bpw = 8;
if (!sdd->pdev->dev.of_node) { if (!sdd->pdev->dev.of_node && (!sci->dma_tx || !sci->dma_rx)) {
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); dev_warn(&pdev->dev, "Unable to get SPI tx/rx DMA data. Switching to poll mode\n");
if (!res) { sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->tx_dma.dmach = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->rx_dma.dmach = res->start;
} }
sdd->tx_dma.direction = DMA_MEM_TO_DEV; sdd->tx_dma.direction = DMA_MEM_TO_DEV;
@ -1197,9 +1184,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
sdd->port_id, master->num_chipselect); sdd->port_id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%p, Tx-%p]\n",
mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1,
sdd->rx_dma.dmach, sdd->tx_dma.dmach); sci->dma_rx, sci->dma_tx);
pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev);
@ -1370,12 +1357,6 @@ static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
}, { }, {
.name = "s3c6410-spi", .name = "s3c6410-spi",
.driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
}, {
.name = "s5pv210-spi",
.driver_data = (kernel_ulong_t)&s5pv210_spi_port_config,
}, {
.name = "exynos4210-spi",
.driver_data = (kernel_ulong_t)&exynos4_spi_port_config,
}, },
{ }, { },
}; };

View File

@ -604,6 +604,24 @@ struct spi_device *spi_new_device(struct spi_master *master,
} }
EXPORT_SYMBOL_GPL(spi_new_device); EXPORT_SYMBOL_GPL(spi_new_device);
/**
* spi_unregister_device - unregister a single SPI device
* @spi: spi_device to unregister
*
* Start making the passed SPI device vanish. Normally this would be handled
* by spi_unregister_master().
*/
void spi_unregister_device(struct spi_device *spi)
{
if (!spi)
return;
if (spi->dev.of_node)
of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
device_unregister(&spi->dev);
}
EXPORT_SYMBOL_GPL(spi_unregister_device);
static void spi_match_master_to_boardinfo(struct spi_master *master, static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi) struct spi_board_info *bi)
{ {
@ -1547,6 +1565,8 @@ static void of_register_spi_devices(struct spi_master *master)
return; return;
for_each_available_child_of_node(master->dev.of_node, nc) { for_each_available_child_of_node(master->dev.of_node, nc) {
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(master, nc); spi = of_register_spi_device(master, nc);
if (IS_ERR(spi)) if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n", dev_warn(&master->dev, "Failed to create SPI device for %s\n",
@ -2632,6 +2652,11 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
if (master == NULL) if (master == NULL)
return NOTIFY_OK; /* not for us */ return NOTIFY_OK; /* not for us */
if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
put_device(&master->dev);
return NOTIFY_OK;
}
spi = of_register_spi_device(master, rd->dn); spi = of_register_spi_device(master, rd->dn);
put_device(&master->dev); put_device(&master->dev);
@ -2643,6 +2668,10 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
break; break;
case OF_RECONFIG_CHANGE_REMOVE: case OF_RECONFIG_CHANGE_REMOVE:
/* already depopulated? */
if (!of_node_check_flag(rd->dn, OF_POPULATED))
return NOTIFY_OK;
/* find our device by node */ /* find our device by node */
spi = of_find_spi_device_by_node(rd->dn); spi = of_find_spi_device_by_node(rd->dn);
if (spi == NULL) if (spi == NULL)

View File

@ -284,7 +284,7 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->speed_hz = spidev->speed_hz; k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE #ifdef VERBOSE
dev_dbg(&spidev->spi->dev, dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n", " xfer len %u %s%s%s%dbits %u usec %uHz\n",
u_tmp->len, u_tmp->len,
u_tmp->rx_buf ? "rx " : "", u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "", u_tmp->tx_buf ? "tx " : "",

View File

@ -40,6 +40,8 @@ struct s3c64xx_spi_info {
int num_cs; int num_cs;
int (*cfg_gpio)(void); int (*cfg_gpio)(void);
dma_filter_fn filter; dma_filter_fn filter;
void *dma_tx;
void *dma_rx;
}; };
/** /**

View File

@ -1135,12 +1135,7 @@ spi_add_device(struct spi_device *spi);
extern struct spi_device * extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *); spi_new_device(struct spi_master *, struct spi_board_info *);
static inline void extern void spi_unregister_device(struct spi_device *spi);
spi_unregister_device(struct spi_device *spi)
{
if (spi)
device_unregister(&spi->dev);
}
extern const struct spi_device_id * extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev); spi_get_device_id(const struct spi_device *sdev);

View File

@ -17,6 +17,7 @@ help:
@echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' lguest - a minimal 32-bit x86 hypervisor'
@echo ' perf - Linux performance measurement and analysis tool' @echo ' perf - Linux performance measurement and analysis tool'
@echo ' selftests - various kernel selftests' @echo ' selftests - various kernel selftests'
@echo ' spi - spi tools'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool' @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' usb - USB testing tools' @echo ' usb - USB testing tools'
@echo ' virtio - vhost test module' @echo ' virtio - vhost test module'
@ -52,7 +53,7 @@ acpi: FORCE
cpupower: FORCE cpupower: FORCE
$(call descend,power/$@) $(call descend,power/$@)
cgroup firewire hv guest usb virtio vm net iio: FORCE cgroup firewire hv guest spi usb virtio vm net iio: FORCE
$(call descend,$@) $(call descend,$@)
liblockdep: FORCE liblockdep: FORCE
@ -118,7 +119,7 @@ acpi_clean:
cpupower_clean: cpupower_clean:
$(call descend,power/cpupower,clean) $(call descend,power/cpupower,clean)
cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean: cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
liblockdep_clean: liblockdep_clean:
@ -143,7 +144,7 @@ freefall_clean:
$(call descend,laptop/freefall,clean) $(call descend,laptop/freefall,clean)
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean freefall_clean

4
tools/spi/Makefile Normal file
View File

@ -0,0 +1,4 @@
all: spidev_test spidev_fdx
clean:
$(RM) spidev_test spidev_fdx

View File

@ -19,6 +19,7 @@
#include <getopt.h> #include <getopt.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/spi/spidev.h> #include <linux/spi/spidev.h>
@ -33,6 +34,8 @@ static void pabort(const char *s)
static const char *device = "/dev/spidev1.1"; static const char *device = "/dev/spidev1.1";
static uint32_t mode; static uint32_t mode;
static uint8_t bits = 8; static uint8_t bits = 8;
static char *input_file;
static char *output_file;
static uint32_t speed = 500000; static uint32_t speed = 500000;
static uint16_t delay; static uint16_t delay;
static int verbose; static int verbose;
@ -49,7 +52,8 @@ uint8_t default_tx[] = {
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char *input_tx; char *input_tx;
static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) static void hex_dump(const void *src, size_t length, size_t line_size,
char *prefix)
{ {
int i = 0; int i = 0;
const unsigned char *address = src; const unsigned char *address = src;
@ -83,13 +87,17 @@ static void hex_dump(const void *src, size_t length, size_t line_size, char *pre
static int unescape(char *_dst, char *_src, size_t len) static int unescape(char *_dst, char *_src, size_t len)
{ {
int ret = 0; int ret = 0;
int match;
char *src = _src; char *src = _src;
char *dst = _dst; char *dst = _dst;
unsigned int ch; unsigned int ch;
while (*src) { while (*src) {
if (*src == '\\' && *(src+1) == 'x') { if (*src == '\\' && *(src+1) == 'x') {
sscanf(src + 2, "%2x", &ch); match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");
src += 4; src += 4;
*dst++ = (unsigned char)ch; *dst++ = (unsigned char)ch;
} else { } else {
@ -103,7 +111,7 @@ static int unescape(char *_dst, char *_src, size_t len)
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{ {
int ret; int ret;
int out_fd;
struct spi_ioc_transfer tr = { struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx, .tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx, .rx_buf = (unsigned long)rx,
@ -134,7 +142,21 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
if (verbose) if (verbose)
hex_dump(tx, len, 32, "TX"); hex_dump(tx, len, 32, "TX");
hex_dump(rx, len, 32, "RX");
if (output_file) {
out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd < 0)
pabort("could not open output file");
ret = write(out_fd, rx, len);
if (ret != len)
pabort("not all bytes written to output file");
close(out_fd);
}
if (verbose || !output_file)
hex_dump(rx, len, 32, "RX");
} }
static void print_usage(const char *prog) static void print_usage(const char *prog)
@ -143,7 +165,9 @@ static void print_usage(const char *prog)
puts(" -D --device device to use (default /dev/spidev1.1)\n" puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n" " -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n" " -d --delay delay (usec)\n"
" -b --bpw bits per word \n" " -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n" " -l --loop loopback\n"
" -H --cpha clock phase\n" " -H --cpha clock phase\n"
" -O --cpol clock polarity\n" " -O --cpol clock polarity\n"
@ -167,6 +191,8 @@ static void parse_opts(int argc, char *argv[])
{ "speed", 1, 0, 's' }, { "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' }, { "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' }, { "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' }, { "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' }, { "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' }, { "cpol", 0, 0, 'O' },
@ -182,7 +208,8 @@ static void parse_opts(int argc, char *argv[])
}; };
int c; int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
lopts, NULL);
if (c == -1) if (c == -1)
break; break;
@ -200,6 +227,12 @@ static void parse_opts(int argc, char *argv[])
case 'b': case 'b':
bits = atoi(optarg); bits = atoi(optarg);
break; break;
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'l': case 'l':
mode |= SPI_LOOP; mode |= SPI_LOOP;
break; break;
@ -249,13 +282,63 @@ static void parse_opts(int argc, char *argv[])
} }
} }
static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str + 1);
uint8_t *tx;
uint8_t *rx;
tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
size = unescape((char *)tx, str, size);
transfer(fd, tx, rx, size);
free(rx);
free(tx);
}
static void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
tx_fd = open(filename, O_RDONLY);
if (fd < 0)
pabort("can't open input file");
tx = malloc(sb.st_size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
pabort("failed to read input file");
transfer(fd, tx, rx, sb.st_size);
free(rx);
free(tx);
close(tx_fd);
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret = 0; int ret = 0;
int fd; int fd;
uint8_t *tx;
uint8_t *rx;
int size;
parse_opts(argc, argv); parse_opts(argc, argv);
@ -300,17 +383,15 @@ int main(int argc, char *argv[])
printf("bits per word: %d\n", bits); printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
if (input_tx) { if (input_tx && input_file)
size = strlen(input_tx+1); pabort("only one of -p and --input may be selected");
tx = malloc(size);
rx = malloc(size); if (input_tx)
size = unescape((char *)tx, input_tx, size); transfer_escaped_string(fd, input_tx);
transfer(fd, tx, rx, size); else if (input_file)
free(rx); transfer_file(fd, input_file);
free(tx); else
} else {
transfer(fd, default_tx, default_rx, sizeof(default_tx)); transfer(fd, default_tx, default_rx, sizeof(default_tx));
}
close(fd); close(fd);