Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/23778
Change subject: soc/cavium: Move SPI to common folder ......................................................................
soc/cavium: Move SPI to common folder
Move SPI code to common folder and implement missing functionality, like setting clk frequency, CS pin, CS level, ...
Change-Id: Ie12b15a7a8594a54099e40e6f8460ff6760da6c6 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/mainboard/cavium/cn8100_sff_evb/bootblock.c M src/soc/cavium/cn81xx/Makefile.inc D src/soc/cavium/cn81xx/include/soc/spi.h D src/soc/cavium/cn81xx/spi.c M src/soc/cavium/common/Makefile.inc A src/soc/cavium/common/include/soc/spi.h A src/soc/cavium/common/spi.c 7 files changed, 451 insertions(+), 296 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/78/23778/1
diff --git a/src/mainboard/cavium/cn8100_sff_evb/bootblock.c b/src/mainboard/cavium/cn8100_sff_evb/bootblock.c index f1e3d60..977a295 100644 --- a/src/mainboard/cavium/cn8100_sff_evb/bootblock.c +++ b/src/mainboard/cavium/cn8100_sff_evb/bootblock.c @@ -42,7 +42,13 @@
static void configure_spi_flash(void) { - cavium_spi_init(0); /* FIXME: add speed argument? */ + spi_init_custom(0, + 25000000, + 0, + 0, + 0, + 0, + 1); }
static void configure_ec(void) @@ -57,13 +63,8 @@
void bootblock_mainboard_init(void) { - struct spi_slave slave; - printk(BIOS_DEBUG, "hello from %s, addr: %p\n", __func__, &bootblock_mainboard_init); - printk(BIOS_DEBUG, "%s: spi_ctrlr_bus_map_count: %zu, &spi_ctrlr_bus_map_count: %p\n", - __func__, spi_ctrlr_bus_map_count, &spi_ctrlr_bus_map_count); - slave.ctrlr = spi_ctrlr_bus_map[0].ctrlr; - printk(BIOS_DEBUG, "%s: slave.ctrlr: %p, slave.ctrlr->xfer: %p\n", - __func__, slave.ctrlr, slave.ctrlr->xfer); + printk(BIOS_DEBUG, "hello from %s, addr: %p\n", __func__, + &bootblock_mainboard_init);
configure_spi_flash(); configure_ec(); diff --git a/src/soc/cavium/cn81xx/Makefile.inc b/src/soc/cavium/cn81xx/Makefile.inc index c16c378..1ac9f69 100644 --- a/src/soc/cavium/cn81xx/Makefile.inc +++ b/src/soc/cavium/cn81xx/Makefile.inc @@ -19,7 +19,6 @@ bootblock-$(CONFIG_BOOTBLOCK_CUSTOM) += bootblock_custom.S
#bootblock-y += ../common/i2c.c -bootblock-y += spi.c ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) bootblock-$(CONFIG_DRIVERS_UART) += uart.c endif @@ -70,14 +69,12 @@ #verstage-y += gpio.c #verstage-y += sdram.c #verstage-y += ../common/i2c.c -#verstage-y += spi.c #verstage-$(CONFIG_DRIVERS_UART) += uart.c
################################################################################
romstage-y += ../common/cbmem.c -romstage-y += spi.c romstage-$(CONFIG_DRIVERS_UART) += uart.c romstage-y += mmu_operations.c #romstage-y += ../common/pwm.c @@ -88,7 +85,6 @@
ramstage-y += ../common/cbmem.c ramstage-y += sdram.c -ramstage-y += spi.c ramstage-$(CONFIG_DRIVERS_UART) += uart.c #ramstage-y += ../common/gpio.c #ramstage-y += gpio.c diff --git a/src/soc/cavium/cn81xx/include/soc/spi.h b/src/soc/cavium/cn81xx/include/soc/spi.h deleted file mode 100644 index 2232205..0000000 --- a/src/soc/cavium/cn81xx/include/soc/spi.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright 2017-present Facebook, Inc. - * - * 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; version 2 of the License. - * - * 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. - */ - -#ifndef __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H -#define __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H - -/* This driver serves as a CBFS media source. */ -#include <spi-generic.h> -#include <stddef.h> - -struct cavium_spi { - u64 cfg; - u64 sts; - u64 tx; - u64 rsvd1; - u64 sts_w1s; - u64 rsvd2; - u64 int_ena_w1c; - u64 int_ena_w1s; - u64 wide_dat; - u8 rsvd4[0x38]; - u64 dat[8]; -}; -check_member(cavium_spi, dat[7], 0xb8); - -#define SF_READ_DATA_CMD 0x3 - -/* MPI_CFG */ -#define MPI_CFG_EN (1 << 0) -#define MPI_CFG_IDLELO (1 << 1) -#define MPI_CFG_CLK_CONT (1 << 2) -#define MPI_CFG_WIREOR (1 << 3) -#define MPI_CFG_LSBFIRST (1 << 4) -#define MPI_CFG_CSHI (1 << 7) -#define MPI_CFG_IDLECLKS_SHIFT 8 -#define MPI_CFG_IDLECLKS_MASK 0x2 -#define MPI_CFG_TRITX (1 << 10) -#define MPI_CFG_CSLATE (1 << 11) -#define MPI_CFG_CSENA0 (1 << 12) -#define MPI_CFG_CSENA1 (1 << 13) -#define MPI_CFG_CSENA2 (1 << 14) -#define MPI_CFG_CSENA3 (1 << 15) -#define MPI_CFG_CLKDIV_SHIFT 16 -#define MPI_CFG_CLKDIV_MASK 0x1fff - -/* MPI_STS */ -#define MPI_STS_BUSY (1 << 0) -#define MPI_STS_MPI_INTR (1 << 1) -#define MPI_STS_RXNUM_SHIFT 8 -#define MPI_STS_RXNUM_MASK 0x1f - -/* MPI_TX */ -#define MPI_TX_TOTNUM_SHIFT 0 -#define MPI_TX_TOTNUM_MASK 0x1f -#define MPI_TX_TXNUM_SHIFT 8 -#define MPI_TX_TXNUM_MASK 0x1f -#define MPI_TX_LEAVECS (1 << 16) -#define MPI_TX_CSID_SHIFT 20 -#define MPI_TX_CSID_MASK 0x3 - -/* MPI_STS_W1S */ -#define MPI_STS_W1S_MPI_INTR (1 << 1) - -/* MPI_INT_ENA_W1C */ -#define MPI_INT_ENA_W1C (1 << 1) - -/* MPI_INT_ENA_W1S */ -#define MPI_INT_ENA_W1S (1 << 1) - -void cavium_spi_init(unsigned int speed_hz); - -#endif /* ! __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H */ diff --git a/src/soc/cavium/cn81xx/spi.c b/src/soc/cavium/cn81xx/spi.c deleted file mode 100644 index 43cfe69..0000000 --- a/src/soc/cavium/cn81xx/spi.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright 2014 Rockchip Inc. - * Copyright 2017-present Facebook, Inc. - * - * 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; version 2 of the License. - * - * 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. - */ - -#include <arch/io.h> -#include <assert.h> -#include <console/console.h> -#include <delay.h> -#include <endian.h> -#include <soc/addressmap.h> -#include <soc/spi.h> -#include <soc/clock.h> -#include <spi-generic.h> -#include <spi_flash.h> -#include <stdlib.h> -#include <timer.h> - -struct cavium_spi_slave { - struct cavium_spi *regs; - int cs; -}; - -#define SPI_TIMEOUT_US 5000 - -static struct cavium_spi_slave cavium_spi_slaves[] = { - { - .regs = (struct cavium_spi *)MPI_PF_BAR0, - .cs = 0, - }, -}; - -static struct cavium_spi_slave *to_cavium_spi(const struct spi_slave *slave) -{ - assert(slave->bus < ARRAY_SIZE(cavium_spi_slaves)); - return &cavium_spi_slaves[slave->bus]; -} - -void cavium_spi_init(unsigned int speed_hz) -{ - /* FIXME(dhendrix): The defaults are sane. We may wish to speed up the - clock eventually, but for now leave it as-is. */ -#if 0 - struct cavium_spi *spi = (struct cavium_spi *)MPI_PF_BAR0; - u64 cfg; - - cfg = read64(&spi->cfg); -#endif - return; -} - -#if 0 -static void spi_cs_activate(const struct spi_slave *slave) -{ - struct cavium_spi *regs = to_cavium_spi(slave)->regs; - - clrsetbits_le64(®s->tx, - MPI_TX_CSID_MASK << MPI_TX_CSID_SHIFT, - slave->cs << MPI_TX_CSID_SHIFT); -} - -static void spi_cs_deactivate(const struct spi_slave *slave) -{ - struct cavium_spi *regs = to_cavium_spi(slave)->regs; - - clrbits_le64(®s->tx, MPI_TX_CSID_MASK << MPI_TX_CSID_SHIFT); -} -#endif - -#if 0 -static void cavium_spi_set_clk(struct cavium_spi *regs, unsigned int hz) -{ - /* FIXME: stub */ - printk(BIOS_ERR, "%s: Not implemented\n", __func__); -} -#endif - -static int cavium_spi_wait(struct cavium_spi *regs) -{ - struct stopwatch sw; - - stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_US); - do { - if (!(read64(®s->sts) & MPI_STS_BUSY)) - return 0; - } while (!stopwatch_expired(&sw)); - printk(BIOS_DEBUG, "Cavium SPI: Timed out after %uus\n", SPI_TIMEOUT_US); - return -1; -} - -static int do_xfer(const struct spi_slave *slave, struct spi_op *vector, - int leavecs) -{ - struct cavium_spi *regs = to_cavium_spi(slave)->regs; - uint8_t *out_buf = (uint8_t *)vector->dout; - size_t bytesout = vector->bytesout; - uint8_t *in_buf = (uint8_t *)vector->din; - size_t bytesin = vector->bytesin; - - /* - * The CN81xx SPI controller is half-duplex and has 8 data registers. - * If >8 bytes remain in the transfer then we must set LEAVECS = 1 so - * that the /CS remains asserted. Once <=8 bytes remain we must set - * LEAVECS = 0 so that /CS is de-asserted, thus completing the transfer. - */ - while (bytesout) { - size_t out_now = MIN(bytesout, 8); - unsigned int i; - u64 regval = 0; - - for (i = 0; i < out_now; i++) - write64(®s->dat[i], out_buf[i] & 0xff); - - regval |= to_cavium_spi(slave)->cs << MPI_TX_CSID_SHIFT; - if (leavecs || ((bytesout > 8) || bytesin)) - regval |= MPI_TX_LEAVECS; - /* number of bytes to transmit goes in both TXNUM and TOTNUM */ - regval |= (out_now << MPI_TX_TXNUM_SHIFT); - regval |= (out_now << MPI_TX_TOTNUM_SHIFT); - write64(®s->tx, regval); - - /* check status */ - if (cavium_spi_wait(regs) < 0) - return -1; - - bytesout -= out_now; - out_buf += out_now; - } - - while (bytesin) { - size_t in_now = MIN(bytesin, 8); - unsigned int i, xferred = 0; - u64 regval = 0; - - regval |= to_cavium_spi(slave)->cs << MPI_TX_CSID_SHIFT; - if (leavecs || (bytesin > 8)) - regval |= MPI_TX_LEAVECS; - regval |= (in_now << MPI_TX_TOTNUM_SHIFT); - write64(®s->tx, regval); - - /* check status */ - if (cavium_spi_wait(regs) < 0) - return -1; - - regval = read64(®s->sts); - xferred = (regval >> MPI_STS_RXNUM_SHIFT) & MPI_STS_RXNUM_MASK; - if (xferred != in_now) { - printk(BIOS_ERR, "Incorrect number of bytes received: " - "%u.\n", xferred); - return -1; - } - - for (i = 0; i < in_now; i++) { - *in_buf = (uint8_t)((read64(®s->dat[i]) & 0xff)); - in_buf++; - } - bytesin -= in_now; - } - - return 0; -} - -static int spi_ctrlr_xfer_vector(const struct spi_slave *slave, - struct spi_op vectors[], size_t count) -{ - int i; - - for (i = 0; i < count; i++) { - if (do_xfer(slave, &vectors[i], count - 1 == i ? 0 : 1)) { - printk(BIOS_ERR, "Failed to transfer %zu vectors.\n", count); - return -1; - } - } - - return 0; -} -static const struct spi_ctrlr spi_ctrlr = { - .xfer_vector = spi_ctrlr_xfer_vector, - .max_xfer_size = 8, /* more with LEAVECS bit enabled */ -}; - -const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { - { - .ctrlr = &spi_ctrlr, - .bus_start = 0, - .bus_end = ARRAY_SIZE(cavium_spi_slaves) - 1, - }, -}; -const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map); diff --git a/src/soc/cavium/common/Makefile.inc b/src/soc/cavium/common/Makefile.inc index 83e81a7..fce377c 100644 --- a/src/soc/cavium/common/Makefile.inc +++ b/src/soc/cavium/common/Makefile.inc @@ -20,6 +20,7 @@ bootblock-y += clock.c bootblock-y += gpio.c bootblock-y += timer.c +bootblock-y += spi.c
@@ -27,6 +28,7 @@ romstage-y += clock.c romstage-y += gpio.c romstage-y += timer.c +romstage-y += spi.c
@@ -34,6 +36,7 @@ ramstage-y += clock.c ramstage-y += gpio.c ramstage-y += timer.c +ramstage-y += spi.c
CPPFLAGS_common += -Isrc/soc/cavium/common/include
diff --git a/src/soc/cavium/common/include/soc/spi.h b/src/soc/cavium/common/include/soc/spi.h new file mode 100644 index 0000000..bb69daa --- /dev/null +++ b/src/soc/cavium/common/include/soc/spi.h @@ -0,0 +1,40 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017-present Facebook, Inc. + * + * 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; version 2 of the License. + * + * 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. + */ + +#ifndef __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H +#define __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H + +/* This driver serves as a CBFS media source. */ +#include <spi-generic.h> +#include <stddef.h> + +void spi_enable(const size_t bus); +void spi_disable(const size_t bus); +void spi_set_cs(const size_t bus, + const size_t chip_select, + const size_t assert_is_low); +void spi_set_clock(const size_t bus, + const size_t speed_hz, + const size_t idle_low, + const size_t idle_cycles); +void spi_set_lsbmsb(const size_t bus, const size_t lsb_first); +void spi_init_custom(const size_t bus, + const size_t speed_hz, + const size_t idle_low, + const size_t idle_cycles, + const size_t lsb_first, + const size_t chip_select, + const size_t assert_is_low); +#endif /* ! __COREBOOT_SRC_SOC_CN81XX_INCLUDE_SOC_SPI_H */ diff --git a/src/soc/cavium/common/spi.c b/src/soc/cavium/common/spi.c new file mode 100644 index 0000000..5b9985c --- /dev/null +++ b/src/soc/cavium/common/spi.c @@ -0,0 +1,399 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * Copyright 2017-present Facebook, Inc. + * + * 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; version 2 of the License. + * + * 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. + */ + +#include <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <delay.h> +#include <endian.h> +#include <soc/addressmap.h> +#include <soc/spi.h> +#include <soc/clock.h> +#include <spi-generic.h> +#include <spi_flash.h> +#include <stdlib.h> +#include <timer.h> + +union cavium_spi_cfg { + u64 u; + struct { + u64 enable : 1; + u64 idlelow : 1; + u64 clk_cont : 1; + u64 wireor : 1; + u64 lsbfirst : 1; + u64 : 2; + u64 cshi : 1; + u64 idleclks : 2; + u64 tristate : 1; + u64 cslate : 1; + u64 csena : 4; /* Must be one */ + u64 clkdiv : 13; + u64 : 35; + } s; +}; + +union cavium_spi_sts { + u64 u; + struct { + u64 busy : 1; + u64 mpi_intr : 1; + u64 : 6; + u64 rxnum : 5; + u64 : 51; + } s; +}; + +union cavium_spi_tx { + u64 u; + struct { + u64 totnum : 5; + u64 : 3; + u64 txnum : 5; + u64 : 3; + u64 leavecs : 1; + u64 : 3; + u64 csid : 2; + u64 : 42; + } s; +}; + +struct cavium_spi { + union cavium_spi_cfg cfg; + union cavium_spi_sts sts; + union cavium_spi_tx tx; + u64 rsvd1; + u64 sts_w1s; + u64 rsvd2; + u64 int_ena_w1c; + u64 int_ena_w1s; + u64 wide_dat; + u8 rsvd4[0x38]; + u64 dat[8]; +}; + +check_member(cavium_spi, cfg, 0); +check_member(cavium_spi, sts, 0x8); +check_member(cavium_spi, tx, 0x10); +check_member(cavium_spi, dat[7], 0xb8); + +struct cavium_spi_slave { + struct cavium_spi *regs; + int cs; +}; + +#define SPI_TIMEOUT_US 5000 + +static struct cavium_spi_slave cavium_spi_slaves[] = { + { + .regs = (struct cavium_spi *)MPI_PF_BAR0, + .cs = 0, + }, +}; + +static struct cavium_spi_slave *to_cavium_spi(const struct spi_slave *slave) +{ + assert(slave->bus < ARRAY_SIZE(cavium_spi_slaves)); + return &cavium_spi_slaves[slave->bus]; +} + +/** + * Enable the SPI controller. Pins are driven. + * + * @param bus The SPI bus to operate on + */ +void spi_enable(const size_t bus) +{ + union cavium_spi_cfg cfg; + + assert(bus < ARRAY_SIZE(cavium_spi_slaves)); + if (bus >= ARRAY_SIZE(cavium_spi_slaves)) + return; + + struct cavium_spi *regs = cavium_spi_slaves[bus].regs; + + cfg.u = read64(®s->cfg); + cfg.s.csena = 0xf; + cfg.s.enable = 1; + write64(®s->cfg, cfg.u); +} + +/** + * Disable the SPI controller. Pins are tristated. + * + * @param bus The SPI bus to operate on + */ +void spi_disable(const size_t bus) +{ + union cavium_spi_cfg cfg; + + assert(bus < ARRAY_SIZE(cavium_spi_slaves)); + if (bus >= ARRAY_SIZE(cavium_spi_slaves)) + return; + + struct cavium_spi *regs = cavium_spi_slaves[bus].regs; + + cfg.u = read64(®s->cfg); + cfg.s.csena = 0xf; + cfg.s.enable = 0; + write64(®s->cfg, cfg.u); +} + +/** + * Set SPI Chip select line and level if asserted. + * + * @param bus The SPI bus to operate on + * @param chip_select The chip select pin to use (0 - 3) + * @param assert_is_low CS pin state is low when asserted + */ +void spi_set_cs(const size_t bus, + const size_t chip_select, + const size_t assert_is_low) +{ + union cavium_spi_cfg cfg; + + assert(bus < ARRAY_SIZE(cavium_spi_slaves)); + if (bus >= ARRAY_SIZE(cavium_spi_slaves)) + return; + + cavium_spi_slaves[bus].cs = chip_select & 0x3; + struct cavium_spi *regs = cavium_spi_slaves[bus].regs; + + cfg.u = read64(®s->cfg); + cfg.s.csena = 0xf; + cfg.s.cshi = !assert_is_low; + write64(®s->cfg, cfg.u); + + //FIXME: CS2/3: Change pin mux here +} + +/** + * Set SPI clock frequency. + * + * @param bus The SPI bus to operate on + * @param speed_hz The SPI frequency in Hz + * @param idle_low The SPI clock idles low + * @param idle_cycles Number of CLK cycles between two commands (0 - 3) + + */ +void spi_set_clock(const size_t bus, + const size_t speed_hz, + const size_t idle_low, + const size_t idle_cycles) +{ + union cavium_spi_cfg cfg; + + assert(bus < ARRAY_SIZE(cavium_spi_slaves)); + if (bus >= ARRAY_SIZE(cavium_spi_slaves)) + return; + + struct cavium_spi *regs = cavium_spi_slaves[bus].regs; + const uint64_t sclk = thunderx_get_io_clock(); + + cfg.u = read64(®s->cfg); + cfg.s.csena = 0xf; + cfg.s.clk_cont = 0; + cfg.s.idlelow = !!idle_low; + cfg.s.idleclks = idle_cycles & 0x3; + cfg.s.clkdiv = MIN(sclk / (2ULL * speed_hz), 0x1fff); + write64(®s->cfg, cfg.u); + + printk(BIOS_DEBUG, "SPI: set clock to %ld kHz\n", + (sclk / (2ULL * cfg.s.clkdiv)) >> 10); +} + +/** + * Set SPI LSB/MSB first. + * + * @param bus The SPI bus to operate on + * @param lsb_first The SPI operates LSB first + * + */ +void spi_set_lsbmsb(const size_t bus, const size_t lsb_first) +{ + union cavium_spi_cfg cfg; + + assert(bus < ARRAY_SIZE(cavium_spi_slaves)); + if (bus >= ARRAY_SIZE(cavium_spi_slaves)) + return; + + struct cavium_spi *regs = cavium_spi_slaves[bus].regs; + + cfg.u = read64(®s->cfg); + cfg.s.csena = 0xf; + cfg.s.lsbfirst = !!lsb_first; + write64(®s->cfg, cfg.u); +} + +/** + * Init SPI with custom parameters and enable SPI controller. + * + * @param bus The SPI bus to operate on + * @param speed_hz The SPI frequency in Hz + * @param idle_low The SPI clock idles low + * @param idle_cycles Number of CLK cycles between two commands (0 - 3) + * @param lsb_first The SPI operates LSB first + * @param chip_select The chip select pin to use (0 - 3) + * @param assert_is_low CS pin state is low when asserted + */ +void spi_init_custom(const size_t bus, + const size_t speed_hz, + const size_t idle_low, + const size_t idle_cycles, + const size_t lsb_first, + const size_t chip_select, + const size_t assert_is_low) +{ + spi_disable(bus); + spi_set_clock(bus, speed_hz, idle_low, idle_cycles); + spi_set_lsbmsb(bus, lsb_first); + spi_set_cs(bus, chip_select, assert_is_low); + spi_enable(bus); +} + +/** + * Init all SPI controllers with default values and enable all SPI controller. + * + */ +void spi_init(void) +{ + for (size_t i = 0; i < ARRAY_SIZE(cavium_spi_slaves); i++) { + spi_disable(i); + spi_set_clock(i, 12500000, 0, 0); + spi_set_lsbmsb(i, 0); + spi_set_cs(i, 0, 1); + spi_enable(i); + } +} + +static int cavium_spi_wait(struct cavium_spi *regs) +{ + struct stopwatch sw; + union cavium_spi_sts sts; + + stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_US); + do { + sts.u = read64(®s->sts); + if (!sts.s.busy) + return 0; + } while (!stopwatch_expired(&sw)); + printk(BIOS_DEBUG, "SPI: Timed out after %uus\n", SPI_TIMEOUT_US); + return -1; +} + +static int do_xfer(const struct spi_slave *slave, struct spi_op *vector, + int leavecs) +{ + struct cavium_spi *regs = to_cavium_spi(slave)->regs; + uint8_t *out_buf = (uint8_t *)vector->dout; + size_t bytesout = vector->bytesout; + uint8_t *in_buf = (uint8_t *)vector->din; + size_t bytesin = vector->bytesin; + union cavium_spi_sts sts; + union cavium_spi_tx tx; + + /** + * The CN81xx SPI controller is half-duplex and has 8 data registers. + * If >8 bytes remain in the transfer then we must set LEAVECS = 1 so + * that the /CS remains asserted. Once <=8 bytes remain we must set + * LEAVECS = 0 so that /CS is de-asserted, thus completing the transfer. + */ + while (bytesout) { + size_t out_now = MIN(bytesout, 8); + unsigned int i; + + for (i = 0; i < out_now; i++) + write64(®s->dat[i], out_buf[i] & 0xff); + + tx.u = 0; + tx.s.csid = to_cavium_spi(slave)->cs; + if (leavecs || ((bytesout > 8) || bytesin)) + tx.s.leavecs = 1; + /* number of bytes to transmit goes in both TXNUM and TOTNUM */ + tx.s.totnum = out_now; + tx.s.txnum = out_now; + write64(®s->tx, tx.u); + + /* check status */ + if (cavium_spi_wait(regs) < 0) + return -1; + + bytesout -= out_now; + out_buf += out_now; + } + + while (bytesin) { + size_t in_now = MIN(bytesin, 8); + unsigned int i; + + tx.u = 0; + tx.s.csid = to_cavium_spi(slave)->cs; + if (leavecs || (bytesin > 8)) + tx.s.leavecs = 1; + tx.s.totnum = in_now; + write64(®s->tx, tx.u); + + /* check status */ + if (cavium_spi_wait(regs) < 0) + return -1; + + sts.u = read64(®s->sts); + if (sts.s.rxnum != in_now) { + printk(BIOS_ERR, + "SPI: Incorrect number of bytes received: %u.\n", + sts.s.rxnum); + return -1; + } + + for (i = 0; i < in_now; i++) { + *in_buf = (uint8_t)((read64(®s->dat[i]) & 0xff)); + in_buf++; + } + bytesin -= in_now; + } + + return 0; +} + +static int spi_ctrlr_xfer_vector(const struct spi_slave *slave, + struct spi_op vectors[], size_t count) +{ + int i; + + for (i = 0; i < count; i++) { + if (do_xfer(slave, &vectors[i], count - 1 == i ? 0 : 1)) { + printk(BIOS_ERR, + "SPI: Failed to transfer %zu vectors.\n", count); + return -1; + } + } + + return 0; +} +static const struct spi_ctrlr spi_ctrlr = { + + .xfer_vector = spi_ctrlr_xfer_vector, + .max_xfer_size = SPI_CTRLR_DEFAULT_MAX_XFER_SIZE, +}; + +const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { + { + .ctrlr = &spi_ctrlr, + .bus_start = 0, + .bus_end = ARRAY_SIZE(cavium_spi_slaves) - 1, + }, +}; +const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);