diff --git a/software/kernel/Kbuild b/software/kernel/Kbuild index 5faca5b9442e8ce3a45f22fbe553aeaafdc0cd74..4bf4fa22f1c06bca72335fe1ae9a98aa5fd917c6 100644 --- a/software/kernel/Kbuild +++ b/software/kernel/Kbuild @@ -17,6 +17,7 @@ ccflags-y += -Wall -Werror ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += -DCONFIG_FPGA_MGR_BACKPORT ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += $(CONFIG_FPGA_MGR_BACKPORT_INCLUDE) ccflags-y += -I$(FMC_ABS)/include +ccflags-y += -I$(SPI_ABS)/include # priority to I2C, FMC headers from our sources LINUXINCLUDE := -I$(FMC_ABS)/include -I$(FMC_ABS)/include/linux -I$(I2C_ABS)/include -I$(I2C_ABS)/include/linux $(LINUXINCLUDE) @@ -31,7 +32,6 @@ obj-m := spec-fmc-carrier.o obj-m += gn412x-gpio.o obj-m += gn412x-fcl.o obj-m += spec-gn412x-dma.o -obj-m += spi-ocores.o spec-fmc-carrier-objs := spec-core.o spec-fmc-carrier-objs += spec-core-fpga.o diff --git a/software/kernel/Makefile b/software/kernel/Makefile index c90eaf53838076dff1fee9f691c9eed7f7e7e3c9..00ac06b19ea8da2ad195b92d888bbd8003447f4b 100644 --- a/software/kernel/Makefile +++ b/software/kernel/Makefile @@ -24,15 +24,18 @@ ifeq ($(DKMS), 1) FPGA_MGR_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/fpga_mgr/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1)) FMC_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/fmc/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1)) I2C_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/i2c-ocores/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1)) +SPI_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/spi-ocores/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1)) CONFIG_FPGA_MGR_BACKPORT_PATH := $(DKMSTREE)/fpga_mgr/$(FPGA_MGR_VERSION)/source FMC := $(DKMSTREE)/fmc/$(FMC_VERSION)/source I2C := $(DKMSTREE)/i2c-ocores/$(I2C_VERSION)/source +SPI := $(DKMSTREE)/spi-ocores/$(SPI_VERSION)/source endif CONFIG_FPGA_MGR_BACKPORT_PATH_ABS ?= $(abspath $(CONFIG_FPGA_MGR_BACKPORT_PATH)) FMC_ABS ?= $(abspath $(FMC)) I2C_ABS ?= $(abspath $(I2C)) +SPI_ABS ?= $(abspath $(SPI)) VERSION = $(shell git describe --dirty --long --tags) @@ -48,7 +51,7 @@ ifeq ($(DKMS), 0) endif modules help install modules_install: spec-core-fpga.h - $(MAKE) -C $(LINUX) M=$(shell pwd) VERSION=$(VERSION) CONFIG_FPGA_MGR_BACKPORT_PATH_ABS=$(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS) CONFIG_FPGA_MGR_BACKPORT=$(CONFIG_FPGA_MGR_BACKPORT) FMC_ABS=$(FMC_ABS) I2C_ABS=$(I2C_ABS) $@ + $(MAKE) -C $(LINUX) M=$(shell pwd) VERSION=$(VERSION) CONFIG_FPGA_MGR_BACKPORT_PATH_ABS=$(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS) CONFIG_FPGA_MGR_BACKPORT=$(CONFIG_FPGA_MGR_BACKPORT) FMC_ABS=$(FMC_ABS) I2C_ABS=$(I2C_ABS) SPI_ABS=$(SPI_ABS) $@ # be able to run the "clean" rule even if $(LINUX) is not valid clean: diff --git a/software/kernel/platform_data/spi-ocores.h b/software/kernel/platform_data/spi-ocores.h deleted file mode 100644 index 042c4ceb5dfa60a248d9984c65c1678e2da7050f..0000000000000000000000000000000000000000 --- a/software/kernel/platform_data/spi-ocores.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 CERN (www.cern.ch) - * Author: Federico Vaga <federico.vaga@cern.ch> - */ - -#ifndef __SPI_OCORES_PDATA_H__ -#define __SPI_OCORES_PDATA_H__ - -#include <linux/spi/spi.h> - -/** - * struct spi_ocores_platform_data - OpenCores SPI data - */ -struct spi_ocores_platform_data { - unsigned int big_endian; - unsigned int clock_hz; - unsigned int num_devices; - struct spi_board_info *devices; -}; - -#endif diff --git a/software/kernel/spec-core-fpga.c b/software/kernel/spec-core-fpga.c index 707b6b8fd0254956c19053d84543780aba2843fb..7810dc8da2b7c8bde3815a395644b1f743cae641 100644 --- a/software/kernel/spec-core-fpga.c +++ b/software/kernel/spec-core-fpga.c @@ -5,6 +5,7 @@ */ #include <linux/types.h> #include <linux/platform_data/i2c-ocores.h> +#include <linux/platform_data/spi-ocores.h> #include <linux/ioport.h> #include <linux/gpio/consumer.h> #include <linux/irqdomain.h> @@ -18,7 +19,6 @@ #include "spec.h" #include "spec-compat.h" -#include "platform_data/spi-ocores.h" enum spec_fpga_irq_lines { SPEC_FPGA_IRQ_FMC_I2C = 0, diff --git a/software/kernel/spi-ocores.c b/software/kernel/spi-ocores.c deleted file mode 100644 index ec0fdcfbd64aae8e17df677727e81d7698fd64a9..0000000000000000000000000000000000000000 --- a/software/kernel/spi-ocores.c +++ /dev/null @@ -1,810 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 CERN (www.cern.ch) - * Author: Federico Vaga <federico.vaga@cern.ch> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/spi/spi.h> -#include <linux/bitops.h> -#include <linux/errno.h> -#include <linux/interrupt.h> - -#include "platform_data/spi-ocores.h" - -#define SPI_OCORES_BUF_SIZE 4 -#define SPI_OCORES_CS_MAX_N 8 - -/* SPI register */ -#define SPI_OCORES_RX(x) (x * SPI_OCORES_BUF_SIZE) -#define SPI_OCORES_TX(x) (x * SPI_OCORES_BUF_SIZE) -#define SPI_OCORES_CTRL 0x10 -#define SPI_OCORES_DIV 0x14 -#define SPI_OCORES_CS 0x18 - -/* SPI control register fields mask */ -#define SPI_OCORES_CTRL_CHAR_LEN 0x007F -#define SPI_OCORES_CTRL_GO 0x0100 /* go/busy */ -#define SPI_OCORES_CTRL_BUSY 0x0100 /* go/busy */ -#define SPI_OCORES_CTRL_Rx_NEG 0x0200 -#define SPI_OCORES_CTRL_Tx_NEG 0x0400 -#define SPI_OCORES_CTRL_LSB 0x0800 -#define SPI_OCORES_CTRL_IE 0x1000 -#define SPI_OCORES_CTRL_ASS 0x2000 - -#define SPI_OCORES_FLAG_POLL BIT(0) - -struct spi_ocores { - struct spi_master *master; - unsigned long flags; - void __iomem *mem; - unsigned int clock_hz; - uint32_t ctrl_base; - - /* Current transfer */ - struct spi_transfer *cur_xfer; - const void *cur_tx_buf; - void *cur_rx_buf; - unsigned int cur_len; - size_t (*cur_tx_push)(struct spi_ocores *sp); - size_t (*cur_rx_pop)(struct spi_ocores *sp); - - /* Register Access functions */ - uint32_t (*read)(struct spi_ocores *sp, unsigned int reg); - void (*write)(struct spi_ocores *sp, uint32_t val, unsigned int reg); -}; - -enum spi_ocores_type { - TYPE_OCORES = 0, -}; - -static inline uint32_t spi_ocores_ioread32(struct spi_ocores *sp, - unsigned int reg) -{ - return ioread32(sp->mem + reg); -} - -static inline void spi_ocores_iowrite32(struct spi_ocores *sp, - uint32_t val, unsigned int reg) -{ - iowrite32(val, sp->mem + reg); -} - -static inline uint32_t spi_ocores_ioread32be(struct spi_ocores *sp, - unsigned int reg) -{ - return ioread32be(sp->mem + reg); -} - -static inline void spi_ocores_iowrite32be(struct spi_ocores *sp, - uint32_t val, unsigned int reg) -{ - iowrite32be(val, sp->mem + reg); -} - -static inline struct spi_ocores *spi_ocoresdev_to_sp(struct spi_device *spi) -{ - return spi_master_get_devdata(spi->master); -} - -/** - * Configure controller according to SPI device needs - */ -static int spi_ocores_setup(struct spi_device *spi) -{ - return 0; -} - -/** - * Release resources used by the SPI device - */ -static void spi_ocores_cleanup(struct spi_device *spi) -{ -} - -/** - * Configure controller - */ -static void spi_ocores_hw_xfer_config(struct spi_ocores *sp, - uint32_t ctrl, - uint16_t divider) -{ - ctrl &= ~SPI_OCORES_CTRL_GO; /* be sure to not start */ - sp->write(sp, ctrl, SPI_OCORES_CTRL); - sp->write(sp, divider, SPI_OCORES_DIV); -} - -/** - * Transmit data in FIFO - */ -static void spi_ocores_hw_xfer_go(struct spi_ocores *sp) -{ - uint32_t ctrl; - - ctrl = sp->read(sp, SPI_OCORES_CTRL); - ctrl |= SPI_OCORES_CTRL_GO; - sp->write(sp, ctrl, SPI_OCORES_CTRL); -} - -/** - * Slave Select - */ -static void spi_ocores_hw_xfer_cs(struct spi_ocores *sp, - unsigned int cs, - unsigned int val) -{ - uint32_t ss; - - ss = sp->read(sp, SPI_OCORES_CS); - if (val) - ss |= BIT(cs); - else - ss &= ~BIT(cs); - sp->write(sp, ss, SPI_OCORES_CS); -} - -/** - * Write a TX register - */ -static void spi_ocores_tx_set(struct spi_ocores *sp, - unsigned int idx, uint32_t val) -{ - if (WARN(idx > 3, "Invalid TX register index %d (min:0, max: 3)\n", - idx)) - return; - - sp->write(sp, val, SPI_OCORES_TX(idx)); -} - -/** - * Read an RX register - */ -static uint32_t spi_ocores_rx_get(struct spi_ocores *sp, unsigned int idx) -{ - uint32_t val; - - if (WARN(idx > 3, "Invalid RX register index %d (min:0, max: 3)\n", - idx)) - return 0; - - val = sp->read(sp, SPI_OCORES_RX(idx)); - - return val; -} - -/** - * Get current bits per word - * @sp: SPI OCORE controller - * - * Return: number of bits_per_word - */ -static uint8_t spi_ocores_hw_xfer_bits_per_word(struct spi_ocores *sp) -{ - uint8_t nbits; - - nbits = (sp->cur_xfer && sp->cur_xfer->bits_per_word) ? - sp->cur_xfer->bits_per_word : - sp->master->cur_msg->spi->bits_per_word; - - return nbits; -} - -static size_t spi_ocores_hw_xfer_tx_push8(struct spi_ocores *sp) -{ - uint8_t data; - - data = ((uint8_t *)sp->cur_tx_buf)[0]; - spi_ocores_tx_set(sp, 0, data); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_tx_push16(struct spi_ocores *sp) -{ - uint16_t data; - - data = ((uint16_t *)sp->cur_tx_buf)[0]; - spi_ocores_tx_set(sp, 0, __cpu_to_be16(data)); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_tx_push32(struct spi_ocores *sp) -{ - uint32_t data; - - data = ((uint32_t *)sp->cur_tx_buf)[0]; - spi_ocores_tx_set(sp, 0, __cpu_to_be32(data)); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_tx_push64(struct spi_ocores *sp) -{ - uint64_t data; - - data = __cpu_to_be64(*((uint64_t *)sp->cur_tx_buf)); - spi_ocores_tx_set(sp, 0, data & 0xFFFFFFFF); - data >>= 32; - spi_ocores_tx_set(sp, 1, data & 0xFFFFFFFF); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_tx_push128(struct spi_ocores *sp) -{ - uint64_t data; - - data = __cpu_to_be64(((uint64_t *)sp->cur_tx_buf)[0]); - spi_ocores_tx_set(sp, 2, data & 0xFFFFFFFF); - data >>= 32; - spi_ocores_tx_set(sp, 3, data & 0xFFFFFFFF); - - data = __cpu_to_be64(((uint64_t *)sp->cur_tx_buf)[1]); - spi_ocores_tx_set(sp, 0, data & 0xFFFFFFFF); - data >>= 32; - spi_ocores_tx_set(sp, 1, data & 0xFFFFFFFF); - - return sizeof(data) * 2; -} - -static size_t spi_ocores_hw_xfer_tx_push(struct spi_ocores *sp) -{ - size_t len = 0; - - if (sp->cur_tx_buf) - len = sp->cur_tx_push(sp); - sp->cur_tx_buf += len; - - return len; -} - -static size_t spi_ocores_hw_xfer_rx_pop8(struct spi_ocores *sp) -{ - uint8_t data; - - data = spi_ocores_rx_get(sp, 0) & 0x000000FF; - ((uint8_t *)sp->cur_rx_buf)[0] = data; - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_rx_pop16(struct spi_ocores *sp) -{ - uint16_t data; - - data = spi_ocores_rx_get(sp, 0) & 0x0000FFFF; - ((uint16_t *)sp->cur_rx_buf)[0] = __be16_to_cpu(data); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_rx_pop32(struct spi_ocores *sp) -{ - uint32_t data; - - data = spi_ocores_rx_get(sp, 0) & 0xFFFFFFFF; - ((uint32_t *)sp->cur_rx_buf)[0] = __be32_to_cpu(data); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_rx_pop64(struct spi_ocores *sp) -{ - uint64_t data; - - data = spi_ocores_rx_get(sp, 1) & 0xFFFFFFFF; - data <<= 32; - data |= spi_ocores_rx_get(sp, 0) & 0xFFFFFFFF; - ((uint64_t *)sp->cur_rx_buf)[0] = __be64_to_cpu(data); - - return sizeof(data); -} - -static size_t spi_ocores_hw_xfer_rx_pop128(struct spi_ocores *sp) -{ - uint64_t data; - - data = spi_ocores_rx_get(sp, 3) & 0xFFFFFFFF; - data <<= 32; - data |= spi_ocores_rx_get(sp, 2) & 0xFFFFFFFF; - ((uint64_t *)sp->cur_rx_buf)[1] = __be64_to_cpu(data); - - data = spi_ocores_rx_get(sp, 1) & 0xFFFFFFFF; - data <<= 32; - data |= spi_ocores_rx_get(sp, 0) & 0xFFFFFFFF; - ((uint64_t *)sp->cur_rx_buf)[0] = __be64_to_cpu(data); - - return sizeof(data) * 2; -} - -static size_t spi_ocores_hw_xfer_rx_pop(struct spi_ocores *sp) -{ - size_t len = 0; - uint8_t nbits; - - /* - * When we read is because a complete HW transfer is over, so we - * can safely decrease the counter of pending bytes - */ - nbits = spi_ocores_hw_xfer_bits_per_word(sp); - sp->cur_len -= (nbits / 8); /* FIXME not working for !pow2 */ - - if (sp->cur_rx_buf) - len = sp->cur_rx_pop(sp); - sp->cur_rx_buf += len; - - return len; -} - -static void spi_ocores_hw_xfer_start(struct spi_ocores *sp) -{ - unsigned int cs = sp->master->cur_msg->spi->chip_select; - - spi_ocores_hw_xfer_cs(sp, cs, 1); - spi_ocores_hw_xfer_go(sp); -} - -/** - * Wait until something change in a given register - * @sp: SPI OCORE controller - * @reg: register to query - * @mask: bitmask to apply on register value - * @val: expected result - * @timeout: timeout in jiffies - * - * Timeout is necessary to avoid to stay here forever when the chip - * does not answer correctly. - * - * Return: 0 on success, -ETIMEDOUT on timeout - */ -static int spi_ocores_wait(struct spi_ocores *sp, - int reg, uint32_t mask, uint32_t val, - const unsigned long timeout) -{ - unsigned long j; - - j = jiffies + timeout; - while (1) { - uint32_t tmp = sp->read(sp, reg); - - if ((tmp & mask) == val) - break; - - if (time_after(jiffies, j)) - return -ETIMEDOUT; - } - return 0; -} - -/** - * Wait transfer completion - * @sp: SPI OCORE controller - * @timeoute: in jiffies - * - * return: 0 on success, -ETIMEDOUT on timeout - */ -static int spi_ocores_hw_xfer_wait_complete(struct spi_ocores *sp, - const unsigned long timeout) -{ - return spi_ocores_wait(sp, SPI_OCORES_CTRL, - SPI_OCORES_CTRL_BUSY, 0, timeout); -} - -/** - * Finish Linux SPI transfer - * @sp: SPI OCORE controller - * @xfer: Linux SPI transfer to finish - * - * Return: 0 on success, otherwise a negative errno - */ -static int spi_ocores_sw_xfer_finish(struct spi_ocores *sp) -{ - if (sp->cur_xfer->delay_usecs) - udelay(sp->cur_xfer->delay_usecs); - if (sp->cur_xfer->cs_change) { - unsigned int cs; - - cs = sp->master->cur_msg->spi->chip_select; - spi_ocores_hw_xfer_cs(sp, cs, 0); - } - - sp->cur_tx_buf = NULL; - sp->cur_rx_buf = NULL; - sp->cur_len = 0; - - return 0; -} - -/** - * Initialize data for next software transfer - * @sp: SPI OCORE controller - * - * Return: 0 on success, - * -ENODATA when there are no transfers left, - * -EINVAL invalid bit per word - */ -static int spi_ocores_sw_xfer_next_init(struct spi_ocores *sp) -{ - struct list_head *head = &sp->master->cur_msg->transfers; - uint8_t nbits; - uint16_t divider; - uint32_t hz, ctrl; - - if (!sp->cur_xfer) { - sp->cur_xfer = list_first_entry_or_null(head, - struct spi_transfer, - transfer_list); - } else { - if (list_is_last(&sp->cur_xfer->transfer_list, head)) - return -ENODATA; - - sp->cur_xfer = list_next_entry(sp->cur_xfer, transfer_list); - } - - if (WARN(!sp->cur_xfer, "Invalid SPI transfer")) { - sp->master->cur_msg->status = -EINVAL; - return sp->master->cur_msg->status; - } - - nbits = spi_ocores_hw_xfer_bits_per_word(sp); - if ((nbits - 1) & (~SPI_OCORES_CTRL_CHAR_LEN)) { - sp->master->cur_msg->status = -EINVAL; - return sp->master->cur_msg->status; - } - - if ((sp->cur_xfer->len << 3) < nbits) { - dev_err(&sp->master->dev, - "Invalid transfer length %d (bits_per_word %d)\n", - sp->cur_xfer->len, nbits); - sp->master->cur_msg->status = -EINVAL; - return sp->master->cur_msg->status; - } - - ctrl = sp->ctrl_base; - if (sp->master->cur_msg->spi->mode & SPI_CPHA) { - ctrl |= SPI_OCORES_CTRL_Tx_NEG; - ctrl |= SPI_OCORES_CTRL_Rx_NEG; - } - if (sp->master->cur_msg->spi->mode & SPI_LSB_FIRST) - ctrl |= SPI_OCORES_CTRL_LSB; - ctrl |= nbits; - if (sp->cur_xfer->speed_hz) - hz = sp->cur_xfer->speed_hz; - else - hz = sp->master->cur_msg->spi->max_speed_hz; - divider = (sp->clock_hz / (hz * 2)) - 1; - spi_ocores_hw_xfer_config(sp, ctrl, divider); - - - sp->cur_tx_buf = sp->cur_xfer->tx_buf; - sp->cur_rx_buf = sp->cur_xfer->rx_buf; - sp->cur_len = sp->cur_xfer->len; - - /* set operations */ - if (nbits <= 8) { - sp->cur_tx_push = spi_ocores_hw_xfer_tx_push8; - sp->cur_rx_pop = spi_ocores_hw_xfer_rx_pop8; - } else if (nbits <= 16) { - sp->cur_tx_push = spi_ocores_hw_xfer_tx_push16; - sp->cur_rx_pop = spi_ocores_hw_xfer_rx_pop16; - } else if (nbits <= 32) { - sp->cur_tx_push = spi_ocores_hw_xfer_tx_push32; - sp->cur_rx_pop = spi_ocores_hw_xfer_rx_pop32; - } else if (nbits <= 64) { - sp->cur_tx_push = spi_ocores_hw_xfer_tx_push64; - sp->cur_rx_pop = spi_ocores_hw_xfer_rx_pop64; - } else if (nbits <= 128) { - sp->cur_tx_push = spi_ocores_hw_xfer_tx_push128; - sp->cur_rx_pop = spi_ocores_hw_xfer_rx_pop128; - } - - return 0; -} - -/** - * Start next software transfer - * @sp: SPI OCORE controller - * - * Return: 0 on success, -ENODEV when missing transfers - */ -static int spi_ocores_sw_xfer_next_start(struct spi_ocores *sp) -{ - int err; - - err = spi_ocores_sw_xfer_next_init(sp); - if (err) - return err; - spi_ocores_hw_xfer_tx_push(sp); - spi_ocores_hw_xfer_start(sp); - - return 0; -} - -/** - * TX pending status - * @sp: SPI OCORE controller - * Return: True is there are still pending data in the current transfer - */ -static bool spi_ocores_sw_xfer_has_pending(struct spi_ocores *sp) -{ - return sp->cur_len > 0; -} - -/** - * Finalize message transmission - * @sp: SPI OCORE controller - */ -static void spi_ocores_finalize_current_message(struct spi_ocores *sp) -{ - unsigned int cs = sp->master->cur_msg->spi->chip_select; - - spi_ocores_hw_xfer_cs(sp, cs, 0); - sp->cur_xfer = NULL; - sp->master->cur_msg->status = 0; - spi_finalize_current_message(sp->master); - -} - -static bool spi_ocores_is_busy(struct spi_ocores *sp) -{ - uint32_t ctrl = sp->read(sp, SPI_OCORES_CTRL); - - return (ctrl & SPI_OCORES_CTRL_BUSY); -} - -/** - * Pop RX word and push next TX from current transfer - * @sp: SPI OCORE controller - * - * Return: 0 on success, -ENODATA when there is nothing to process - */ -static int spi_ocores_hw_xfer_rxtx(struct spi_ocores *sp) -{ - spi_ocores_hw_xfer_rx_pop(sp); - if (spi_ocores_sw_xfer_has_pending(sp)) - return -ENODATA; - - spi_ocores_hw_xfer_tx_push(sp); - spi_ocores_hw_xfer_start(sp); - - return 0; -} - -/** - * Process an SPI transfer - * @sp: SPI OCORE controller - * - * Return: 0 on success, -ENODATA when there is nothing to process - */ -static int spi_ocores_process(struct spi_ocores *sp) -{ - int err; - - if (spi_ocores_is_busy(sp)) - return -EBUSY; - - err = spi_ocores_hw_xfer_rxtx(sp); - if (err == -ENODATA) { - spi_ocores_sw_xfer_finish(sp); - err = spi_ocores_sw_xfer_next_start(sp); - if (err) - spi_ocores_finalize_current_message(sp); - } - - return 0; -} - -/** - * Process an SPI transfer - * @sp: SPI OCORE controller - * @timeout: timeout in milli-seconds - * - * Return: 0 on success, -ENODATA when there is nothing to process, -ETIMEDOUT - * when is still pending after timeout - */ -static int spi_ocores_process_poll(struct spi_ocores *sp, unsigned int timeout) -{ - int err; - - err = spi_ocores_hw_xfer_wait_complete(sp, msecs_to_jiffies(timeout)); - if (err) - return err; - err = spi_ocores_process(sp); - if (err) - return err; - return 0; -} - -static irqreturn_t spi_ocores_irq_handler(int irq, void *arg) -{ - struct spi_ocores *sp = arg; - int err; - - err = spi_ocores_process(sp); - if (err && err != -ENODATA) - return IRQ_NONE; - - return IRQ_HANDLED; -} - -/** - * Transfer one SPI message - */ -static int spi_ocores_transfer_one_message(struct spi_master *master, - struct spi_message *mesg) -{ - struct spi_ocores *sp = spi_master_get_devdata(master); - int err = 0; - - err = spi_ocores_sw_xfer_next_start(sp); - if (sp->flags & SPI_OCORES_FLAG_POLL) { - do { - err = spi_ocores_process_poll(sp, 100); - } while (!err); - } - - return 0; -} - -/** - * Unprepare hardware - * - * Mainly it disables interrupts - */ -static int spi_ocores_unprepare_transfer_hardware(struct spi_master *master) -{ - struct spi_ocores *sp = spi_master_get_devdata(master); - - spi_ocores_hw_xfer_config(sp, 0, 0); - - return 0; -} - -static int spi_ocores_probe(struct platform_device *pdev) -{ - struct spi_master *master; - struct spi_ocores *sp; - struct spi_ocores_platform_data *pdata; - struct resource *r; - int err; - int irq; - int i; - - master = spi_alloc_master(&pdev->dev, sizeof(*sp)); - if (!master) { - dev_err(&pdev->dev, "failed to allocate spi master\n"); - return -ENOMEM; - } - - sp = spi_master_get_devdata(master); - platform_set_drvdata(pdev, sp); - sp->master = master; - - pdata = pdev->dev.platform_data; - if (!pdata) { - err = -EINVAL; - goto err_get_pdata; - } - - sp->clock_hz = pdata->clock_hz; - - /* configure SPI master */ - master->setup = spi_ocores_setup; - master->cleanup = spi_ocores_cleanup; - master->transfer_one_message = spi_ocores_transfer_one_message; - master->unprepare_transfer_hardware = spi_ocores_unprepare_transfer_hardware; - master->num_chipselect = SPI_OCORES_CS_MAX_N; - master->mode_bits = SPI_LSB_FIRST | SPI_CPHA; - if (pdata->big_endian) { - sp->read = spi_ocores_ioread32be; - sp->write = spi_ocores_iowrite32be; - } else { - sp->read = spi_ocores_ioread32; - sp->write = spi_ocores_iowrite32; - } - - /* assign resources */ - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sp->mem = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(sp->mem)) { - err = PTR_ERR(sp->mem); - goto err_get_mem; - } - - irq = platform_get_irq(pdev, 0); - if (irq == -ENXIO) { - sp->flags |= SPI_OCORES_FLAG_POLL; - } else { - if (irq < 0) { - err = irq; - goto err_get_irq; - } - } - - if (!(sp->flags & SPI_OCORES_FLAG_POLL)) { - sp->ctrl_base |= SPI_OCORES_CTRL_IE; - err = request_any_context_irq(irq, spi_ocores_irq_handler, - 0, pdev->name, sp); - if (err < 0) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_irq; - } - } - - err = spi_register_master(master); - if (err) - goto err_reg_spi; - - for (i = 0; i < pdata->num_devices; ++i) { - struct spi_device *sdev; - - sdev = spi_new_device(master, &pdata->devices[i]); - if (!sdev) - dev_err(&pdev->dev, - "Cannot register SPI device '%s'\n", - pdata->devices[i].modalias); - } - - return 0; - -err_reg_spi: - if (!(sp->flags & SPI_OCORES_FLAG_POLL)) - free_irq(irq, sp); -err_irq: -err_get_irq: - devm_iounmap(&pdev->dev, sp->mem); -err_get_mem: -err_get_pdata: - spi_master_put(master); - return err; -} - -static int spi_ocores_remove(struct platform_device *pdev) -{ - struct spi_ocores *sp = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - - if (irq > 0) - free_irq(irq, sp); - spi_unregister_master(sp->master); - devm_iounmap(&pdev->dev, sp->mem); - platform_set_drvdata(pdev, NULL); - spi_master_put(sp->master); - - return 0; -} - -static const struct platform_device_id spi_ocores_id_table[] = { - { - .name = "ocores-spi", - .driver_data = TYPE_OCORES, - }, - { - .name = "spi-ocores", - .driver_data = TYPE_OCORES, - }, - { .name = "" }, /* last */ -}; - -static struct platform_driver spi_ocores_driver = { - .probe = spi_ocores_probe, - .remove = spi_ocores_remove, - .driver = { - .name = "ocores-spi", - .owner = THIS_MODULE, - }, - .id_table = spi_ocores_id_table, -}; -module_platform_driver(spi_ocores_driver); - -MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>"); -MODULE_DESCRIPTION("SPI controller driver for OHWR General-Cores SPI Master"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(VERSION); -MODULE_DEVICE_TABLE(platform, spi_ocores_id_table);