Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From 7661790f85f44433fe368985dced95775ad48b91 Mon Sep 17 00:00:00 2001
- From: Jacko Dirks <[email protected]>
- Date: Sun, 19 Jan 2020 17:46:27 +0100
- Subject: [PATCH] spi: add driver for bcm2835 bsc
- The BCM2835 contains a Broadcom Serial Device. This device can act as a
- SPI slave or an I2C slave. This driver implements the device as an SPI
- slave with IRQ support.
- This commit also includes the necessary changes to the DTS system so
- that the driver is included when booting the RPI 4 with the correct
- overlay selected. This driver might work on different devices with
- different DTS settings, only the Raspberry Pi 4 has been tested at this
- point.
- ---
- arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 10 +
- arch/arm/boot/dts/bcm2838.dtsi | 5 +
- arch/arm/boot/dts/bcm283x.dtsi | 10 +
- arch/arm/boot/dts/overlays/Makefile | 1 +
- .../dts/overlays/spislave-1cs-overlay.dts | 44 ++
- arch/arm/configs/bcm2711_defconfig | 1 +
- drivers/spi/Kconfig | 11 +
- drivers/spi/Makefile | 3 +
- drivers/spi/spi-bcm2835-slave.c | 502 ++++++++++++++++++
- 9 files changed, 587 insertions(+)
- create mode 100644 arch/arm/boot/dts/overlays/spislave-1cs-overlay.dts
- create mode 100644 drivers/spi/spi-bcm2835-slave.c
- diff --git a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
- index a967a7b86f2c..ebc25d985db9 100644
- --- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
- +++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
- @@ -209,6 +209,16 @@
- brcm,function = <BCM2835_FSEL_GPIO_OUT>;
- };
- + spi_slave_pins: spi_slave_pins {
- + brcm,pins = <9 10 11>;
- + brcm,function = <BCM2835_FSEL_ALT3>;
- + };
- +
- + spi_slave_cs_pins: spi_slave_cs_pins {
- + brcm,pins = <8>;
- + brcm,function = <BCM2835_FSEL_GPIO_IN>;
- + };
- +
- i2c0_pins: i2c0 {
- brcm,pins = <0 1>;
- brcm,function = <BCM2835_FSEL_ALT0>;
- diff --git a/arch/arm/boot/dts/bcm2838.dtsi b/arch/arm/boot/dts/bcm2838.dtsi
- index 5aef393c09a6..7ed68cbf5824 100644
- --- a/arch/arm/boot/dts/bcm2838.dtsi
- +++ b/arch/arm/boot/dts/bcm2838.dtsi
- @@ -148,6 +148,11 @@
- status = "disabled";
- };
- + spi_slave: spi_slave@7e214000 {
- + reg = <0x7e214000 0x0200>;
- + interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
- + };
- +
- i2c3: i2c@7e205600 {
- compatible = "brcm,bcm2835-i2c";
- reg = <0x7e205600 0x200>;
- diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
- index c8eaaf608df5..709e985516d6 100644
- --- a/arch/arm/boot/dts/bcm283x.dtsi
- +++ b/arch/arm/boot/dts/bcm283x.dtsi
- @@ -410,6 +410,16 @@
- status = "disabled";
- };
- + spi_slave: spi_slave@7e214000 {
- + compatible = "brcm,bcm2835-spi-slave";
- + reg = <0x7e214000 0x200>;
- + interrupts = <2 11>;
- + clocks = <&clocks BCM2835_CLOCK_VPU>;
- + #address-cells = <1>;
- + #size-cells = <0>;
- + status = "disabled";
- + };
- +
- i2c0: i2c@7e205000 {
- compatible = "brcm,bcm2835-i2c";
- reg = <0x7e205000 0x200>;
- diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile
- index e5e7183d2cdb..edd76b76eda0 100644
- --- a/arch/arm/boot/dts/overlays/Makefile
- +++ b/arch/arm/boot/dts/overlays/Makefile
- @@ -167,6 +167,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
- spi5-2cs.dtbo \
- spi6-1cs.dtbo \
- spi6-2cs.dtbo \
- + spislave-1cs.dtbo \
- ssd1306.dtbo \
- superaudioboard.dtbo \
- sx150x.dtbo \
- diff --git a/arch/arm/boot/dts/overlays/spislave-1cs-overlay.dts b/arch/arm/boot/dts/overlays/spislave-1cs-overlay.dts
- new file mode 100644
- index 000000000000..b4df98215f03
- --- /dev/null
- +++ b/arch/arm/boot/dts/overlays/spislave-1cs-overlay.dts
- @@ -0,0 +1,44 @@
- +/dts-v1/;
- +/plugin/;
- +
- +
- +/ {
- + compatible = "brcm,bcm2711";
- +
- + fragment@0 {
- + target = <&spi_slave_cs_pins>;
- + frag0: __overlay__ {
- + brcm,pins = <8>;
- + brcm,function = <1>; /* output */
- + };
- + };
- +
- + fragment@1 {
- + target = <&spi_slave>;
- + frag1: __overlay__ {
- + /* needed to avoid dtc warning */
- + #address-cells = <1>;
- + #size-cells = <0>;
- +
- + pinctrl-names = "default";
- + pinctrl-0 = <&spi_slave_pins &spi_slave_cs_pins>;
- + cs-gpios = <&gpio 8 1>;
- + status = "okay";
- +
- + spidev0_0: slave@0 {
- + compatible = "spidev";
- + reg = <0>; /* CE0 */
- + #address-cells = <1>;
- + #size-cells = <0>;
- + spi-max-frequency = <125000000>;
- + status = "okay";
- + };
- + };
- + };
- +
- + __overrides__ {
- + cs0_pin = <&frag0>,"brcm,pins:0",
- + <&frag1>,"cs-gpios:4";
- + cs0_spidev = <&spidev0_0>,"status";
- + };
- +};
- diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig
- index ea4c5ccde850..7784da699f19 100644
- --- a/arch/arm/configs/bcm2711_defconfig
- +++ b/arch/arm/configs/bcm2711_defconfig
- @@ -694,6 +694,7 @@ CONFIG_I2C_TINY_USB=m
- CONFIG_SPI=y
- CONFIG_SPI_BCM2835=m
- CONFIG_SPI_BCM2835AUX=m
- +CONFIG_SPI_BCM2835_BSC=m
- CONFIG_SPI_GPIO=m
- CONFIG_SPI_SPIDEV=m
- CONFIG_SPI_SLAVE=y
- diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
- index 671d078349cc..7fd3563e36d7 100644
- --- a/drivers/spi/Kconfig
- +++ b/drivers/spi/Kconfig
- @@ -815,6 +815,17 @@ config SPI_SLAVE_SYSTEM_CONTROL
- SPI slave handler to allow remote control of system reboot, power
- off, halt, and suspend.
- +config SPI_BCM2835_SLAVE
- + tristate "BCM2835 SLAVE SPI controller"
- + depends on GPIOLIB
- + depends on ARCH_BCM2835 || COMPILE_TEST
- + help
- + This selects a driver for the Broadcom BCM2835 BSC SPI slave.
- +
- + The BCM2835 contains a Broadcom Serial Controller or BSC.
- + The BSC can be used as an SPI slave or an I2C slave.
- + This driver uses the BSC as an SPI slave.
- +
- endif # SPI_SLAVE
- endif # SPI
- diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
- index a90d55970036..76c05023a80f 100644
- --- a/drivers/spi/Makefile
- +++ b/drivers/spi/Makefile
- @@ -111,3 +111,6 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
- # SPI slave protocol handlers
- obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
- obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
- +
- +# SPI slave controller drivers
- +obj-$(CONFIG_SPI_BCM2835_SLAVE) += spi-bcm2835-slave.o
- diff --git a/drivers/spi/spi-bcm2835-slave.c b/drivers/spi/spi-bcm2835-slave.c
- new file mode 100644
- index 000000000000..6162b4c7827c
- --- /dev/null
- +++ b/drivers/spi/spi-bcm2835-slave.c
- @@ -0,0 +1,502 @@
- +// SPDX-License-Identifier: GPL-2.0+
- +/*
- + * Driver for Broadcom BCM2835 BSC SPI slave Controller
- + *
- + * This device (BCM2835 BSC) is usable as an SPI and an I2C slave, therefore
- + * some registers are not used. The BSC does not support DMA.
- + *
- + * Copyright (C) 2020 Jacko Dirks
- + *
- + * This driver is inspired by:
- + * spi-bcm2835.c, Copyright (C) 2012 Chris Boot et al
- + * spi-sh-msiof.c, Copyright (c) 2009 Magnus Damm et al
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation; either version 2 of the License, or
- + * (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + */
- +
- +
- +/**
- + * TODO:
- + * - implement chip select
- + * Currently, we are acting as if the slave is always selected.
- + */
- +#include <linux/module.h>
- +#include <linux/completion.h>
- +#include <linux/of.h>
- +#include <linux/of_address.h>
- +#include <linux/of_device.h>
- +#include <linux/of_gpio.h>
- +#include <linux/of_irq.h>
- +#include <linux/spi/spi.h>
- +
- +/* BSC Register offsets */
- +#define BSC_DR 0x0
- +#define BSC_RSR 0x4
- +#define BSC_SLV 0x8
- +#define BSC_CR 0xc
- +#define BSC_FR 0x10
- +#define BSC_IFLS 0x14
- +#define BSC_IMSC 0x18
- +#define BSC_RIS 0x1c
- +#define BSC_MIS 0x20
- +#define BSC_ICR 0x24
- +#define BSC_DMACR 0x28
- +#define BSC_TDR 0x2c
- +#define BSC_GPUSTAT 0x30
- +#define BSC_HCTRL 0x34
- +#define BSC_DEBUG1 0x38
- +#define BSC_DEBUG2 0x4c
- +
- +/* CR register fields, only those relevant to SPI */
- +#define BSC_CR_INV_TXF 0x2000
- +#define BSC_CR_TESTFIFO 0x0800
- +#define BSC_CR_INV_RXF 0x0400
- +#define BSC_CR_RXE 0x0200
- +#define BSC_CR_TXE 0x0100
- +#define BSC_CR_BRK 0x0080
- +#define BSC_CR_CPOL 0x0010
- +#define BSC_CR_CPHA 0x0008
- +#define BSC_CR_I2C 0x0004
- +#define BSC_CR_SPI 0x0002
- +#define BSC_CR_EN 0x0001
- +
- +/* DR register fields, only those relevant to SPI */
- +#define BSC_DR_RXBUSY 0x200000
- +#define BSC_DR_TXFE 0x100000
- +#define BSC_DR_RXFF 0x080000
- +#define BSC_DR_TXFF 0x040000
- +#define BSC_DR_RXFE 0x020000
- +#define BSC_DR_TXBUSY 0x010000
- +#define BSC_DR_UE 0x000200
- +#define BSC_DR_OE 0x000100
- +
- +/* FR register fields */
- +#define BSC_FR_RXBUSY 0x20
- +#define BSC_FR_TXFE 0x10
- +#define BSC_FR_RXFF 0x08
- +#define BSC_FR_TXFF 0x04
- +#define BSC_FR_RXFE 0x02
- +#define BSC_FR_TXBUSY 0x01
- +
- +/* IMSC register_fields */
- +#define BSC_IMSC_OEIM 0x8
- +#define BSC_IMSC_BEIM 0x4
- +#define BSC_IMSC_TXIM 0x2
- +#define BSC_IMSC_RXIM 0x1
- +
- +/* ICR register fields */
- +#define BSC_ICR_OEIC 0x8
- +#define BSC_ICR_BEIC 0x4
- +#define BSC_ICR_TXIC 0x2
- +#define BSC_ICR_RXIC 0x1
- +
- +/* RIS register fields */
- +#define BSC_RIS_OERIS 0x8
- +#define BSC_RIS_BERIS 0x4
- +#define BSC_RIS_TXRIS 0x2
- +#define BSC_RIS_RXRIS 0x1
- +
- +/* IFLS TXIFLSEL FIFO level select */
- +#define BSC_IFLS_TXIFLSEL_ONE_EIGHTH 0x0
- +#define BSC_IFLS_TXIFLSEL_ONE_FOURTH 0x1
- +#define BSC_IFLS_TXIFLSEL_ONE_HALF 0x2
- +#define BSC_IFLS_TXIFLSEL_THREE_FOURTH 0x3
- +#define BSC_IFLS_TXIFLSEL_SEVEN_EIGHTHS 0x4
- +
- +#define DRV_NAME "spi-bcm2835-slave"
- +#define BCM2835_BSC_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS)
- +
- +struct bcm2835_slave_spi {
- + void __iomem *regs;
- + int irq;
- + const uint8_t *tx_buf;
- + uint8_t *rx_buf;
- + int tx_len;
- + int rx_len;
- +};
- +
- +static uint32_t bcm2835_slave_read(struct bcm2835_slave_spi *bs, unsigned reg)
- +{
- + return readl(bs->regs + reg);
- +}
- +
- +static void bcm2835_slave_write(struct bcm2835_slave_spi *bs, unsigned reg, uint32_t val)
- +{
- + writel(val, bs->regs + reg);
- +}
- +
- +static void bcm2835_slave_fifo_write(struct bcm2835_slave_spi *bs)
- +{
- + uint8_t byte;
- + // While we expect bytes and the tx buffer is not full.
- + while (bs->tx_len > 0 && !(bcm2835_slave_read(bs, BSC_FR) & BSC_FR_TXFF) ) {
- + byte = bs->tx_buf ? *bs->tx_buf : 0;
- + bs->tx_buf++;
- + bs->tx_len--;
- + bcm2835_slave_write(bs, BSC_DR, byte);
- + }
- +}
- +
- +static void bcm2835_slave_fifo_read(struct bcm2835_slave_spi *bs)
- +{
- + uint8_t byte;
- + // While we expect bytes and the rx buffer is not empty.
- + while (bs->rx_len > 0 && !(bcm2835_slave_read(bs, BSC_FR) & BSC_FR_RXFE)) {
- + byte = bcm2835_slave_read(bs, BSC_DR) & 0xff;
- + if (bs->rx_buf) {
- + *bs->rx_buf = byte;
- + bs->rx_buf++;
- + }
- + bs->rx_len--;
- + }
- +}
- +
- +static int bcm2835_slave_validate(struct spi_device *spi)
- +{
- + uint16_t mode = spi->mode;
- + // 3 wire mode is not supported
- + if (mode & SPI_3WIRE)
- + return -EINVAL;
- + // Loop is not supported
- + if (mode & SPI_LOOP)
- + return -EINVAL;
- + return 0;
- +}
- +
- +static int bcm2835_test(struct bcm2835_slave_spi *bs, struct device *dev)
- +{
- + unsigned i, j;
- + // Test wether or not the BSC actually functions
- + // TODO remove in the future
- + bcm2835_slave_write(bs, BSC_SLV, 0);
- + bcm2835_slave_write(bs, BSC_DEBUG2, 0x400000);
- + bcm2835_slave_write(bs, BSC_ICR,
- + BSC_ICR_OEIC | BSC_ICR_BEIC | BSC_ICR_TXIC | BSC_ICR_RXIC);
- + uint32_t fr = bcm2835_slave_read(bs, BSC_FR);
- + printk("_TEST_INITIAL: 0x%X\n", fr);
- + printk("_TEST_INITIAL: RXFLEVEL 0x%X\n", (fr & 0xf800) >> 11);
- + printk("_TEST_INITIAL: TXLEVEL 0x%X\n", (fr & 0x7e0) >> 6);
- + printk("_TEST_INITIAL: RXBUSY: 0x%X\n", (fr & BSC_FR_RXBUSY));
- + printk("_TEST_INITIAL: TXFE: 0x%X\n", (fr & BSC_FR_TXFE));
- + printk("_TEST_INITIAL: RXFF: 0x%X\n", (fr & BSC_FR_RXFF));
- + printk("_TEST_INITIAL: TXFF: 0x%X\n", (fr & BSC_FR_TXFF));
- + printk("_TEST_INITIAL: RXFE: 0x%X\n", (fr & BSC_FR_RXFE));
- + printk("_TEST_INITIAL: TXBUSY: 0x%X\n", (fr & BSC_FR_TXBUSY));
- + printk("_TEST_INITIAL: RSR: 0x%x\n", bcm2835_slave_read(bs,BSC_RSR));
- + printk("_TEST_INITIAL: RIS: 0x%x\n", bcm2835_slave_read(bs,BSC_RIS));
- + printk("_TEST_INITIAL: IMSC: 0x%x\n", bcm2835_slave_read(bs,BSC_IMSC));
- + printk("_TEST_INITIAL: DEBUG1: 0x%x\n", bcm2835_slave_read(bs,BSC_DEBUG1));
- + printk("_TEST_INITIAL: DEBUG2: 0x%x\n", bcm2835_slave_read(bs,BSC_DEBUG2));
- + for (i = 0; i <= 0; ++i) {
- + bcm2835_slave_write(bs, BSC_DR, i & 0xff);
- + uint32_t fr = bcm2835_slave_read(bs, BSC_FR);
- + printk("_TEST: 0x%X\n", fr);
- + printk("_TEST: RXFLEVEL 0x%X\n", (fr & 0xf800) >> 11);
- + printk("_TEST: TXLEVEL 0x%X\n", (fr & 0x7e0) >> 6);
- + printk("_TEST: RXBUSY: 0x%X\n", (fr & BSC_FR_RXBUSY));
- + printk("_TEST: TXFE: 0x%X\n", (fr & BSC_FR_TXFE));
- + printk("_TEST: RXFF: 0x%X\n", (fr & BSC_FR_RXFF));
- + printk("_TEST: TXFF: 0x%X\n", (fr & BSC_FR_TXFF));
- + printk("_TEST: RXFE: 0x%X\n", (fr & BSC_FR_RXFE));
- + printk("_TEST: TXBUSY: 0x%X\n", (fr & BSC_FR_TXBUSY));
- + printk("_TEST: RSR: 0x%x\n", bcm2835_slave_read(bs,BSC_RSR));
- + printk("_TEST: RIS: 0x%x\n", bcm2835_slave_read(bs,BSC_RIS));
- + printk("_TEST: IMSC: 0x%x\n", bcm2835_slave_read(bs,BSC_IMSC));
- + printk("_TEST: DEBUG1: 0x%x\n", bcm2835_slave_read(bs,BSC_DEBUG1));
- + printk("_TEST: DEBUG2: 0x%x\n", bcm2835_slave_read(bs,BSC_DEBUG2));
- + j = bcm2835_slave_read(bs, BSC_TDR) & 0xff;
- + if (i != j) {
- + dev_err(dev,
- + "Device test failed (TX). Expected: %u, but got: %u\n",
- + i, j);
- + return -1;
- + }
- + }
- + for (i = 0; i <= 255; ++i) {
- + bcm2835_slave_write(bs, BSC_TDR, i & 0xff);
- + j = bcm2835_slave_read(bs, BSC_DR) & 0xff;
- + if (i != j) {
- + dev_err(dev,
- + "Device test failed (RX). Expected: %u, but got: %u\n",
- + i, j);
- + return -1;
- + }
- + }
- + return 0;
- +}
- +
- +static void bcm2835_slave_reset(struct bcm2835_slave_spi *bs)
- +{
- + uint32_t dr;
- + uint32_t cr = bcm2835_slave_read(bs, BSC_CR);
- + cr &= ~BSC_CR_SPI;
- + cr |= BSC_CR_BRK;
- + bcm2835_slave_write(bs, BSC_CR, 0);
- + bcm2835_slave_write(bs, BSC_IMSC, 0);
- + bcm2835_slave_write(bs, BSC_ICR,
- + BSC_ICR_OEIC | BSC_ICR_BEIC | BSC_ICR_TXIC | BSC_ICR_RXIC);
- + bcm2835_slave_write(bs, BSC_RSR, 0);
- + bcm2835_slave_write(bs, BSC_CR, cr);
- +}
- +
- +
- +static int bcm2835_slave_spi_setup(struct spi_device *spi)
- +{
- + int err;
- + err = bcm2835_slave_validate(spi);
- + if (err < 0)
- + return err;
- + // TODO
- + // There could be some stuff with CS, but we ignore that for now
- + return 0;
- +}
- +
- +static void bcm2835_slave_spi_set_cs(struct spi_device *spi, bool gpio_level)
- +{
- + // TODO
- +}
- +
- +static irqreturn_t bcm2835_slave_spi_interrupt(int irq, void *dev_id)
- +{
- + // TODO some interrupts might come from errors.
- + struct spi_controller *slave = dev_id;
- + struct bcm2835_slave_spi *bs = spi_controller_get_devdata(slave);
- + // Check if we enabled this interrupt
- + if (!bcm2835_slave_read(bs, BSC_IMSC))
- + return IRQ_NONE;
- + // Clear interrupt
- + bcm2835_slave_write(bs, BSC_ICR,
- + BSC_ICR_OEIC | BSC_ICR_BEIC | BSC_ICR_TXIC | BSC_ICR_RXIC);
- + // Read and write bytes
- + bcm2835_slave_fifo_read(bs);
- + bcm2835_slave_fifo_write(bs);
- + if (bs->rx_len <= 0) {
- + // Transaction finished.
- + bcm2835_slave_reset(bs);
- + complete(&slave->xfer_completion);
- + }
- + return IRQ_HANDLED;
- +}
- +
- +static int bcm2835_slave_spi_transfer_one(struct spi_controller *slave,
- + struct spi_device *spi, struct spi_transfer *tfr)
- +{
- + struct bcm2835_slave_spi *bs = spi_controller_get_devdata(slave);
- + uint32_t cr, dr;
- + int err = bcm2835_slave_validate(spi);
- + if (err < 0)
- + return err;
- + // We are a slave and there is no DMA support,
- + // this means that IRQ based transactions are the only viable option.
- + // Polling is not an option because we cannot know when the clock runs,
- + // so we cannot estimate when the transfer will be finished.
- +
- + // Set the buffers and lengths
- + bs->tx_buf = tfr->tx_buf;
- + bs->rx_buf = tfr->rx_buf;
- + bs->tx_len = tfr->len;
- + bs->rx_len = tfr->len;
- + // Fill the TX buffer
- + bcm2835_slave_fifo_write(bs);
- + printk("_T1: transfer_one");
- + uint32_t fr = bcm2835_slave_read(bs, BSC_FR);
- + printk("_T1: 0x%X\n", fr);
- + printk("_T1: RXFLEVEL 0x%X\n", (fr & 0xf800) >> 11);
- + printk("_T1: TXLEVEL 0x%X\n", (fr & 0x7e0) >> 6);
- + printk("_T1: RXBUSY: 0x%X\n", (fr & BSC_FR_RXBUSY));
- + printk("_T1: TXFE: 0x%X\n", (fr & BSC_FR_TXFE));
- + printk("_T1: RXFF: 0x%X\n", (fr & BSC_FR_RXFF));
- + printk("_T1: TXFF: 0x%X\n", (fr & BSC_FR_TXFF));
- + printk("_T1: RXFE: 0x%X\n", (fr & BSC_FR_RXFE));
- + printk("_T1: TXBUSY: 0x%X\n", (fr & BSC_FR_TXBUSY));
- + // Start running in interrupt mode.
- + // TODO do not interrupt after every byte (I suppose?)
- + // Reset underrun or overrun errors
- + bcm2835_slave_write(bs, BSC_RSR, 0);
- + cr = bcm2835_slave_read(bs, BSC_CR);
- + cr &= ~BSC_CR_BRK;
- + cr |= (BSC_CR_SPI);
- + bcm2835_slave_write(bs, BSC_IFLS, BSC_IFLS_TXIFLSEL_ONE_EIGHTH);
- + bcm2835_slave_write(bs, BSC_IMSC, BSC_IMSC_RXIM);
- + bcm2835_slave_write(bs, BSC_CR, cr);
- +
- + // Return one to signal that the transfer is not yet complete.
- + return 1;
- +}
- +
- +static void bcm2835_slave_spi_handle_err(struct spi_controller *master,
- + struct spi_message *msg)
- +{
- + // TODO
- +}
- +
- +static int bcm2835_slave_spi_slave_abort(struct spi_controller *slave)
- +{
- + struct bcm2835_slave_spi *bs = spi_controller_get_devdata(slave);
- + printk("_S_ABORT: transfer_one");
- + uint32_t fr = bcm2835_slave_read(bs, BSC_FR);
- + printk("_S_ABORT: 0x%X\n", fr);
- + printk("_S_ABORT: RXFLEVEL 0x%X\n", (fr & 0xf800) >> 11);
- + printk("_S_ABORT: TXLEVEL 0x%X\n", (fr & 0x7e0) >> 6);
- + printk("_S_ABORT: RXBUSY: 0x%X\n", (fr & BSC_FR_RXBUSY));
- + printk("_S_ABORT: TXFE: 0x%X\n", (fr & BSC_FR_TXFE));
- + printk("_S_ABORT: RXFF: 0x%X\n", (fr & BSC_FR_RXFF));
- + printk("_S_ABORT: TXFF: 0x%X\n", (fr & BSC_FR_TXFF));
- + printk("_S_ABORT: RXFE: 0x%X\n", (fr & BSC_FR_RXFE));
- + printk("_S_ABORT: TXBUSY: 0x%X\n", (fr & BSC_FR_TXBUSY));
- + bcm2835_slave_reset(bs);
- + return 0;
- +}
- +
- +static int bcm2835_slave_spi_prepare_message(struct spi_controller *slave,
- + struct spi_message *msg)
- +{
- +
- + uint32_t cr;
- + struct spi_device *spi = msg->spi;
- + struct bcm2835_slave_spi *bs = spi_controller_get_devdata(slave);
- + int err = bcm2835_slave_validate(spi);
- + if (err < 0)
- + return err;
- + cr = bcm2835_slave_read(bs, BSC_CR);
- + cr &= ~(BSC_CR_CPHA | BSC_CR_CPOL);
- +
- + if (spi->mode & SPI_CPOL)
- + cr |= BSC_CR_CPOL;
- + if (spi->mode & SPI_CPHA)
- + cr |= BSC_CR_CPHA;
- + bcm2835_slave_write(bs, BSC_CR, cr);
- + return 0;
- +}
- +
- +
- +static int bcm2835_slave_spi_probe(struct platform_device *pdev)
- +{
- + struct spi_controller *slave;
- + struct bcm2835_slave_spi *bs;
- + struct resource *res;
- + int err;
- + uint32_t cr;
- +
- + slave = spi_alloc_slave(&pdev->dev, sizeof(*bs));
- + if (!slave) {
- + dev_err(&pdev->dev,
- + "spi_alloc_slave() failed (is your kernel compiled with CONFIG_SPI_SLAVE?)\n");
- + return -ENOMEM;
- + }
- + platform_set_drvdata(pdev, slave);
- +
- + bs = spi_controller_get_devdata(slave);
- +
- + slave->dev.of_node = pdev->dev.of_node;
- + slave->mode_bits = BCM2835_BSC_MODE_BITS;
- + slave->bits_per_word_mask = SPI_BPW_MASK(8);
- + slave->setup = bcm2835_slave_spi_setup;
- + slave->set_cs = bcm2835_slave_spi_set_cs;
- + slave->transfer_one = bcm2835_slave_spi_transfer_one;
- + slave->prepare_message = bcm2835_slave_spi_prepare_message;
- + slave->handle_err = bcm2835_slave_spi_handle_err;
- + slave->slave_abort = bcm2835_slave_spi_slave_abort;
- + slave->num_chipselect = 1;
- +
- + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- + bs->regs = devm_ioremap_resource(&pdev->dev, res);
- + if (IS_ERR(bs->regs)) {
- + err = PTR_ERR(bs->regs);
- + goto out_err;
- + }
- +
- + bs->irq = platform_get_irq(pdev, 0);
- + if (bs->irq <= 0) {
- + dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
- + err = bs->irq ? bs->irq : -ENODEV;
- + goto out_err;
- + }
- + cr = BSC_CR_EN|BSC_CR_SPI|BSC_CR_RXE|BSC_CR_TXE;
- + // reset
- + bcm2835_slave_write(bs, BSC_CR, 0);
- + // set the default CR
- + //bcm2835_slave_write(bs, BSC_CR, cr|BSC_CR_TESTFIFO);
- + // Sanity test the slave
- + //if (bcm2835_test(bs, &pdev->dev) < 0) {
- + // err = -EIO;
- + // goto out_err;
- + //}
- + // Clear interrupts and masks
- + bcm2835_slave_write(bs, BSC_IMSC, 0);
- + bcm2835_slave_write(bs, BSC_ICR,
- + BSC_ICR_OEIC | BSC_ICR_BEIC | BSC_ICR_TXIC | BSC_ICR_RXIC);
- + // Reinitialize with TESTFIFO disabled
- + bcm2835_slave_write(bs, BSC_CR, cr);
- + // Reset underrun or overrun errors
- + bcm2835_slave_write(bs, BSC_RSR, 0);
- +
- + uint32_t fr = bcm2835_slave_read(bs, BSC_FR);
- + printk("_INIT: 0x%X\n", fr);
- + printk("_INIT: RXFLEVEL 0x%X\n", (fr & 0xf800) >> 11);
- + printk("_INIT: TXLEVEL 0x%X\n", (fr & 0x7e0) >> 6);
- + printk("_INIT: RXBUSY: 0x%X\n", (fr & BSC_FR_RXBUSY));
- + printk("_INIT: TXFE: 0x%X\n", (fr & BSC_FR_TXFE));
- + printk("_INIT: RXFF: 0x%X\n", (fr & BSC_FR_RXFF));
- + printk("_INIT: TXFF: 0x%X\n", (fr & BSC_FR_TXFF));
- + printk("_INIT: RXFE: 0x%X\n", (fr & BSC_FR_RXFE));
- + printk("_INIT: TXBUSY: 0x%X\n", (fr & BSC_FR_TXBUSY));
- +
- + // Request the SPI BSC IRQ
- + err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_slave_spi_interrupt,
- + IRQF_SHARED,
- + dev_name(&pdev->dev), slave);
- + if (err) {
- + dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
- + goto out_err;
- + }
- +
- + err = devm_spi_register_controller(&pdev->dev, slave);
- + if (err) {
- + dev_err(&pdev->dev, "could not register SPI slave: %d\n", err);
- + goto out_err;
- + }
- +
- + return 0;
- +
- +out_err:
- + spi_controller_put(slave);
- + return err;
- +}
- +
- +static int bcm2835_slave_spi_remove(struct platform_device *pdev)
- +{
- + //struct spi_controller *slave = platform_get_drvdata(pdev);
- + //struct bcm2835_slave_spi *bs = spi_controller_get_devdata(master);
- + //TODO
- + return 0;
- +}
- +
- +static const struct of_device_id bcm2835_slave_spi_match[] = {
- + { .compatible = "brcm,bcm2835-spi-slave", },
- + {}
- +};
- +
- +MODULE_DEVICE_TABLE(of, bcm2835_slave_spi_match);
- +
- +static struct platform_driver bcm2835_slave_spi_driver = {
- + .driver = {
- + .name = DRV_NAME,
- + .of_match_table = bcm2835_slave_spi_match,
- + },
- + .probe = bcm2835_slave_spi_probe,
- + .remove = bcm2835_slave_spi_remove,
- +};
- +module_platform_driver(bcm2835_slave_spi_driver);
- +
- +MODULE_DESCRIPTION("SPI slave controller driver for Broadcom BCM2835 BSC");
- +MODULE_AUTHOR("Jacko Dirks <[email protected]>");
- +MODULE_LICENSE("GPL v2");
- --
- 2.25.0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement