- Add xtxtech spi-nor chip parts (Bruce Suen)
- Add bcm63xx-hsspi driver fixes (William Zhang)
This commit is contained in:
Tom Rini 2023-07-13 20:39:10 -04:00
commit cef3675509
12 changed files with 931 additions and 74 deletions

View file

@ -0,0 +1,134 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/brcm,bcm63xx-hsspi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Broadband SoC High Speed SPI controller
maintainers:
- William Zhang <william.zhang@broadcom.com>
- Kursad Oney <kursad.oney@broadcom.com>
- Jonas Gorski <jonas.gorski@gmail.com>
description: |
Broadcom Broadband SoC supports High Speed SPI master controller since the
early MIPS based chips such as BCM6328 and BCM63268. This initial rev 1.0
controller was carried over to recent ARM based chips, such as BCM63138,
BCM4908 and BCM6858. The old MIPS based chip should continue to use the
brcm,bcm6328-hsspi compatible string. The recent ARM based chip is required to
use the brcm,bcmbca-hsspi-v1.0 as part of its compatible string list as
defined below to match the specific chip along with ip revision info.
This rev 1.0 controller has a limitation that can not keep the chip select line
active between the SPI transfers within the same SPI message. This can
terminate the transaction to some SPI devices prematurely. The issue can be
worked around by either the controller's prepend mode or using the dummy chip
select workaround. Driver automatically picks the suitable mode based on
transfer type so it is transparent to the user.
The newer SoCs such as BCM6756, BCM4912 and BCM6855 include an updated SPI
controller rev 1.1 that add the capability to allow the driver to control chip
select explicitly. This solves the issue in the old controller.
properties:
compatible:
oneOf:
- const: brcm,bcm6328-hsspi
- items:
- enum:
- brcm,bcm47622-hsspi
- brcm,bcm4908-hsspi
- brcm,bcm63138-hsspi
- brcm,bcm63146-hsspi
- brcm,bcm63148-hsspi
- brcm,bcm63158-hsspi
- brcm,bcm63178-hsspi
- brcm,bcm6846-hsspi
- brcm,bcm6856-hsspi
- brcm,bcm6858-hsspi
- brcm,bcm6878-hsspi
- const: brcm,bcmbca-hsspi-v1.0
- items:
- enum:
- brcm,bcm4912-hsspi
- brcm,bcm6756-hsspi
- brcm,bcm6813-hsspi
- brcm,bcm6855-hsspi
- const: brcm,bcmbca-hsspi-v1.1
reg:
items:
- description: main registers
- description: miscellaneous control registers
minItems: 1
reg-names:
items:
- const: hsspi
- const: spim-ctrl
minItems: 1
clocks:
items:
- description: SPI master reference clock
- description: SPI master pll clock
clock-names:
items:
- const: hsspi
- const: pll
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- brcm,bcm6328-hsspi
- brcm,bcmbca-hsspi-v1.0
then:
properties:
reg:
maxItems: 1
reg-names:
maxItems: 1
else:
properties:
reg:
minItems: 2
maxItems: 2
reg-names:
minItems: 2
maxItems: 2
required:
- reg-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
spi@ff801000 {
compatible = "brcm,bcm6756-hsspi", "brcm,bcmbca-hsspi-v1.1";
reg = <0xff801000 0x1000>,
<0xff802610 0x4>;
reg-names = "hsspi", "spim-ctrl";
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&hsspi>, <&hsspi_pll>;
clock-names = "hsspi", "pll";
num-cs = <8>;
#address-cells = <1>;
#size-cells = <0>;
};

View file

@ -9,10 +9,10 @@ The soft SPI node requires the following properties:
Mandatory properties:
compatible: "spi-gpio"
cs-gpios: GPIOs to use for SPI chip select (output)
gpio-sck: GPIO to use for SPI clock (output)
sck-gpios: GPIO to use for SPI clock (output)
And at least one of:
gpio-mosi: GPIO to use for SPI MOSI line (output)
gpio-miso: GPIO to use for SPI MISO line (input)
mosi-gpios: GPIO to use for SPI MOSI line (output)
miso-gpios: GPIO to use for SPI MISO line (input)
Optional propertie:
spi-delay-us: Number of microseconds of delay between each CS transition
@ -27,9 +27,9 @@ Example:
soft-spi {
compatible = "spi-gpio";
cs-gpios = <&gpio 235 0>; /* Y43 */
gpio-sck = <&gpio 225 0>; /* Y31 */
gpio-mosi = <&gpio 227 0>; /* Y33 */
gpio-miso = <&gpio 224 0>; /* Y30 */
sck-gpios = <&gpio 225 0>; /* Y31 */
mosi-gpios = <&gpio 227 0>; /* Y33 */
miso-gpios = <&gpio 224 0>; /* Y30 */
spi-delay-us = <1>;
#address-cells = <1>;
#size-cells = <0>;

View file

@ -446,6 +446,11 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q256jwm", 0xef8019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{ INFO("w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{
INFO("w25q64dw", 0xef6017, 0, 64 * 1024, 128,
@ -528,8 +533,42 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
#endif
#ifdef CONFIG_SPI_FLASH_XTX
/* XTX Technology (Shenzhen) Limited */
{ INFO("xt25f128b", 0x0b4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
/* XTX Technology Limited */
/* adding these 3V QSPI flash parts */
{ INFO("xt25f08", 0x0b4014, 0, 64 * 1024, 16,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25f16", 0x0b4015, 0, 64 * 1024, 32,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25f32", 0x0b4016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25f64", 0x0b4017, 0, 64 * 1024, 128,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25f128", 0x0b4018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25f256", 0x0b4019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
/* adding these 1.8V QSPI flash parts */
{ INFO("xt25q08", 0x0b6014, 0, 64 * 1024, 16,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25q16", 0x0b6015, 0, 64 * 1024, 32,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25q32", 0x0b6016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25q64", 0x0b6017, 0, 64 * 1024, 128,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25q128", 0x0b6018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("xt25q256", 0x0b6019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("xt25q512", 0x0b601A, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("xt25q01g", 0x0b601B, 0, 64 * 1024, 2048,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
/* adding these wide voltage QSPI flash parts */
{ INFO("xt25w512", 0x0b651A, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("xt25w01g", 0x0b651B, 0, 64 * 1024, 2048,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
#endif
{ },
};

View file

@ -101,12 +101,21 @@ config ATMEL_SPI
config BCM63XX_HSSPI
bool "BCM63XX HSSPI driver"
depends on (ARCH_BMIPS || BCM6856 || BCM6858 || BCM63158)
depends on (ARCH_BMIPS || ARCH_BCMBCA)
help
Enable the BCM6328 HSSPI driver. This driver can be used to
Enable the BCM63XX HSSPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Broadcom
SPI core.
config BCMBCA_HSSPI
bool "BCMBCA HSSPI driver"
depends on ARCH_BCMBCA && HAVE_SPI_CS_CTRL
help
This enables support for the High Speed SPI controller present on
newer Broadcom BCMBCA SoCs. These SoCs include an updated SPI controller
that adds the capability to allow the driver to control chip select
explicitly.
config BCM63XX_SPI
bool "BCM6348 SPI driver"
depends on ARCH_BMIPS

View file

@ -25,6 +25,7 @@ obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o
obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o
obj-$(CONFIG_BCMBCA_HSSPI) += bcmbca_hsspi.o
obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
obj-$(CONFIG_CF_SPI) += cf_spi.o

View file

@ -20,7 +20,13 @@
#define HSSPI_PP 0
#define SPI_MAX_SYNC_CLOCK 30000000
/*
* The maximum frequency for SPI synchronous mode is 30MHz for some chips and
* 25MHz for some others. This depends on the chip layout and SPI signals
* distance to the pad. We use the lower of these values to cover all relevant
* chips.
*/
#define SPI_MAX_SYNC_CLOCK 25000000
/* SPI Control register */
#define SPI_CTL_REG 0x000
@ -72,12 +78,16 @@
#define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08)
#define SPI_PFL_MODE_FILL_SHIFT 0
#define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT)
#define SPI_PFL_MODE_MDRDST_SHIFT 8
#define SPI_PFL_MODE_MDWRST_SHIFT 12
#define SPI_PFL_MODE_MDRDSZ_SHIFT 16
#define SPI_PFL_MODE_MDRDSZ_MASK (1 << SPI_PFL_MODE_MDRDSZ_SHIFT)
#define SPI_PFL_MODE_MDWRSZ_SHIFT 18
#define SPI_PFL_MODE_MDWRSZ_MASK (1 << SPI_PFL_MODE_MDWRSZ_SHIFT)
#define SPI_PFL_MODE_3WIRE_SHIFT 20
#define SPI_PFL_MODE_3WIRE_MASK (1 << SPI_PFL_MODE_3WIRE_SHIFT)
#define SPI_PFL_MODE_PREPCNT_SHIFT 24
#define SPI_PFL_MODE_PREPCNT_MASK (4 << SPI_PFL_MODE_PREPCNT_SHIFT)
/* SPI Ping-Pong FIFO registers */
#define HSSPI_FIFO_SIZE 0x200
@ -96,12 +106,21 @@
#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE)
#define HSSPI_MAX_PREPEND_SIZE 15
#define HSSPI_XFER_MODE_PREPEND 0
#define HSSPI_XFER_MODE_DUMMYCS 1
struct bcm63xx_hsspi_priv {
void __iomem *regs;
ulong clk_rate;
uint8_t num_cs;
uint8_t cs_pols;
uint speed;
uint xfer_mode;
uint32_t prepend_cnt;
uint8_t prepend_buf[HSSPI_MAX_PREPEND_SIZE];
};
static int bcm63xx_hsspi_cs_info(struct udevice *bus, uint cs,
@ -143,9 +162,16 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
struct dm_spi_slave_plat *plat)
{
uint32_t clr, set;
uint speed = priv->speed;
if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS &&
speed > SPI_MAX_SYNC_CLOCK) {
speed = SPI_MAX_SYNC_CLOCK;
debug("Force to dummy cs mode. Reduce the speed to %dHz\n", speed);
}
/* profile clock */
set = DIV_ROUND_UP(priv->clk_rate, priv->speed);
set = DIV_ROUND_UP(priv->clk_rate, speed);
set = DIV_ROUND_UP(2048, set);
set &= SPI_PFL_CLK_FREQ_MASK;
set |= SPI_PFL_CLK_RSTLOOP_MASK;
@ -164,7 +190,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
set |= SPI_PFL_SIG_LATCHRIS_MASK;
/* async clk */
if (priv->speed > SPI_MAX_SYNC_CLOCK)
if (speed > SPI_MAX_SYNC_CLOCK)
set |= SPI_PFL_SIG_ASYNCIN_MASK;
clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
@ -173,17 +199,24 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
set = 0;
clr = 0;
/* invert cs polarity */
if (priv->cs_pols & BIT(plat->cs))
clr |= BIT(plat->cs);
else
set |= BIT(plat->cs);
if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
if (priv->cs_pols & BIT(plat->cs))
set |= BIT(plat->cs);
else
clr |= BIT(plat->cs);
} else {
/* invert cs polarity */
if (priv->cs_pols & BIT(plat->cs))
clr |= BIT(plat->cs);
else
set |= BIT(plat->cs);
/* invert dummy cs polarity */
if (priv->cs_pols & BIT(!plat->cs))
clr |= BIT(!plat->cs);
else
set |= BIT(!plat->cs);
/* invert dummy cs polarity */
if (priv->cs_pols & BIT(!plat->cs))
clr |= BIT(!plat->cs);
else
set |= BIT(!plat->cs);
}
clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
}
@ -212,16 +245,21 @@ static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv)
* all the time. This hack is also used in the upstream linux driver and
* allows keeping CS active between transfers even if the HW doesn't give
* this possibility.
*
* This workaround only works when the dummy CS (usually CS1 when the actual
* CS is 0) pinmuxed to SPI chip select function if SPI clock is faster than
* SPI_MAX_SYNC_CLOCK. In old broadcom chip, CS1 pin is default to chip select
* function. But this is not the case for new chips. To make this function
* always work, it should be called with maximum clock of SPI_MAX_SYNC_CLOCK.
*/
static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_bytes,
const void *dout, void *din, unsigned long flags)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
size_t data_bytes = bitlen / 8;
size_t step_size = HSSPI_FIFO_SIZE;
uint16_t opcode = 0;
uint32_t val;
uint32_t val = SPI_PFL_MODE_FILL_MASK;
const uint8_t *tx = dout;
uint8_t *rx = din;
@ -240,14 +278,17 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
step_size -= HSSPI_FIFO_OP_SIZE;
/* dual mode */
if ((opcode == HSSPI_FIFO_OP_CODE_R && plat->mode == SPI_RX_DUAL) ||
(opcode == HSSPI_FIFO_OP_CODE_W && plat->mode == SPI_TX_DUAL))
if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) ||
(opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) {
opcode |= HSSPI_FIFO_OP_MBIT_MASK;
/* profile mode */
val = SPI_PFL_MODE_FILL_MASK |
SPI_PFL_MODE_MDRDSZ_MASK |
SPI_PFL_MODE_MDWRSZ_MASK;
/* profile mode */
if (plat->mode & SPI_RX_DUAL)
val |= SPI_PFL_MODE_MDRDSZ_MASK;
if (plat->mode & SPI_TX_DUAL)
val |= SPI_PFL_MODE_MDWRSZ_MASK;
}
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
@ -301,6 +342,182 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
return 0;
}
static int bcm63xx_prepare_prepend_transfer(struct bcm63xx_hsspi_priv *priv,
unsigned int data_bytes, const void *dout, void *din,
unsigned long flags)
{
/*
* only support multiple half duplex write transfer + optional
* full duplex read/write at the end.
*/
if (flags & SPI_XFER_BEGIN) {
/* clear prepends */
priv->prepend_cnt = 0;
}
if (din) {
/* buffering reads not possible for prepend mode */
if (!(flags & SPI_XFER_END)) {
debug("unable to buffer reads\n");
return HSSPI_XFER_MODE_DUMMYCS;
}
/* check rx size */
if (data_bytes > HSSPI_MAX_DATA_SIZE) {
debug("max rx bytes exceeded\n");
return HSSPI_XFER_MODE_DUMMYCS;
}
}
if (dout) {
/* check tx size */
if (flags & SPI_XFER_END) {
if (priv->prepend_cnt + data_bytes > HSSPI_MAX_DATA_SIZE) {
debug("max tx bytes exceeded\n");
return HSSPI_XFER_MODE_DUMMYCS;
}
} else {
if (priv->prepend_cnt + data_bytes > HSSPI_MAX_PREPEND_SIZE) {
debug("max prepend bytes exceeded\n");
return HSSPI_XFER_MODE_DUMMYCS;
}
/*
* buffer transfer data in the prepend buf in case we have to fall
* back to dummy cs mode.
*/
memcpy(&priv->prepend_buf[priv->prepend_cnt], dout, data_bytes);
priv->prepend_cnt += data_bytes;
}
}
return HSSPI_XFER_MODE_PREPEND;
}
static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_bytes,
const void *dout, void *din, unsigned long flags)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
uint16_t opcode = 0;
uint32_t val, offset;
int ret;
if (flags & SPI_XFER_END) {
offset = HSSPI_FIFO_BASE + HSSPI_FIFO_OP_SIZE;
if (priv->prepend_cnt) {
/* copy prepend data */
memcpy_toio(priv->regs + offset,
priv->prepend_buf, priv->prepend_cnt);
}
if (dout && data_bytes) {
/* copy tx data */
offset += priv->prepend_cnt;
memcpy_toio(priv->regs + offset, dout, data_bytes);
}
bcm63xx_hsspi_activate_cs(priv, plat);
if (dout && !din) {
/* all half-duplex write. merge to single write */
data_bytes += priv->prepend_cnt;
opcode = HSSPI_FIFO_OP_CODE_W;
priv->prepend_cnt = 0;
} else if (!dout && din) {
/* half-duplex read with prepend write */
opcode = HSSPI_FIFO_OP_CODE_R;
} else {
/* full duplex read/write */
opcode = HSSPI_FIFO_OP_READ_WRITE;
}
/* profile mode */
val = SPI_PFL_MODE_FILL_MASK;
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
/* dual mode */
if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) ||
(opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) {
opcode |= HSSPI_FIFO_OP_MBIT_MASK;
if (plat->mode & SPI_RX_DUAL) {
val |= SPI_PFL_MODE_MDRDSZ_MASK;
val |= priv->prepend_cnt << SPI_PFL_MODE_MDRDST_SHIFT;
}
if (plat->mode & SPI_TX_DUAL) {
val |= SPI_PFL_MODE_MDWRSZ_MASK;
val |= priv->prepend_cnt << SPI_PFL_MODE_MDWRST_SHIFT;
}
}
val |= (priv->prepend_cnt << SPI_PFL_MODE_PREPCNT_SHIFT);
writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
/* set fifo operation */
val = opcode | (data_bytes & HSSPI_FIFO_OP_BYTES_MASK);
writew(cpu_to_be16(val),
priv->regs + HSSPI_FIFO_OP_REG);
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
SPI_CMD_PFL_MASK;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) &
SPI_CMD_SLAVE_MASK;
writel(val, priv->regs + SPI_CMD_REG);
/* wait for completion */
ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
SPI_STAT_SRCBUSY_MASK, false,
1000, false);
if (ret) {
bcm63xx_hsspi_deactivate_cs(priv);
printf("spi polling timeout\n");
return ret;
}
/* copy rx data */
if (din)
memcpy_fromio(din, priv->regs + HSSPI_FIFO_BASE,
data_bytes);
bcm63xx_hsspi_deactivate_cs(priv);
}
return 0;
}
static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
int ret;
u32 data_bytes = bitlen >> 3;
if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
priv->xfer_mode =
bcm63xx_prepare_prepend_transfer(priv, data_bytes, dout, din, flags);
}
/* if not prependable, fall back to dummy cs mode with safe clock */
if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS) {
/* For pending prepend data from previous transfers, send it first */
if (priv->prepend_cnt) {
bcm63xx_hsspi_xfer_dummy_cs(dev, priv->prepend_cnt,
priv->prepend_buf, NULL,
(flags & ~SPI_XFER_END) | SPI_XFER_BEGIN);
priv->prepend_cnt = 0;
}
ret = bcm63xx_hsspi_xfer_dummy_cs(dev, data_bytes, dout, din, flags);
} else {
ret = bcm63xx_hsspi_xfer_prepend(dev, data_bytes, dout, din, flags);
}
if (flags & SPI_XFER_END)
priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
return ret;
}
static const struct dm_spi_ops bcm63xx_hsspi_ops = {
.cs_info = bcm63xx_hsspi_cs_info,
.set_mode = bcm63xx_hsspi_set_mode,
@ -310,6 +527,7 @@ static const struct dm_spi_ops bcm63xx_hsspi_ops = {
static const struct udevice_id bcm63xx_hsspi_ids[] = {
{ .compatible = "brcm,bcm6328-hsspi", },
{ .compatible = "brcm,bcmbca-hsspi-v1.0", },
{ /* sentinel */ }
};
@ -317,6 +535,7 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
{
struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
struct spi_slave *slave = dev_get_parent_priv(dev);
/* check cs */
if (plat->cs >= priv->num_cs) {
@ -330,6 +549,13 @@ static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
else
priv->cs_pols &= ~BIT(plat->cs);
/*
* set the max read/write size to make sure each xfer are within the
* prepend limit
*/
slave->max_read_size = HSSPI_MAX_DATA_SIZE;
slave->max_write_size = HSSPI_MAX_DATA_SIZE;
return 0;
}
@ -391,6 +617,9 @@ static int bcm63xx_hsspi_probe(struct udevice *dev)
priv->cs_pols = readl(priv->regs + SPI_CTL_REG) &
SPI_CTL_CS_POL_MASK;
/* default in prepend mode */
priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
return 0;
}

414
drivers/spi/bcmbca_hsspi.c Normal file
View file

@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
*
* Derived from linux/drivers/spi/spi-bcm63xx-hsspi.c:
* Copyright (C) 2000-2010 Broadcom Corporation
* Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
* Copyright (C) 2021 Broadcom Ltd
*/
#include <common.h>
#include <asm/io.h>
#include <clk.h>
#include <spi.h>
#include <reset.h>
#include <wait_bit.h>
#include <dm.h>
#include <dm/device_compat.h>
#define HSSPI_PP 0
#define SPI_MAX_SYNC_CLOCK 30000000
/* SPI Control register */
#define SPI_CTL_REG 0x000
#define SPI_CTL_CS_POL_SHIFT 0
#define SPI_CTL_CS_POL_MASK (0xff << SPI_CTL_CS_POL_SHIFT)
#define SPI_CTL_CLK_GATE_SHIFT 16
#define SPI_CTL_CLK_GATE_MASK BIT(SPI_CTL_CLK_GATE_SHIFT)
#define SPI_CTL_CLK_POL_SHIFT 17
#define SPI_CTL_CLK_POL_MASK BIT(SPI_CTL_CLK_POL_SHIFT)
/* SPI Interrupts registers */
#define SPI_IR_STAT_REG 0x008
#define SPI_IR_ST_MASK_REG 0x00c
#define SPI_IR_MASK_REG 0x010
#define SPI_IR_CLEAR_ALL 0xff001f1f
/* SPI Ping-Pong Command registers */
#define SPI_CMD_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x00)
#define SPI_CMD_OP_SHIFT 0
#define SPI_CMD_OP_START BIT(SPI_CMD_OP_SHIFT)
#define SPI_CMD_PFL_SHIFT 8
#define SPI_CMD_PFL_MASK (0x7 << SPI_CMD_PFL_SHIFT)
#define SPI_CMD_SLAVE_SHIFT 12
#define SPI_CMD_SLAVE_MASK (0x7 << SPI_CMD_SLAVE_SHIFT)
/* SPI Ping-Pong Status registers */
#define SPI_STAT_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x04)
#define SPI_STAT_SRCBUSY_SHIFT 1
#define SPI_STAT_SRCBUSY_MASK BIT(SPI_STAT_SRCBUSY_SHIFT)
/* SPI Profile Clock registers */
#define SPI_PFL_CLK_REG(x) (0x100 + (0x20 * (x)) + 0x00)
#define SPI_PFL_CLK_FREQ_SHIFT 0
#define SPI_PFL_CLK_FREQ_MASK (0x3fff << SPI_PFL_CLK_FREQ_SHIFT)
#define SPI_PFL_CLK_RSTLOOP_SHIFT 15
#define SPI_PFL_CLK_RSTLOOP_MASK BIT(SPI_PFL_CLK_RSTLOOP_SHIFT)
/* SPI Profile Signal registers */
#define SPI_PFL_SIG_REG(x) (0x100 + (0x20 * (x)) + 0x04)
#define SPI_PFL_SIG_LATCHRIS_SHIFT 12
#define SPI_PFL_SIG_LATCHRIS_MASK BIT(SPI_PFL_SIG_LATCHRIS_SHIFT)
#define SPI_PFL_SIG_LAUNCHRIS_SHIFT 13
#define SPI_PFL_SIG_LAUNCHRIS_MASK BIT(SPI_PFL_SIG_LAUNCHRIS_SHIFT)
#define SPI_PFL_SIG_ASYNCIN_SHIFT 16
#define SPI_PFL_SIG_ASYNCIN_MASK BIT(SPI_PFL_SIG_ASYNCIN_SHIFT)
/* SPI Profile Mode registers */
#define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08)
#define SPI_PFL_MODE_FILL_SHIFT 0
#define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT)
#define SPI_PFL_MODE_MDRDSZ_SHIFT 16
#define SPI_PFL_MODE_MDRDSZ_MASK BIT(SPI_PFL_MODE_MDRDSZ_SHIFT)
#define SPI_PFL_MODE_MDWRSZ_SHIFT 18
#define SPI_PFL_MODE_MDWRSZ_MASK BIT(SPI_PFL_MODE_MDWRSZ_SHIFT)
#define SPI_PFL_MODE_3WIRE_SHIFT 20
#define SPI_PFL_MODE_3WIRE_MASK BIT(SPI_PFL_MODE_3WIRE_SHIFT)
/* SPI Ping-Pong FIFO registers */
#define HSSPI_FIFO_SIZE 0x200
#define HSSPI_FIFO_BASE (0x200 + \
(HSSPI_FIFO_SIZE * HSSPI_PP))
/* SPI Ping-Pong FIFO OP register */
#define HSSPI_FIFO_OP_SIZE 0x2
#define HSSPI_FIFO_OP_REG (HSSPI_FIFO_BASE + 0x00)
#define HSSPI_FIFO_OP_BYTES_SHIFT 0
#define HSSPI_FIFO_OP_BYTES_MASK (0x3ff << HSSPI_FIFO_OP_BYTES_SHIFT)
#define HSSPI_FIFO_OP_MBIT_SHIFT 11
#define HSSPI_FIFO_OP_MBIT_MASK BIT(HSSPI_FIFO_OP_MBIT_SHIFT)
#define HSSPI_FIFO_OP_CODE_SHIFT 13
#define HSSPI_FIFO_OP_READ_WRITE (1 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT)
#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE)
#define SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT 0
#define SPIM_CTRL_CS_OVERRIDE_SEL_MASK 0xff
#define SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT 8
#define SPIM_CTRL_CS_OVERRIDE_VAL_MASK 0xff
struct bcmbca_hsspi_priv {
void __iomem *regs;
void __iomem *spim_ctrl;
u32 clk_rate;
u8 num_cs;
u8 cs_pols;
u32 speed;
};
static int bcmbca_hsspi_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
if (cs >= priv->num_cs) {
dev_err(bus, "no cs %u\n", cs);
return -EINVAL;
}
return 0;
}
static int bcmbca_hsspi_set_mode(struct udevice *bus, uint mode)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
/* clock polarity */
if (mode & SPI_CPOL)
setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
else
clrbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
return 0;
}
static int bcmbca_hsspi_set_speed(struct udevice *bus, uint speed)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(bus);
priv->speed = speed;
return 0;
}
static void bcmbca_hsspi_setup_clock(struct bcmbca_hsspi_priv *priv,
struct dm_spi_slave_plat *plat)
{
u32 clr, set;
/* profile clock */
set = DIV_ROUND_UP(priv->clk_rate, priv->speed);
set = DIV_ROUND_UP(2048, set);
set &= SPI_PFL_CLK_FREQ_MASK;
set |= SPI_PFL_CLK_RSTLOOP_MASK;
writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs));
/* profile signal */
set = 0;
clr = SPI_PFL_SIG_LAUNCHRIS_MASK |
SPI_PFL_SIG_LATCHRIS_MASK |
SPI_PFL_SIG_ASYNCIN_MASK;
/* latch/launch config */
if (plat->mode & SPI_CPHA)
set |= SPI_PFL_SIG_LAUNCHRIS_MASK;
else
set |= SPI_PFL_SIG_LATCHRIS_MASK;
/* async clk */
if (priv->speed > SPI_MAX_SYNC_CLOCK)
set |= SPI_PFL_SIG_ASYNCIN_MASK;
clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
/* global control */
set = 0;
clr = 0;
if (priv->cs_pols & BIT(plat->cs))
set |= BIT(plat->cs);
else
clr |= BIT(plat->cs);
clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
}
static void bcmbca_hsspi_activate_cs(struct bcmbca_hsspi_priv *priv,
struct dm_spi_slave_plat *plat)
{
u32 val;
/* set the override bit */
val = readl(priv->spim_ctrl);
val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
writel(val, priv->spim_ctrl);
}
static void bcmbca_hsspi_deactivate_cs(struct bcmbca_hsspi_priv *priv,
struct dm_spi_slave_plat *plat)
{
u32 val;
/* clear the cs override bit */
val = readl(priv->spim_ctrl);
val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT);
writel(val, priv->spim_ctrl);
}
static int bcmbca_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
size_t data_bytes = bitlen / 8;
size_t step_size = HSSPI_FIFO_SIZE;
u16 opcode = 0;
u32 val = SPI_PFL_MODE_FILL_MASK;
const u8 *tx = dout;
u8 *rx = din;
u32 cs_act = 0;
if (flags & SPI_XFER_BEGIN)
bcmbca_hsspi_setup_clock(priv, plat);
/* fifo operation */
if (tx && rx)
opcode = HSSPI_FIFO_OP_READ_WRITE;
else if (rx)
opcode = HSSPI_FIFO_OP_CODE_R;
else if (tx)
opcode = HSSPI_FIFO_OP_CODE_W;
if (opcode != HSSPI_FIFO_OP_CODE_R)
step_size -= HSSPI_FIFO_OP_SIZE;
/* dual mode */
if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) ||
(opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) {
opcode |= HSSPI_FIFO_OP_MBIT_MASK;
/* profile mode */
if (plat->mode & SPI_RX_DUAL)
val |= SPI_PFL_MODE_MDRDSZ_MASK;
if (plat->mode & SPI_TX_DUAL)
val |= SPI_PFL_MODE_MDWRSZ_MASK;
}
if (plat->mode & SPI_3WIRE)
val |= SPI_PFL_MODE_3WIRE_MASK;
writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
/* transfer loop */
while (data_bytes > 0) {
size_t curr_step = min(step_size, data_bytes);
int ret;
/* copy tx data */
if (tx) {
memcpy_toio(priv->regs + HSSPI_FIFO_BASE +
HSSPI_FIFO_OP_SIZE, tx, curr_step);
tx += curr_step;
}
/* set fifo operation */
writew(cpu_to_be16(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK)),
priv->regs + HSSPI_FIFO_OP_REG);
/* make sure we keep cs active until spi transfer is done */
if (!cs_act) {
bcmbca_hsspi_activate_cs(priv, plat);
cs_act = 1;
}
/* issue the transfer */
val = SPI_CMD_OP_START;
val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
SPI_CMD_PFL_MASK;
val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) &
SPI_CMD_SLAVE_MASK;
writel(val, priv->regs + SPI_CMD_REG);
/* wait for completion */
ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
SPI_STAT_SRCBUSY_MASK, false,
1000, false);
if (ret) {
bcmbca_hsspi_deactivate_cs(priv, plat);
dev_err(dev, "interrupt timeout\n");
return ret;
}
data_bytes -= curr_step;
if ((flags & SPI_XFER_END) && !data_bytes)
bcmbca_hsspi_deactivate_cs(priv, plat);
/* copy rx data */
if (rx) {
memcpy_fromio(rx, priv->regs + HSSPI_FIFO_BASE,
curr_step);
rx += curr_step;
}
}
return 0;
}
static const struct dm_spi_ops bcmbca_hsspi_ops = {
.cs_info = bcmbca_hsspi_cs_info,
.set_mode = bcmbca_hsspi_set_mode,
.set_speed = bcmbca_hsspi_set_speed,
.xfer = bcmbca_hsspi_xfer,
};
static const struct udevice_id bcmbca_hsspi_ids[] = {
{ .compatible = "brcm,bcmbca-hsspi-v1.1", },
{ /* sentinel */ }
};
static int bcmbca_hsspi_child_pre_probe(struct udevice *dev)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(dev->parent);
struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
u32 val;
/* check cs */
if (plat->cs >= priv->num_cs) {
dev_err(dev, "no cs %u\n", plat->cs);
return -EINVAL;
}
/* cs polarity */
if (plat->mode & SPI_CS_HIGH)
priv->cs_pols |= BIT(plat->cs);
else
priv->cs_pols &= ~BIT(plat->cs);
/* set the polarity to spim cs register */
val = readl(priv->spim_ctrl);
val &= ~BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
if (priv->cs_pols & BIT(plat->cs))
val |= BIT(plat->cs + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT);
writel(val, priv->spim_ctrl);
return 0;
}
static int bcmbca_hsspi_probe(struct udevice *dev)
{
struct bcmbca_hsspi_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret;
priv->regs = dev_remap_addr_name(dev, "hsspi");
if (!priv->regs)
return -EINVAL;
priv->spim_ctrl = dev_remap_addr_name(dev, "spim-ctrl");
if (!priv->spim_ctrl) {
dev_err(dev, "misc spim ctrl register not defined in dts!\n");
return -EINVAL;
}
priv->num_cs = dev_read_u32_default(dev, "num-cs", 8);
/* enable clock */
ret = clk_get_by_name(dev, "hsspi", &clk);
if (ret < 0)
return ret;
ret = clk_enable(&clk);
if (ret < 0 && ret != -ENOSYS)
return ret;
clk_free(&clk);
/* get clock rate */
ret = clk_get_by_name(dev, "pll", &clk);
if (ret < 0 && ret != -ENOSYS)
return ret;
priv->clk_rate = clk_get_rate(&clk);
clk_free(&clk);
/* initialize hardware */
writel(0, priv->regs + SPI_IR_MASK_REG);
/* clear pending interrupts */
writel(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG);
/* enable clk gate */
setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK);
/* read default cs polarities */
priv->cs_pols = readl(priv->regs + SPI_CTL_REG) &
SPI_CTL_CS_POL_MASK;
dev_info(dev, "Broadcom BCMBCA HS SPI bus driver\n");
return 0;
}
U_BOOT_DRIVER(bcmbca_hsspi) = {
.name = "bcmbca_hsspi",
.id = UCLASS_SPI,
.of_match = bcmbca_hsspi_ids,
.ops = &bcmbca_hsspi_ops,
.priv_auto = sizeof(struct bcmbca_hsspi_priv),
.child_pre_probe = bcmbca_hsspi_child_pre_probe,
.probe = bcmbca_hsspi_probe,
};

View file

@ -40,7 +40,7 @@ static inline void spi_cs_activate(struct udevice *dev)
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
dm_gpio_set_value(&priv->cs_gpio, 0);
dm_gpio_set_value(&priv->cs_gpio, 1);
}
static inline void spi_cs_deactivate(struct udevice *dev)
@ -48,7 +48,7 @@ static inline void spi_cs_deactivate(struct udevice *dev)
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
dm_gpio_set_value(&priv->cs_gpio, 1);
dm_gpio_set_value(&priv->cs_gpio, 0);
}
static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv)
@ -122,6 +122,9 @@ static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen,
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("npcm_pspi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
dev->parent->name, dev->name, *(uint *)tx, *(uint *)rx, bitlen);
npcm_pspi_disable(priv);
return ret;
@ -183,6 +186,7 @@ static int npcm_pspi_set_mode(struct udevice *bus, uint mode)
val |= pspi_mode;
writew(val, priv->base + PSPI_CTL1);
debug("%s: mode=%u\n", __func__, mode);
return 0;
}
@ -197,9 +201,9 @@ static int npcm_pspi_probe(struct udevice *bus)
return ret;
priv->base = dev_read_addr_ptr(bus);
priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0);
priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 1000000);
gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
&priv->cs_gpio, GPIOD_IS_OUT);
&priv->cs_gpio, GPIOD_IS_OUT| GPIOD_ACTIVE_LOW);
return 0;
}

View file

@ -12,9 +12,11 @@
#include <clk.h>
#include <common.h>
#include <dm.h>
#include <dm/platform_data/spi_pl022.h>
#include <dm/device_compat.h>
#include <fdtdec.h>
#include <linux/io.h>
#include <asm/global_data.h>
#include <asm/gpio.h>
#include <spi.h>
#define SSP_CR0 0x000
@ -66,6 +68,15 @@
#define SSP_SR_MASK_RFF (0x1 << 3) /* Receive FIFO full */
#define SSP_SR_MASK_BSY (0x1 << 4) /* Busy Flag */
struct pl022_spi_pdata {
fdt_addr_t addr;
fdt_size_t size;
unsigned int freq;
#if CONFIG_IS_ENABLED(DM_GPIO)
struct gpio_desc cs_gpio;
#endif
};
struct pl022_spi_slave {
void *base;
unsigned int freq;
@ -107,7 +118,7 @@ static int pl022_spi_probe(struct udevice *bus)
return 0;
}
static void flush(struct pl022_spi_slave *ps)
static void pl022_spi_flush(struct pl022_spi_slave *ps)
{
do {
while (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE)
@ -126,7 +137,7 @@ static int pl022_spi_claim_bus(struct udevice *dev)
reg |= SSP_CR1_MASK_SSE;
writew(reg, ps->base + SSP_CR1);
flush(ps);
pl022_spi_flush(ps);
return 0;
}
@ -137,7 +148,7 @@ static int pl022_spi_release_bus(struct udevice *dev)
struct pl022_spi_slave *ps = dev_get_priv(bus);
u16 reg;
flush(ps);
pl022_spi_flush(ps);
/* Disable the SPI hardware */
reg = readw(ps->base + SSP_CR1);
@ -147,6 +158,17 @@ static int pl022_spi_release_bus(struct udevice *dev)
return 0;
}
static void pl022_spi_set_cs(struct udevice *dev, bool on)
{
#if CONFIG_IS_ENABLED(DM_GPIO)
struct udevice *bus = dev->parent;
struct pl022_spi_pdata *plat = dev_get_plat(bus);
if (dm_gpio_is_valid(&plat->cs_gpio))
dm_gpio_set_value(&plat->cs_gpio, on ? 1 : 0);
#endif
}
static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@ -159,7 +181,7 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (bitlen == 0)
/* Finish any previously submitted transfers */
return 0;
goto done;
/*
* TODO: The controller can do non-multiple-of-8 bit
@ -172,9 +194,13 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
return -1;
ret = -1;
goto done;
}
if (flags & SPI_XFER_BEGIN)
pl022_spi_set_cs(dev, true);
len = bitlen / 8;
while (len_tx < len) {
@ -201,6 +227,10 @@ static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
}
done:
if (flags & SPI_XFER_END)
pl022_spi_set_cs(dev, false);
return ret;
}
@ -303,11 +333,18 @@ static int pl022_spi_of_to_plat(struct udevice *bus)
plat->freq = clk_get_rate(&clkdev);
#if CONFIG_IS_ENABLED(DM_GPIO)
ret = gpio_request_by_name(bus, "cs-gpios", 0, &plat->cs_gpio,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret < 0 && ret != -ENOENT)
return ret;
#endif
return 0;
}
static const struct udevice_id pl022_spi_ids[] = {
{ .compatible = "arm,pl022-spi" },
{ .compatible = "arm,pl022" },
{ }
};
#endif

View file

@ -248,19 +248,32 @@ static int soft_spi_probe(struct udevice *dev)
cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
GPIOD_IS_OUT | cs_flags) ||
gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
GPIOD_IS_OUT | clk_flags))
ret = gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
GPIOD_IS_OUT | cs_flags);
if (ret)
return -EINVAL;
ret = gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
GPIOD_IS_OUT | clk_flags);
if (ret)
ret = gpio_request_by_name(dev, "sck-gpios", 0, &plat->sclk,
GPIOD_IS_OUT | clk_flags);
if (ret)
return -EINVAL;
ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
ret = gpio_request_by_name(dev, "mosi-gpios", 0, &plat->mosi,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
plat->flags |= SPI_MASTER_NO_TX;
ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
GPIOD_IS_IN);
if (ret)
ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
GPIOD_IS_IN);
if (ret)
plat->flags |= SPI_MASTER_NO_RX;

View file

@ -186,7 +186,7 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
struct udevice *bus = dev->parent;
struct synquacer_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
u32 val, div, bus_width = 1;
u32 val, div, bus_width;
int rwflag;
rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
@ -203,16 +203,14 @@ static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
priv->mode = slave_plat->mode;
priv->speed = slave_plat->max_hz;
if (priv->mode & SPI_TX_BYTE)
bus_width = 1;
else if (priv->mode & SPI_TX_DUAL)
if (priv->mode & SPI_TX_DUAL)
bus_width = 2;
else if (priv->mode & SPI_TX_QUAD)
bus_width = 4;
else if (priv->mode & SPI_TX_OCTAL)
bus_width = 8;
else
log_warning("SPI mode not configured, setting to byte mode\n");
bus_width = 1; /* default is single bit mode */
div = DIV_ROUND_UP(125000000, priv->speed);

View file

@ -1,21 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2018
* Quentin Schulz, Bootlin, quentin.schulz@bootlin.com
*
* Structure for use with U_BOOT_DRVINFO for pl022 SPI devices or to use
* in of_to_plat.
*/
#ifndef __spi_pl022_h
#define __spi_pl022_h
#include <fdtdec.h>
struct pl022_spi_pdata {
fdt_addr_t addr;
fdt_size_t size;
unsigned int freq;
};
#endif /* __spi_pl022_h */