drivers: spi: add kbus

Signed-off-by: Oleg Karfich <oleg.karfich@wago.com>
This commit is contained in:
Oleg Karfich
2021-01-18 10:43:08 +01:00
parent fcb81b750b
commit 284700b29a
8 changed files with 2149 additions and 1 deletions
@@ -256,6 +256,8 @@ CONFIG_I2C_MUX_PCA9541=y
CONFIG_I2C_MUX_PCA954x=y
CONFIG_SPI=y
CONFIG_SPI_OMAP24XX=y
CONFIG_SPI_KBUS=y
CONFIG_SPI_SPIDEV=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_PCA953X=y
+16
View File
@@ -758,6 +758,22 @@ config SPI_TI_QSPI
This device supports single, dual and quad read support, while
it only supports single write mode.
config SPI_KBUS_OMAP_EXTENSION
bool "KBUS Extension for OMAP MCSPI Driver (Read Notes!)"
depends on SPI_OMAP24XX
help
KBUS Extension for the McSPI OMAP driver.
It implements the communication protocol for the infineon XE164 Chip
which does the communication with the KBUS logic.
ATTENTION: This disbles the use of a worker thread (work queue).
In its current state only one userspace process is allowed.
config SPI_KBUS
select SPI_KBUS_OMAP_EXTENSION
tristate "WAGO KBUS Driver"
help
This is driver provides access to the KBUS interface.
config SPI_ORION
tristate "Orion SPI master"
depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST
+2
View File
@@ -152,6 +152,8 @@ obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
obj-$(CONFIG_SPI_AMD) += spi-amd.o
obj-$(CONFIG_SPI_KBUS) += spi-kbus.o
obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
# SPI slave protocol handlers
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
File diff suppressed because it is too large Load Diff
+305 -1
View File
@@ -29,6 +29,20 @@
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/errno.h>
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
#include <linux/sched.h>
#endif
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
#include <linux/spi/kbus.h>
#include <misc/wago-tests.h>
#define PXC_SPI_KBUS_TRACER
#include <trace/events/pxc.h>
static int kbusdelay = 0; /* KBUS Inter-Frame-Delay in nanoseconds */
#endif
#define OMAP2_MCSPI_MAX_FREQ 48000000
#define OMAP2_MCSPI_MAX_DIVIDER 4096
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
@@ -290,6 +304,25 @@ static void omap2_mcspi_set_mode(struct spi_controller *ctlr)
ctx->modulctrl = l;
}
#ifdef CONFIG_SPI_KBUS_OMAP_SET_SPIDAT_DIR
static void omap2_mcspi_set_spidat_direction(struct spi_master *master)
{
u32 l;
#define OMAP2_MCSPI_SYST_SPIDATDIR0_INPUT_EN BIT(8)
#define OMAP2_MCSPI_SYST_SPIDATDIR1_INPUT_EN BIT(9)
l = mcspi_read_reg(master, OMAP2_MCSPI_SYST);
pr_info("%s: read-OMAP2_MCSPI_SYST: 0x%x\n", __func__, l);
l &= ~(OMAP2_MCSPI_SYST_SPIDATDIR0_INPUT_EN | OMAP2_MCSPI_SYST_SPIDATDIR1_INPUT_EN);
l |= OMAP2_MCSPI_SYST_SPIDATDIR0_INPUT_EN;
pr_info("%s: write-OMAP2_MCSPI_SYST: 0x%x\n", __func__, l);
mcspi_write_reg(master, OMAP2_MCSPI_SYST, l);
l = mcspi_read_reg(master, OMAP2_MCSPI_SYST);
pr_info("%s: (update) read-OMAP2_MCSPI_SYST: 0x%x\n", __func__, l);
}
#endif
static void omap2_mcspi_set_fifo(const struct spi_device *spi,
struct spi_transfer *t, int enable)
{
@@ -386,6 +419,11 @@ static void omap2_mcspi_rx_callback(void *data)
/* We must disable the DMA RX request */
omap2_mcspi_set_dma_req(spi, 1, 0);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->dev.driver == &kbus_driver.driver)
trace_pxc_kbus(__func__, "DMA: RX completed!");
#endif
complete(&mcspi_dma->dma_rx_completion);
}
@@ -398,6 +436,11 @@ static void omap2_mcspi_tx_callback(void *data)
/* We must disable the DMA TX request */
omap2_mcspi_set_dma_req(spi, 0, 0);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->dev.driver == &kbus_driver.driver)
trace_pxc_kbus(__func__, "DMA: TX completed!");
#endif
complete(&mcspi_dma->dma_tx_completion);
}
@@ -522,6 +565,11 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
return 0;
}
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->dev.driver == &kbus_driver.driver)
if (xfer->rx_buf && ((char *) xfer->rx_buf)[96-1] == 0x66)
trace_pxc_kbus(__func__, "DMA: RX Data MATCH (0x66)");
#endif
for (x = 0; x < nb_sizes; x++)
kfree(sg_out[x]);
@@ -592,6 +640,12 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
void __iomem *irqstat_reg;
int wait_res;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "enter");
kbus_boost_dma_task(1);
#endif
mcspi = spi_controller_get_devdata(spi->controller);
mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)];
@@ -682,6 +736,11 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
dev_err(&spi->dev, "EOT timed out\n");
}
}
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "leave");
#endif
return count;
}
@@ -696,11 +755,20 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
void __iomem *rx_reg;
void __iomem *chstat_reg;
int word_len;
struct omap2_mcspi_device_config *cd;
cd = spi->controller_data;
count = xfer->len;
c = count;
word_len = cs->word_len;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "enter");
kbus_dbg("%s[%d]: count: %d\n", __func__,__LINE__, count);
kbus_boost_dma_task(0);
#endif
l = mcspi_cached_chconf0(spi);
/* We store the pre-calculated register addresses on stack to speed
@@ -712,6 +780,10 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
if (c < (word_len>>3))
return 0;
dev_dbg(&spi->dev, "xx: %s-%d %d %s:%s\n", xfer->tx_buf ? "tx" : "rx",
word_len, count, cd->turbo_mode ? "turbo" : "-",
(l & OMAP2_MCSPI_CHCONF_TURBO) ? "1" : "-");
if (word_len <= 8) {
u8 *rx;
const u8 *tx;
@@ -722,6 +794,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
do {
c -= 1;
if (tx != NULL) {
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_TXS) < 0) {
dev_err(&spi->dev, "TXS timed out\n");
@@ -729,9 +804,15 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
dev_vdbg(&spi->dev, "write-%d %02x\n",
word_len, *tx);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "write", word_len, *tx);
#endif
writel_relaxed(*tx++, tx_reg);
}
if (rx != NULL) {
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev, "RXS timed out\n");
@@ -744,6 +825,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
*rx++ = readl_relaxed(rx_reg);
dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "readtb", word_len, *(rx - 1));
#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev,
@@ -756,8 +840,14 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
*rx++ = readl_relaxed(rx_reg);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
#endif
dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "read", word_len, *(rx - 1));
#endif
}
/* Add word delay between each word */
spi_delay_exec(&xfer->word_delay, xfer);
@@ -768,22 +858,34 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
rx = xfer->rx_buf;
tx = xfer->tx_buf;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
#endif
do {
c -= 2;
if (tx != NULL) {
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_TXS) < 0) {
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "TXS timed out");
#endif
dev_err(&spi->dev, "TXS timed out\n");
goto out;
}
dev_vdbg(&spi->dev, "write-%d %04x\n",
word_len, *tx);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "write", word_len, *tx);
#endif
writel_relaxed(*tx++, tx_reg);
}
if (rx != NULL) {
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev, "RXS timed out\n");
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbus(__func__, "RXS timed out");
#endif
goto out;
}
@@ -793,6 +895,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
*rx++ = readl_relaxed(rx_reg);
dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "readtb", word_len, *(rx - 1));
#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev,
@@ -804,13 +909,30 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
omap2_mcspi_set_enable(spi, 0);
}
/* INFO:
* We have a timing problem here!!!
* During extensive spi traffic some bytes were lost
* during read. Some tests also showed that the Infineon
* needs some more time between the spi words.
*/
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (kbusdelay)
ndelay(kbusdelay); /* 400: With this delay we did a test over 14h successfully! */
#endif
*rx++ = readl_relaxed(rx_reg);
dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
trace_pxc_kbusdump(__func__, "read", word_len, *(rx - 1));
#endif
}
/* Add word delay between each word */
spi_delay_exec(&xfer->word_delay, xfer);
} while (c >= 2);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
#endif
} else if (word_len <= 32) {
u32 *rx;
const u32 *tx;
@@ -840,6 +962,14 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
omap2_mcspi_set_enable(spi, 0);
*rx++ = readl_relaxed(rx_reg);
/* For some reason while beeing in
* turbo mode we need a short delay
* here. Otherwise it will hang if we
* try to disable and enable
* turbo mode again */
ndelay(1);
dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1));
if (mcspi_wait_for_reg_bit(chstat_reg,
@@ -879,6 +1009,10 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
out:
omap2_mcspi_set_enable(spi, 1);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
kbus_wago_mpoint(); /* MX */
trace_pxc_kbus(__func__, "leave");
#endif
return count - c;
}
@@ -936,6 +1070,9 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
l &= ~OMAP2_MCSPI_CHCONF_IS;
l &= ~OMAP2_MCSPI_CHCONF_DPE1;
l |= OMAP2_MCSPI_CHCONF_DPE0;
#ifdef CONFIG_SPI_KBUS_OMAP_SET_SPIDAT_DIR
omap2_mcspi_set_spidat_direction(spi->master);
#endif
} else {
l |= OMAP2_MCSPI_CHCONF_IS;
l |= OMAP2_MCSPI_CHCONF_DPE1;
@@ -1061,6 +1198,12 @@ static int omap2_mcspi_setup(struct spi_device *spi)
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
struct omap2_mcspi_cs *cs = spi->controller_state;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->max_speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15) ||
spi->max_speed_hz > OMAP2_MCSPI_MAX_FREQ)
return -EINVAL;
#endif
if (!cs) {
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
if (!cs)
@@ -1140,15 +1283,29 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
struct omap2_mcspi_dma *mcspi_dma;
struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd;
int par_override = 0;
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
int par_override = 0;
#endif
int status = 0;
u32 chconf;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
struct kbus_drv_data *kdrvdata = NULL;
trace_pxc_kbus(__func__, "enter");
kbus_wago_mpoint(); /* M4 */
#endif
mcspi = spi_controller_get_devdata(ctlr);
mcspi_dma = mcspi->dma_channels + spi_get_chipselect(spi, 0);
cs = spi->controller_state;
cd = spi->controller_data;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (spi->dev.driver == &kbus_driver.driver)
kdrvdata = spi_get_drvdata(spi);
#endif
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
/*
* The target driver could have changed spi->mode in which case
* it will be different from cs->mode (the current hardware setup).
@@ -1158,12 +1315,14 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
*/
if (spi->mode != cs->mode)
par_override = 1;
#endif
omap2_mcspi_set_enable(spi, 0);
if (spi_get_csgpiod(spi, 0))
omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
if (par_override ||
(t->speed_hz != spi->max_speed_hz) ||
(t->bits_per_word != spi->bits_per_word)) {
@@ -1175,6 +1334,8 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
t->bits_per_word == spi->bits_per_word)
par_override = 0;
}
#endif
if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl;
chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
@@ -1202,11 +1363,19 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
if (t->len) {
unsigned count;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
int i;
#endif
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
/* FIXME
At this moment keep fifo disabled due to some issues
that were coming up with large kbus nodes. */
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
ctlr->cur_msg_mapped &&
ctlr->can_dma(ctlr, spi, t))
omap2_mcspi_set_fifo(spi, t, 1);
#endif
omap2_mcspi_set_enable(spi, 1);
@@ -1215,10 +1384,47 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
writel_relaxed(0, cs->base
+ OMAP2_MCSPI_TX0);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
/*
* kbus:
* release irq and check the sync signal
* before sending data
*/
if (kdrvdata && t->tx_buf) {
/*
* XE164 should be ready a long time ago (several us).
* Otherwise something is wrong with the controller!
*/
for (i = 0; i < PAC_KBUS_SYNC_CYCLES; i++) {
if (gpiod_get_value(kdrvdata->gpio_nsync)) /* active low */
continue;
break;
}
if (i >= PAC_KBUS_SYNC_CYCLES) {
trace_pxc_kbus(__func__, "err: sync pin is always high [-EBUSY(-16)]!");
status = -EBUSY;
goto out;
}
/* release the irq pin */
gpiod_set_value(kdrvdata->gpio_nirq, 1);
}
#endif
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
ctlr->cur_msg_mapped &&
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
ctlr->can_dma(ctlr, spi, t)) {
if (kdrvdata)
trace_pxc_kbus(__func__, "DMA: TXRX: Trigger DMA Transfer.");
#endif
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
ctlr->can_dma(ctlr, spi, t))
#endif
count = omap2_mcspi_txrx_dma(spi, t);
}
else
count = omap2_mcspi_txrx_pio(spi, t);
@@ -1233,12 +1439,97 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
if (mcspi->fifo_depth > 0)
omap2_mcspi_set_fifo(spi, t, 0);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
/*
* Special KBUS Treatment
*
*/
if (kdrvdata) {
static u16 *kcmd_txbuf;
int tmp_ret;
kbus_wago_mpoint(); /* MX */
if (t->tx_buf) { /* after tx transfer */
kcmd_txbuf = (u16 *) t->tx_buf;
/* wait for READYn IRQ from xe164 */
trace_pxc_kbus(__func__, "TX0");
kbus_dbg("%s: irq timeout is %dms\n",
__func__, kdrvdata->timeout_ms);
tmp_ret = wait_event_interruptible_timeout(kdrvdata->kbus_irq_wq,
kdrvdata->kbus_irq_state == 0,
msecs_to_jiffies(kdrvdata->timeout_ms));
if (tmp_ret == 0 && kdrvdata->kbus_irq_state) {
status = -ETIMEDOUT;
trace_pxc_kbus(__func__, "TX0: IRQ timeout!");
goto out;
}
if (kbus_error() < 0) {
status = -ENODATA;
trace_pxc_kbus(__func__, "TX0: KBUS ERROR.");
goto out;
}
trace_pxc_kbus(__func__, "TX0: IRQ received");
}
if (kdrvdata->cmdsel) { /* special treatment in command mode */
/* We're getting the data in two nibbles.
* First: 6 bytes header where we get the length for the rest.
* Second: The remaining N bytes.
*/
if (t->rx_buf == kdrvdata->rx_buf) { /* This RX0 */
u16 *kcmd_hdr = (u16 *) t->rx_buf;
u8 lb_cmd = kcmd_hdr[0] & 0xff;
u8 hb_cmd_inv = ~(kcmd_hdr[0] >> 8) & 0xff;
u8 lb_wlen = kcmd_hdr[2] & 0xff;
u8 hb_wlen_inv = ~(kcmd_hdr[2] >> 8) & 0xff;
unsigned int byte_len;
struct spi_transfer *kcmd_tnext;
trace_pxc_kbus(__func__, "RX0");
/* validate header data here */
if (lb_cmd != (kcmd_txbuf[0] & 0xff) ||
hb_cmd_inv != lb_cmd ||
hb_wlen_inv != lb_wlen) {
trace_pxc_kbus(__func__, "RX0: RX0 HDR not valid.");
kbus_dbg("%s[%d]: RX0 HDR not valid: 0x%.4x(TX:0x%.2x)|0x%.4x|0x%.4x\n",
__func__, __LINE__, kcmd_hdr[0], kcmd_txbuf[0] & 0xff,
kcmd_hdr[1], kcmd_hdr[2]);
status = -EPROTO;
goto out;
}
/* get next transfer entry */
kcmd_tnext = list_entry(t->transfer_list.next,
struct spi_transfer, transfer_list);
/* regard word (16bit) count */
byte_len = (kcmd_hdr[2] & 0xff) << 1;
if (byte_len < kcmd_tnext->len) {
kcmd_tnext->len = byte_len;
trace_pxc_kbus(__func__, "RX0: RX1 len updated.");
}
kbus_dbg("%s[%d]: RX1 len set to: %d\n",
__func__, __LINE__, kcmd_tnext->len);
} else if (t->rx_buf != NULL) {
trace_pxc_kbus(__func__, "RX1");
}
}
kbus_wago_mpoint(); /* MX */
}
#endif
out:
#ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION
/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL);
}
#endif
if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl;
@@ -1540,6 +1831,16 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
if (status < 0)
goto disable_pm;
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
dev_dbg(&pdev->dev, "kbusdelay=%d, %s interframe gap delay.\n",
kbusdelay, kbusdelay ? "using" : "NOT using");
#endif
#if 0 /* #ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION */
if (omap2_mcspi_enable_clocks(mcspi) < 0)
goto free_master;
#endif
return status;
disable_pm:
@@ -1618,4 +1919,7 @@ static struct platform_driver omap2_mcspi_driver = {
};
module_platform_driver(omap2_mcspi_driver);
#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION
core_param(kbusdelay, kbusdelay, int, 0000);
#endif
MODULE_LICENSE("GPL");
+142
View File
@@ -0,0 +1,142 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/spi/kbus.h
*
* Copyright (C) 2020 WAGO
* Heinrich Toews <heinrich.toews@wago.com>
*/
#ifndef KBUS_H
#define KBUS_H
#include <linux/types.h>
#define KBUS_DRIVER_MAJOR 240
#define KBUS_IRQ_TIMEOUT 10 /* wait for a max. of 10 ms */
#define KBUS__DEFAULT_SPEED 12000000
#define KBUS_READYN_IRQ
#define KBUS_DATA_FULLDPX
#undef KBUS__WAIT_ACTIVE
#undef KBUS_IRQN_IRQ
/*---------------------------------------------------------------------------*/
#define PAC_KBUS_SYNC_CYCLES 3
#define SPI_MODE_MASK \
(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_3WIRE | \
SPI_LOOP | SPI_NO_CS | SPI_READY)
#define KBUS__MAX_BUF_LEN PAGE_SIZE /* FIXME */
#define KBUS_TESTING 0 /* enable when gpio testing wanted */
#define KBUS__USE_DMA_ONLY 0
#if KBUS_TESTING
#define kbus_wago_mpoint(k) wago_mpoint()
#define kbus_dbg(format, arg...) pr_debug("kbus-dbg:" format, ##arg)
#else
#define kbus_wago_mpoint(k) \
({ \
if (0) \
wago_mpoint(); \
0; \
})
#define kbus_dbg(format, arg...) \
({ \
if (0) \
pr_debug("kbus-dbg:" format, ##arg); \
0; \
})
#endif
#define KBUS_ENABLE_IRQ(irq) \
do { \
if (irq != -1) \
enable_irq(irq); \
} while (0)
#define KBUS_DISABLE_IRQ(irq) \
do { \
if (irq != -1) \
disable_irq(irq); \
} while (0)
struct kbus_drv_data {
u8 cmdsel; /* 0: data, 1: cmd mode */
int kbus_err;
int kbus_err_state;
u8 *tx_buf;
u8 *rx_buf;
bool use_dma;
u32 timeout_ms;
dma_addr_t tx_buf_dma;
dma_addr_t rx_buf_dma;
int kbus_irq;
int kbus_irq_enabled;
int kbus_irq_state;
struct task_struct
*dma_task; /* task pointer to boost the dma task if necessary */
bool kbus_dma_boost_en;
u8 kbus_dma_boost_prio;
const char *kbus_dma_boost_irq_thread;
u8 kbus_dma_normal_prio;
/* gpios */
struct gpio_desc *gpio_nrdy;
struct gpio_desc *gpio_nrst;
struct gpio_desc *gpio_nsync;
struct gpio_desc *gpio_cmdsel;
struct gpio_desc *gpio_nirq;
struct gpio_desc *gpio_nerr;
wait_queue_head_t kbus_irq_wq;
struct spi_device *spi;
const char *kbus_tty_device_name;
};
/* For userspace ioctl communication */
struct kbus_data {
__u8 __user *tx_buf;
__u8 __user *rx_buf;
__u32 byte_len;
__u8 __user *err; /* will only be set when err occurs! */
__u8 __user *err_state;
__u32 timeout_ms;
};
struct kbus_cmd {
__u8 __user *tx_buf;
__u8 __user *rx_buf;
__u32 byte_len_tx;
__u32 byte_len_rx;
__u8 __user *err; /* will only be set when err occurs! */
__u8 __user *err_state;
__u32 timeout_ms;
};
struct kbus_spi_config {
__u8 bits_per_word; /* bits_per_word */
__u8 mode; /* see SPI_ mode bits in spi.h */
__u32 max_speed_hz;
};
extern int kbus_wait_for_irq(void);
extern int kbus_wait_for_event(int *event);
extern int kbus_error(void);
extern int kbus_wait_for_gpio(int gpio);
extern void kbus_boost_dma_task(u8 enable);
/* IOCTL commands */
#define KBUS_IOC_MAGIC 'K'
#define KBUS_IOC_DATA _IOW(KBUS_IOC_MAGIC, 1, struct kbus_data)
#define KBUS_IOC_CMD _IOW(KBUS_IOC_MAGIC, 2, struct kbus_cmd)
#define KBUS_IOC_CONFIG _IOW(KBUS_IOC_MAGIC, 3, struct kbus_spi_config)
#define KBUS_IOC_BINARY _IOW(KBUS_IOC_MAGIC, 4, struct kbus_data)
extern struct spi_driver
kbus_driver; /* used by spi-omap2-mcspi to recognize the kbus device */
#endif /* KBUS_H */
+40
View File
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Copyright (c) 2014 WAGO GmbH & Co. KG
*
* Author: Heinrich Toews <heinrich.toews@wago.com>
*
*/
#ifndef _WAGO_TESTS_H_
#define _WAGO_TESTS_H_
#define WAGO_TEST_DEBUG
#ifdef WAGO_TEST_DEBUG
#define pac_kdebug(format, arg...) \
printk(KERN_INFO "pac-kdebug: " format , ## arg)
#else
#define pac_kdebug(format, arg...) \
({ if (0) printk(KERN_INFO "pac-kdebug: " format , ## arg); 0; })
#endif
#include <linux/gpio.h>
#include <linux/time.h>
#define WAGO_TEST__MAX_MEASUREMENTS 20
#define WAGO_TEST__GPIO 175 /* FB-nINT_GPIO175 */
struct wago_trace_data {
struct timespec64 mpoints[WAGO_TEST__MAX_MEASUREMENTS];
int mpoint_index;
};
extern void wago_tests_init(struct wago_trace_data *tdata, u8 gpios_enable);
extern void wago_tests_deinit(void);
extern void wago_measure_reset(void);
extern void wago_mpoint(void);
#endif /* _WAGO_TESTS_H_ */
+377
View File
@@ -0,0 +1,377 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
*
* Copyright (c) 2015 WAGO GmbH & Co. KG
*
* Author: Heinrich Toews <heinrich.toews@wago.com>
*
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM pxc
#if !defined(_TRACE_PXC_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_PXC_H
#include <linux/tracepoint.h>
#ifdef PFC_CLOCK_TRACER
TRACE_EVENT(pfc_clock, /* trace pfc clock events */
TP_PROTO(cycle_t cycle_now, cycle_t cycle_delta, struct timekeeper *tk, s64 nsec, char *msg),
TP_ARGS(cycle_now, cycle_delta, tk, nsec, msg),
TP_STRUCT__entry(
__field(cycle_t, cycle_now)
__field(cycle_t, cycle_delta)
__field(s64, nsec)
__field(u32, mult)
__field(u32, shift)
__field(u64, xtime_nsec)
__array(char, msg, 128)
),
TP_fast_assign(
__entry->cycle_now = cycle_now;
__entry->cycle_delta = cycle_delta;
__entry->nsec = nsec;
__entry->mult = tk->mult;
__entry->shift = tk->shift;
__entry->xtime_nsec = tk->xtime_nsec;
strncpy( __entry->msg, msg, 128);
),
TP_printk("%llu d:%llu->%lli|%u|%u|%llu|%s",
__entry->cycle_now, __entry->cycle_delta, __entry->nsec,
__entry->mult, __entry->shift, __entry->xtime_nsec,
__entry->msg)
);
#endif
#if defined(PXC_ETH_EMAC)
TRACE_EVENT(pxc_eth_emac, /* trace davinci_emac events */
TP_PROTO(struct emac_priv *priv, char *msg),
TP_ARGS(priv, msg),
TP_STRUCT__entry(
__field(unsigned, link)
__field(unsigned, speed)
__field(unsigned, duplex)
__array(char, phy_id, 32)
__array(char, other_phy_id, 32)
__array(char, msg, 128)
__array(char, devname, 64)
),
TP_fast_assign(
__entry->link = priv->link;
__entry->speed = priv->speed;
__entry->duplex = priv->duplex;
strncpy( __entry->phy_id, priv->phy_id, 32);
strncpy(__entry->other_phy_id, priv->other_phy_id, 32);
strncpy( __entry->msg, msg, 128);
if (priv->ndev)
strncpy(__entry->devname, priv->ndev->name, 64);
),
TP_printk("%6s 1-%s 2-%s: %4s|%d/%4s %s", __entry->devname, __entry->phy_id, __entry->other_phy_id,
__entry->link ? "UP" : "DOWN", __entry->speed, __entry->duplex == 1 ? "Full" : "Half",
__entry->msg)
);
TRACE_EVENT(pxc_eth_emac_phy, /* trace phy events */
TP_PROTO(struct phy_device *phydev, char *msg),
TP_ARGS(phydev, msg),
TP_STRUCT__entry(
__array(char, msg, 128)
__array(char, phyname, 16)
__field(int, link)
__field(int, speed)
__field(int, duplex) /* DUPLEX_HALF=0, DUPLEX_FULL=1 */
__field(int, state)
__field(int, irq)
__array(char, devname, 64)
__field(u32, dev_flags)
),
TP_fast_assign(
if (phydev->attached_dev)
strncpy(__entry->devname, phydev->attached_dev->name, 64);
strncpy(__entry->msg, msg, 128);
strncpy(__entry->phyname, dev_name(&phydev->dev), 16);
__entry->link = phydev->link;
__entry->speed = phydev->speed;
__entry->duplex = phydev->duplex;
__entry->state = phydev->state;
__entry->irq = phydev->irq;
__entry->dev_flags = phydev->dev_flags;
),
TP_printk("%6s(%s) %4s|%d/%4s|%d|%d|%u %s", __entry->devname, __entry->phyname,
__entry->link ? "UP" : "DOWN", __entry->speed, __entry->duplex == 1 ? "Full" : "Half",
__entry->state, __entry->irq, __entry->dev_flags, __entry->msg)
);
#endif
#if defined(PXC_SPI_TRACER)
TRACE_EVENT(pxc_spi, /* trace mcspi events */
TP_PROTO(char *msg, const char *func, int data),
TP_ARGS(msg, func, data),
TP_STRUCT__entry(
__array(char, msg, 128)
__array(char, func, 32)
__field(int, data)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
strncpy( __entry->func, func, 32);
__entry->data = data;
),
TP_printk("%s[%d]: %s", __entry->func, __entry->data, __entry->msg)
);
TRACE_EVENT(pxc_spi_measure_a, /* trace mcspi events */
TP_PROTO(char *msg, struct wago_trace_data *tdata),
TP_ARGS(msg, tdata),
TP_STRUCT__entry(
__array(char, msg, 128)
__field(unsigned int, completion_delay)
__field(unsigned int, completion_delay_work)
__field(unsigned int, async_delay)
__field(unsigned int, enqueue_delay)
__field(unsigned int, work_delay)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
if (tdata->mpoint_index > 5) {
__entry->completion_delay = tdata->mpoints[5].tv_nsec - tdata->mpoints[1].tv_nsec;
__entry->completion_delay_work = tdata->mpoints[4].tv_nsec - tdata->mpoints[2].tv_nsec;
__entry->async_delay = tdata->mpoints[1].tv_nsec - tdata->mpoints[0].tv_nsec;
__entry->enqueue_delay = tdata->mpoints[2].tv_nsec - tdata->mpoints[1].tv_nsec;
__entry->work_delay = tdata->mpoints[3].tv_nsec - tdata->mpoints[2].tv_nsec;
}
),
TP_printk("%s: completion_delay=%u, completion_delay_work=%u, async_delay=%u, enqueue_delay=%u, work_delay=%u",
__entry->msg, __entry->completion_delay, __entry->completion_delay_work,
__entry->async_delay, __entry->enqueue_delay, __entry->work_delay)
);
TRACE_EVENT(pxc_spi_measure_b, /* trace mcspi events */
TP_PROTO(char *msg, struct wago_trace_data *tdata),
TP_ARGS(msg, tdata),
TP_STRUCT__entry(
__array(char, msg, 128)
__field(unsigned int, completion_delay)
__field(unsigned int, completion_delay_work)
__field(unsigned int, async_delay)
__field(unsigned int, enqueue_delay)
__field(unsigned int, work_delay)
__field(unsigned int, delay1)
__field(unsigned int, delay2)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
if (tdata->mpoint_index > 8) {
__entry->completion_delay = tdata->mpoints[7].tv_nsec - tdata->mpoints[2].tv_nsec;
__entry->completion_delay_work = tdata->mpoints[6].tv_nsec - tdata->mpoints[4].tv_nsec;
__entry->async_delay = tdata->mpoints[2].tv_nsec - tdata->mpoints[1].tv_nsec;
__entry->enqueue_delay = tdata->mpoints[4].tv_nsec - tdata->mpoints[2].tv_nsec;
__entry->work_delay = tdata->mpoints[5].tv_nsec - tdata->mpoints[4].tv_nsec;
__entry->delay1 = tdata->mpoints[7].tv_nsec - tdata->mpoints[1].tv_nsec;
__entry->delay2 = tdata->mpoints[8].tv_nsec - tdata->mpoints[0].tv_nsec;
}
),
TP_printk("%s: completion_delay=%u, completion_delay_work=%u, async_delay=%u, enqueue_delay=%u, work_delay=%u, delay1=%u, delay2=%u",
__entry->msg, __entry->completion_delay, __entry->completion_delay_work,
__entry->async_delay, __entry->enqueue_delay, __entry->work_delay, __entry->delay1, __entry->delay2)
);
#endif /* PXC_SPI_TRACER */
#if defined(PXC_SPI_KBUS_TRACER)
TRACE_EVENT(pxc_kbus, /* trace kbus events */
TP_PROTO(const char *func, char *msg),
TP_ARGS(func, msg),
TP_STRUCT__entry(
__array(char, msg, 128)
__array(char, func, 32)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
strncpy( __entry->func, func, 32);
),
TP_printk("%s:%s", __entry->func, __entry->msg)
);
TRACE_EVENT(pxc_kbusmsg, /* trace kbus events */
TP_PROTO(const char *func, struct spi_message *m, char *msg),
TP_ARGS(func, m, msg),
TP_STRUCT__entry(
__array(char, msg, 128)
__array(char, func, 32)
__field(int, status)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
strncpy( __entry->func, func, 32);
__entry->status = m->status;
),
TP_printk("%s:%s m->status=%d", __entry->func, __entry->msg, __entry->status)
);
TRACE_EVENT(pxc_kbusdump, /* trace kbus events */
TP_PROTO(const char *func, char *msg, int word_len, u16 word),
TP_ARGS(func, msg, word_len, word),
TP_STRUCT__entry(
__array(char, func, 32)
__array(char, msg, 128)
__field( int, word_len)
__field( u16, word)
),
TP_fast_assign(
strncpy( __entry->func, func, 32);
strncpy( __entry->msg, msg, 128);
__entry->word_len = word_len;
__entry->word = word;
),
TP_printk("%s:%s-%d 0x%x", __entry->func, __entry->msg, __entry->word_len, __entry->word) /* 0x%04x */
);
TRACE_EVENT(pxc_buf32,
TP_PROTO(const char *prefix, char *in_buf, int len, int offs),
TP_ARGS(prefix, in_buf, len, offs),
TP_STRUCT__entry(
__array(char, prefix, 32)
__field(int, copy_len)
__array(char, buf, 32)
__field(char *, in_buf_p)
),
TP_fast_assign(
strncpy(__entry->prefix, prefix, 32);
if (len < 32)
memset(__entry->buf, 0, 32);
strncpy(__entry->buf, in_buf + offs, len > 32 ? 32 : len);
__entry->copy_len = len;
__entry->in_buf_p = in_buf;
),
TP_printk("DATADUMP(%s) copylen %4d (in_buf_p %p) "
"[%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
"-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
"-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
"-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x]",
__entry->prefix,
__entry->copy_len,
__entry->in_buf_p,
__entry->buf[0], __entry->buf[1], __entry->buf[2], __entry->buf[3],
__entry->buf[4], __entry->buf[5], __entry->buf[6], __entry->buf[7],
__entry->buf[8], __entry->buf[9], __entry->buf[10], __entry->buf[11],
__entry->buf[12], __entry->buf[13], __entry->buf[14], __entry->buf[15],
__entry->buf[16], __entry->buf[17], __entry->buf[18], __entry->buf[19],
__entry->buf[20], __entry->buf[21], __entry->buf[22], __entry->buf[23],
__entry->buf[24], __entry->buf[25], __entry->buf[26], __entry->buf[27],
__entry->buf[28], __entry->buf[29], __entry->buf[30], __entry->buf[31])
);
TRACE_EVENT(pxc_kbus_mdata, /* trace kbus measurement events */
TP_PROTO(const char *func, char *msg, struct wago_trace_data *tdata),
TP_ARGS(func, msg, tdata),
TP_STRUCT__entry(
__array(char, msg, 128)
__array(char, func, 32)
__field(unsigned int, delay1)
__field(unsigned int, delay2)
),
TP_fast_assign(
strncpy( __entry->msg, msg, 128);
strncpy( __entry->func, func, 32);
if (tdata->mpoint_index > 3) {
__entry->delay1 = tdata->mpoints[3].tv_nsec - tdata->mpoints[0].tv_nsec;
__entry->delay2 = tdata->mpoints[2].tv_nsec - tdata->mpoints[1].tv_nsec;
}
),
TP_printk("%s:%s delay1=%u delay2=%u", __entry->func, __entry->msg,
__entry->delay1, __entry->delay2)
);
#endif /* PXC_SPI_KBUS_TRACER */
#if defined(PXC_CAN_TRACER)
TRACE_EVENT(pxc_canpkt,
TP_PROTO(struct can_frame *canframe),
TP_ARGS(canframe),
TP_STRUCT__entry(
__field(struct can_frame *, canframe)
),
TP_fast_assign(
__entry->canframe = canframe;
),
TP_printk("%s-0x%x: can_id=0x%x, len=%d, data:%.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x.%.2x",
(__entry->canframe->can_id & CAN_ERR_FLAG) ? " err" : "data",
__entry->canframe->can_id >> CAN_EFF_ID_BITS,
__entry->canframe->can_id & CAN_ERR_MASK,
__entry->canframe->can_dlc,
__entry->canframe->data[0],
__entry->canframe->data[1],
__entry->canframe->data[2],
__entry->canframe->data[3],
__entry->canframe->data[4],
__entry->canframe->data[5],
__entry->canframe->data[6],
__entry->canframe->data[7])
);
#endif /* PXC_CAN_TRACER */
#endif /* if !defined(_TRACE_PXC_H) || defined(TRACE_HEADER_MULTI_READ) */
/* This part must be outside protection */
#include <trace/define_trace.h>