mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-16 14:08:45 +00:00
- Add xtxtech spi-nor chip parts (Bruce Suen) - Add bcm63xx-hsspi driver fixes (William Zhang)
This commit is contained in:
commit
cef3675509
12 changed files with 931 additions and 74 deletions
134
doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml
Normal file
134
doc/device-tree-bindings/spi/brcm,bcm63xx-hsspi.yaml
Normal 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>;
|
||||
};
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
414
drivers/spi/bcmbca_hsspi.c
Normal 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,
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
Loading…
Add table
Reference in a new issue