mediatek: Refactor SPI code among similar SOCs
Refactor SPI code which will be reused amon similar SOCs. BUG=b:80501386 BRANCH=none TEST=Boots correctly on Elm Change-Id: If5a6c554dc8361e729cf5c464325b97b2bfb7098 Signed-off-by: Tristan Shieh <tristan.shieh@mediatek.com> Reviewed-on: https://review.coreboot.org/27497 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
parent
9d6523c7db
commit
86d0d6e2cf
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2018 MediaTek 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 MTK_COMMON_SPI_H
|
||||
#define MTK_COMMON_SPI_H
|
||||
|
||||
#include <spi-generic.h>
|
||||
|
||||
enum {
|
||||
SPI_CFG1_CS_IDLE_SHIFT = 0,
|
||||
SPI_CFG1_PACKET_LOOP_SHIFT = 8,
|
||||
SPI_CFG1_PACKET_LENGTH_SHIFT = 16,
|
||||
|
||||
SPI_CFG1_CS_IDLE_MASK = 0xff << SPI_CFG1_CS_IDLE_SHIFT,
|
||||
SPI_CFG1_PACKET_LOOP_MASK = 0xff << SPI_CFG1_PACKET_LOOP_SHIFT,
|
||||
SPI_CFG1_PACKET_LENGTH_MASK = 0x3ff << SPI_CFG1_PACKET_LENGTH_SHIFT,
|
||||
};
|
||||
|
||||
enum {
|
||||
SPI_CMD_ACT_SHIFT = 0,
|
||||
SPI_CMD_RESUME_SHIFT = 1,
|
||||
SPI_CMD_RST_SHIFT = 2,
|
||||
SPI_CMD_PAUSE_EN_SHIFT = 4,
|
||||
SPI_CMD_DEASSERT_SHIFT = 5,
|
||||
SPI_CMD_CPHA_SHIFT = 8,
|
||||
SPI_CMD_CPOL_SHIFT = 9,
|
||||
SPI_CMD_RX_DMA_SHIFT = 10,
|
||||
SPI_CMD_TX_DMA_SHIFT = 11,
|
||||
SPI_CMD_TXMSBF_SHIFT = 12,
|
||||
SPI_CMD_RXMSBF_SHIFT = 13,
|
||||
SPI_CMD_RX_ENDIAN_SHIFT = 14,
|
||||
SPI_CMD_TX_ENDIAN_SHIFT = 15,
|
||||
SPI_CMD_FINISH_IE_SHIFT = 16,
|
||||
SPI_CMD_PAUSE_IE_SHIFT = 17,
|
||||
|
||||
SPI_CMD_ACT_EN = BIT(SPI_CMD_ACT_SHIFT),
|
||||
SPI_CMD_RESUME_EN = BIT(SPI_CMD_RESUME_SHIFT),
|
||||
SPI_CMD_RST_EN = BIT(SPI_CMD_RST_SHIFT),
|
||||
SPI_CMD_PAUSE_EN = BIT(SPI_CMD_PAUSE_EN_SHIFT),
|
||||
SPI_CMD_DEASSERT_EN = BIT(SPI_CMD_DEASSERT_SHIFT),
|
||||
SPI_CMD_CPHA_EN = BIT(SPI_CMD_CPHA_SHIFT),
|
||||
SPI_CMD_CPOL_EN = BIT(SPI_CMD_CPOL_SHIFT),
|
||||
SPI_CMD_RX_DMA_EN = BIT(SPI_CMD_RX_DMA_SHIFT),
|
||||
SPI_CMD_TX_DMA_EN = BIT(SPI_CMD_TX_DMA_SHIFT),
|
||||
SPI_CMD_TXMSBF_EN = BIT(SPI_CMD_TXMSBF_SHIFT),
|
||||
SPI_CMD_RXMSBF_EN = BIT(SPI_CMD_RXMSBF_SHIFT),
|
||||
SPI_CMD_RX_ENDIAN_EN = BIT(SPI_CMD_RX_ENDIAN_SHIFT),
|
||||
SPI_CMD_TX_ENDIAN_EN = BIT(SPI_CMD_TX_ENDIAN_SHIFT),
|
||||
SPI_CMD_FINISH_IE_EN = BIT(SPI_CMD_FINISH_IE_SHIFT),
|
||||
SPI_CMD_PAUSE_IE_EN = BIT(SPI_CMD_PAUSE_IE_SHIFT),
|
||||
};
|
||||
|
||||
enum spi_pad_mask {
|
||||
SPI_PAD0_MASK = 0x0,
|
||||
SPI_PAD1_MASK = 0x1,
|
||||
SPI_PAD2_MASK = 0x2,
|
||||
SPI_PAD3_MASK = 0x3,
|
||||
SPI_PAD_SEL_MASK = 0x3
|
||||
};
|
||||
|
||||
struct mtk_spi_regs;
|
||||
|
||||
struct mtk_spi_bus {
|
||||
struct spi_slave slave;
|
||||
struct mtk_spi_regs *regs;
|
||||
int initialized;
|
||||
int state;
|
||||
};
|
||||
|
||||
extern const struct spi_ctrlr spi_ctrlr;
|
||||
extern struct mtk_spi_bus spi_bus[];
|
||||
|
||||
void mtk_spi_set_gpio_pinmux(unsigned int bus,
|
||||
enum spi_pad_mask pad_select);
|
||||
void mtk_spi_set_timing(struct mtk_spi_regs *regs, u32 sck_ticks, u32 cs_ticks);
|
||||
void mtk_spi_init(unsigned int bus, enum spi_pad_mask pad_select,
|
||||
unsigned int speed_hz);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2018 MediaTek 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 <endian.h>
|
||||
#include <stdlib.h>
|
||||
#include <soc/pll.h>
|
||||
#include <soc/spi.h>
|
||||
#include <timer.h>
|
||||
|
||||
#define MTK_SPI_DEBUG 0
|
||||
|
||||
enum {
|
||||
MTK_FIFO_DEPTH = 32,
|
||||
MTK_TXRX_TIMEOUT_US = 1000 * 1000,
|
||||
MTK_ARBITRARY_VALUE = 0xdeaddead
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_SPI_IDLE = 0,
|
||||
MTK_SPI_PAUSE_IDLE = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_SPI_BUSY_STATUS = 1,
|
||||
MTK_SPI_PAUSE_FINISH_INT_STATUS = 3
|
||||
};
|
||||
|
||||
static inline struct mtk_spi_bus *to_mtk_spi(const struct spi_slave *slave)
|
||||
{
|
||||
assert(slave->bus < SPI_BUS_NUMBER);
|
||||
return &spi_bus[slave->bus];
|
||||
}
|
||||
|
||||
static void spi_sw_reset(struct mtk_spi_regs *regs)
|
||||
{
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_RST_EN);
|
||||
clrbits_le32(®s->spi_cmd_reg, SPI_CMD_RST_EN);
|
||||
}
|
||||
|
||||
void mtk_spi_init(unsigned int bus, enum spi_pad_mask pad_select,
|
||||
unsigned int speed_hz)
|
||||
{
|
||||
u32 div, sck_ticks, cs_ticks;
|
||||
|
||||
assert(bus < SPI_BUS_NUMBER);
|
||||
|
||||
struct mtk_spi_bus *slave = &spi_bus[bus];
|
||||
struct mtk_spi_regs *regs = slave->regs;
|
||||
|
||||
if (speed_hz < SPI_HZ / 2)
|
||||
div = div_round_up(SPI_HZ, speed_hz);
|
||||
else
|
||||
div = 1;
|
||||
|
||||
sck_ticks = div_round_up(div, 2);
|
||||
cs_ticks = sck_ticks * 2;
|
||||
|
||||
printk(BIOS_DEBUG, "SPI%u(PAD%u) initialized at %u Hz\n",
|
||||
bus, pad_select, SPI_HZ / (sck_ticks * 2));
|
||||
|
||||
mtk_spi_set_timing(regs, sck_ticks, cs_ticks);
|
||||
|
||||
clrsetbits_le32(®s->spi_cmd_reg,
|
||||
(SPI_CMD_CPHA_EN | SPI_CMD_CPOL_EN |
|
||||
SPI_CMD_TX_ENDIAN_EN | SPI_CMD_RX_ENDIAN_EN |
|
||||
SPI_CMD_TX_DMA_EN | SPI_CMD_RX_DMA_EN |
|
||||
SPI_CMD_PAUSE_EN | SPI_CMD_DEASSERT_EN),
|
||||
(SPI_CMD_TXMSBF_EN | SPI_CMD_RXMSBF_EN |
|
||||
SPI_CMD_FINISH_IE_EN | SPI_CMD_PAUSE_IE_EN));
|
||||
|
||||
mtk_spi_set_gpio_pinmux(bus, pad_select);
|
||||
|
||||
clrsetbits_le32(®s->spi_pad_macro_sel_reg, SPI_PAD_SEL_MASK,
|
||||
pad_select);
|
||||
}
|
||||
|
||||
static void mtk_spi_dump_data(const char *name, const uint8_t *data, int size)
|
||||
{
|
||||
if (MTK_SPI_DEBUG) {
|
||||
int i;
|
||||
|
||||
printk(BIOS_DEBUG, "%s: 0x ", name);
|
||||
for (i = 0; i < size; i++)
|
||||
printk(BIOS_INFO, "%#x ", data[i]);
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_ctrlr_claim_bus(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
|
||||
setbits_le32(®s->spi_cmd_reg, 1 << SPI_CMD_PAUSE_EN_SHIFT);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_transfer(const struct spi_slave *slave, void *in, const void *out,
|
||||
size_t *bytes_in, size_t *bytes_out)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
uint32_t reg_val = 0;
|
||||
uint32_t i;
|
||||
struct stopwatch sw;
|
||||
size_t size;
|
||||
|
||||
if (*bytes_out == 0)
|
||||
size = *bytes_in;
|
||||
else if (*bytes_in == 0)
|
||||
size = *bytes_out;
|
||||
else
|
||||
size = MIN(*bytes_in, *bytes_out);
|
||||
|
||||
clrsetbits_le32(®s->spi_cfg1_reg,
|
||||
SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK,
|
||||
((size - 1) << SPI_CFG1_PACKET_LENGTH_SHIFT) |
|
||||
(0 << SPI_CFG1_PACKET_LOOP_SHIFT));
|
||||
|
||||
if (*bytes_out) {
|
||||
const uint8_t *outb = (const uint8_t *)out;
|
||||
for (i = 0; i < size; i++) {
|
||||
reg_val |= outb[i] << ((i % 4) * 8);
|
||||
if (i % 4 == 3) {
|
||||
write32(®s->spi_tx_data_reg, reg_val);
|
||||
reg_val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i % 4 != 0)
|
||||
write32(®s->spi_tx_data_reg, reg_val);
|
||||
|
||||
mtk_spi_dump_data("the outb data is",
|
||||
(const uint8_t *)outb, size);
|
||||
} else {
|
||||
/* The SPI controller will transmit in full-duplex for RX,
|
||||
* therefore we need arbitrary data on MOSI which the slave
|
||||
* must ignore.
|
||||
*/
|
||||
uint32_t word_count = div_round_up(size, sizeof(u32));
|
||||
for (i = 0; i < word_count; i++)
|
||||
write32(®s->spi_tx_data_reg, MTK_ARBITRARY_VALUE);
|
||||
}
|
||||
|
||||
if (mtk_slave->state == MTK_SPI_IDLE) {
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_ACT_EN);
|
||||
mtk_slave->state = MTK_SPI_PAUSE_IDLE;
|
||||
} else if (mtk_slave->state == MTK_SPI_PAUSE_IDLE) {
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_RESUME_EN);
|
||||
}
|
||||
|
||||
stopwatch_init_usecs_expire(&sw, MTK_TXRX_TIMEOUT_US);
|
||||
while ((read32(®s->spi_status1_reg) & MTK_SPI_BUSY_STATUS) == 0) {
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR,
|
||||
"Timeout waiting for status1 status.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
stopwatch_init_usecs_expire(&sw, MTK_TXRX_TIMEOUT_US);
|
||||
while ((read32(®s->spi_status0_reg) &
|
||||
MTK_SPI_PAUSE_FINISH_INT_STATUS) == 0) {
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR,
|
||||
"Timeout waiting for status0 status.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (*bytes_in) {
|
||||
uint8_t *inb = (uint8_t *)in;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (i % 4 == 0)
|
||||
reg_val = read32(®s->spi_rx_data_reg);
|
||||
inb[i] = (reg_val >> ((i % 4) * 8)) & 0xff;
|
||||
}
|
||||
mtk_spi_dump_data("the inb data is", inb, size);
|
||||
|
||||
*bytes_in -= size;
|
||||
}
|
||||
|
||||
if (*bytes_out)
|
||||
*bytes_out -= size;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
spi_sw_reset(regs);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
|
||||
size_t bytes_out, void *din, size_t bytes_in)
|
||||
{
|
||||
while (bytes_out || bytes_in) {
|
||||
size_t in_now = MIN(bytes_in, MTK_FIFO_DEPTH);
|
||||
size_t out_now = MIN(bytes_out, MTK_FIFO_DEPTH);
|
||||
size_t in_rem = in_now;
|
||||
size_t out_rem = out_now;
|
||||
|
||||
int ret = do_transfer(slave, din, dout, &in_rem, &out_rem);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (bytes_out) {
|
||||
size_t sent = out_now - out_rem;
|
||||
bytes_out -= sent;
|
||||
dout += sent;
|
||||
}
|
||||
|
||||
if (bytes_in) {
|
||||
size_t received = in_now - in_rem;
|
||||
bytes_in -= received;
|
||||
din += received;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_ctrlr_release_bus(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
|
||||
clrbits_le32(®s->spi_cmd_reg, SPI_CMD_PAUSE_EN);
|
||||
spi_sw_reset(regs);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
}
|
||||
|
||||
static int spi_ctrlr_setup(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *eslave = to_mtk_spi(slave);
|
||||
assert(read32(&eslave->regs->spi_cfg0_reg) != 0);
|
||||
spi_sw_reset(eslave->regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct spi_ctrlr spi_ctrlr = {
|
||||
.setup = spi_ctrlr_setup,
|
||||
.claim_bus = spi_ctrlr_claim_bus,
|
||||
.release_bus = spi_ctrlr_release_bus,
|
||||
.xfer = spi_ctrlr_xfer,
|
||||
.max_xfer_size = 65535,
|
||||
};
|
|
@ -19,7 +19,7 @@ bootblock-y += bootblock.c
|
|||
bootblock-$(CONFIG_SPI_FLASH) += flash_controller.c
|
||||
bootblock-y += i2c.c
|
||||
bootblock-y += ../common/pll.c pll.c
|
||||
bootblock-y += spi.c
|
||||
bootblock-y += ../common/spi.c spi.c
|
||||
bootblock-y += ../common/timer.c
|
||||
bootblock-y += timer.c
|
||||
|
||||
|
@ -34,7 +34,7 @@ bootblock-y += ../common/mmu_operations.c mmu_operations.c
|
|||
################################################################################
|
||||
|
||||
verstage-y += i2c.c
|
||||
verstage-y += spi.c
|
||||
verstage-y += ../common/spi.c spi.c
|
||||
|
||||
verstage-$(CONFIG_DRIVERS_UART) += ../common/uart.c
|
||||
|
||||
|
@ -53,8 +53,8 @@ romstage-y += timer.c
|
|||
|
||||
romstage-$(CONFIG_DRIVERS_UART) += ../common/uart.c
|
||||
romstage-y += ../common/cbmem.c
|
||||
romstage-y += spi.c
|
||||
romstage-y += ../common/gpio.c gpio.c
|
||||
romstage-y += ../common/spi.c spi.c
|
||||
romstage-y += pmic_wrap.c mt6391.c
|
||||
romstage-y += memory.c
|
||||
romstage-y += emi.c dramc_pi_basic_api.c dramc_pi_calibration_api.c
|
||||
|
@ -64,7 +64,7 @@ romstage-y += rtc.c
|
|||
################################################################################
|
||||
|
||||
ramstage-y += ../common/cbmem.c emi.c
|
||||
ramstage-y += spi.c
|
||||
ramstage-y += ../common/spi.c spi.c
|
||||
ramstage-$(CONFIG_SPI_FLASH) += flash_controller.c
|
||||
ramstage-y += soc.c ../common/mtcmos.c
|
||||
ramstage-y += ../common/timer.c
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
#ifndef MTK_MT8173_SPI_H
|
||||
#define MTK_MT8173_SPI_H
|
||||
|
||||
#include <spi-generic.h>
|
||||
#include <types.h>
|
||||
#include <soc/spi_common.h>
|
||||
|
||||
#define SPI_BUS_NUMBER 1
|
||||
|
||||
/* SPI peripheral register map. */
|
||||
typedef struct mtk_spi_regs {
|
||||
|
@ -35,7 +36,6 @@ typedef struct mtk_spi_regs {
|
|||
|
||||
check_member(mtk_spi_regs, spi_pad_macro_sel_reg, 0x24);
|
||||
|
||||
/* SPI_CFG0_REG */
|
||||
enum {
|
||||
SPI_CFG0_SCK_HIGH_SHIFT = 0,
|
||||
SPI_CFG0_SCK_LOW_SHIFT = 8,
|
||||
|
@ -43,67 +43,4 @@ enum {
|
|||
SPI_CFG0_CS_SETUP_SHIFT = 24,
|
||||
};
|
||||
|
||||
/*SPI_CFG1_REG*/
|
||||
enum {
|
||||
SPI_CFG1_CS_IDLE_SHIFT = 0,
|
||||
SPI_CFG1_PACKET_LOOP_SHIFT = 8,
|
||||
SPI_CFG1_PACKET_LENGTH_SHIFT = 16,
|
||||
|
||||
SPI_CFG1_CS_IDLE_MASK = 0xff << SPI_CFG1_CS_IDLE_SHIFT,
|
||||
SPI_CFG1_PACKET_LOOP_MASK = 0xff << SPI_CFG1_PACKET_LOOP_SHIFT,
|
||||
SPI_CFG1_PACKET_LENGTH_MASK = 0x3ff << SPI_CFG1_PACKET_LENGTH_SHIFT,
|
||||
};
|
||||
|
||||
enum {
|
||||
SPI_CMD_ACT_SHIFT = 0,
|
||||
SPI_CMD_RESUME_SHIFT = 1,
|
||||
SPI_CMD_RST_SHIFT = 2,
|
||||
SPI_CMD_PAUSE_EN_SHIFT = 4,
|
||||
SPI_CMD_DEASSERT_SHIFT = 5,
|
||||
SPI_CMD_CPHA_SHIFT = 8,
|
||||
SPI_CMD_CPOL_SHIFT = 9,
|
||||
SPI_CMD_RX_DMA_SHIFT = 10,
|
||||
SPI_CMD_TX_DMA_SHIFT = 11,
|
||||
SPI_CMD_TXMSBF_SHIFT = 12,
|
||||
SPI_CMD_RXMSBF_SHIFT = 13,
|
||||
SPI_CMD_RX_ENDIAN_SHIFT = 14,
|
||||
SPI_CMD_TX_ENDIAN_SHIFT = 15,
|
||||
SPI_CMD_FINISH_IE_SHIFT = 16,
|
||||
SPI_CMD_PAUSE_IE_SHIFT = 17,
|
||||
|
||||
SPI_CMD_ACT_EN = BIT(SPI_CMD_ACT_SHIFT),
|
||||
SPI_CMD_RESUME_EN = BIT(SPI_CMD_RESUME_SHIFT),
|
||||
SPI_CMD_RST_EN = BIT(SPI_CMD_RST_SHIFT),
|
||||
SPI_CMD_PAUSE_EN = BIT(SPI_CMD_PAUSE_EN_SHIFT),
|
||||
SPI_CMD_DEASSERT_EN = BIT(SPI_CMD_DEASSERT_SHIFT),
|
||||
SPI_CMD_CPHA_EN = BIT(SPI_CMD_CPHA_SHIFT),
|
||||
SPI_CMD_CPOL_EN = BIT(SPI_CMD_CPOL_SHIFT),
|
||||
SPI_CMD_RX_DMA_EN = BIT(SPI_CMD_RX_DMA_SHIFT),
|
||||
SPI_CMD_TX_DMA_EN = BIT(SPI_CMD_TX_DMA_SHIFT),
|
||||
SPI_CMD_TXMSBF_EN = BIT(SPI_CMD_TXMSBF_SHIFT),
|
||||
SPI_CMD_RXMSBF_EN = BIT(SPI_CMD_RXMSBF_SHIFT),
|
||||
SPI_CMD_RX_ENDIAN_EN = BIT(SPI_CMD_RX_ENDIAN_SHIFT),
|
||||
SPI_CMD_TX_ENDIAN_EN = BIT(SPI_CMD_TX_ENDIAN_SHIFT),
|
||||
SPI_CMD_FINISH_IE_EN = BIT(SPI_CMD_FINISH_IE_SHIFT),
|
||||
SPI_CMD_PAUSE_IE_EN = BIT(SPI_CMD_PAUSE_IE_SHIFT),
|
||||
};
|
||||
|
||||
enum spi_pad_mask {
|
||||
SPI_PAD0_MASK = 0x0,
|
||||
SPI_PAD1_MASK = 0x1,
|
||||
SPI_PAD2_MASK = 0x2,
|
||||
SPI_PAD3_MASK = 0x3,
|
||||
SPI_PAD_SEL_MASK = 0x3
|
||||
};
|
||||
|
||||
|
||||
struct mtk_spi_bus {
|
||||
struct spi_slave slave;
|
||||
struct mtk_spi_regs *regs;
|
||||
int initialized;
|
||||
int state;
|
||||
};
|
||||
|
||||
void mtk_spi_init(unsigned int bus, unsigned int pad_select,
|
||||
unsigned int speed_hz);
|
||||
#endif
|
||||
|
|
|
@ -15,55 +15,20 @@
|
|||
|
||||
#include <arch/io.h>
|
||||
#include <assert.h>
|
||||
#include <console/console.h>
|
||||
#include <delay.h>
|
||||
#include <endian.h>
|
||||
#include <spi_flash.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <timer.h>
|
||||
#include <soc/addressmap.h>
|
||||
#include <soc/flash_controller.h>
|
||||
#include <soc/gpio.h>
|
||||
#include <soc/pll.h>
|
||||
#include <soc/spi.h>
|
||||
|
||||
enum {
|
||||
MTK_FIFO_DEPTH = 32,
|
||||
MTK_TXRX_TIMEOUT_US = 1000 * 1000,
|
||||
MTK_ARBITRARY_VALUE = 0xdeaddead
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_SPI_IDLE = 0,
|
||||
MTK_SPI_PAUSE_IDLE = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_SPI_BUSY_STATUS = 1,
|
||||
MTK_SPI_PAUSE_FINISH_INT_STATUS = 3
|
||||
};
|
||||
|
||||
static struct mtk_spi_bus spi_bus[1] = {
|
||||
struct mtk_spi_bus spi_bus[SPI_BUS_NUMBER] = {
|
||||
{
|
||||
.regs = (void *)SPI_BASE,
|
||||
.state = MTK_SPI_IDLE,
|
||||
}
|
||||
};
|
||||
|
||||
static inline struct mtk_spi_bus *to_mtk_spi(const struct spi_slave *slave)
|
||||
{
|
||||
assert(slave->bus < ARRAY_SIZE(spi_bus));
|
||||
return &spi_bus[slave->bus];
|
||||
}
|
||||
|
||||
static void spi_sw_reset(struct mtk_spi_regs *regs)
|
||||
{
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_RST_EN);
|
||||
clrbits_le32(®s->spi_cmd_reg, SPI_CMD_RST_EN);
|
||||
}
|
||||
|
||||
static void mtk_spi_set_gpio_pinmux(enum spi_pad_mask pad_select)
|
||||
void mtk_spi_set_gpio_pinmux(unsigned int bus,
|
||||
enum spi_pad_mask pad_select)
|
||||
{
|
||||
/* TODO: implement support for other pads when needed */
|
||||
assert(pad_select == SPI_PAD1_MASK);
|
||||
|
@ -73,30 +38,8 @@ static void mtk_spi_set_gpio_pinmux(enum spi_pad_mask pad_select)
|
|||
gpio_set_mode(GPIO(MSDC2_CMD), PAD_MSDC2_CMD_FUNC_SPI_CS_1);
|
||||
}
|
||||
|
||||
void mtk_spi_init(unsigned int bus, unsigned int pad_select,
|
||||
unsigned int speed_hz)
|
||||
void mtk_spi_set_timing(struct mtk_spi_regs *regs, u32 sck_ticks, u32 cs_ticks)
|
||||
{
|
||||
u32 div, sck_ticks, cs_ticks, reg_val;
|
||||
|
||||
/* mtk spi HW just supports bus 0 */
|
||||
if (bus != 0)
|
||||
die("Error: Only SPI bus 0 is supported.\n");
|
||||
|
||||
struct mtk_spi_bus *slave = &spi_bus[bus];
|
||||
struct mtk_spi_regs *regs = slave->regs;
|
||||
|
||||
if (speed_hz < SPI_HZ / 2)
|
||||
div = div_round_up(SPI_HZ, speed_hz);
|
||||
else
|
||||
div = 1;
|
||||
|
||||
sck_ticks = div_round_up(div, 2);
|
||||
cs_ticks = sck_ticks * 2;
|
||||
|
||||
printk(BIOS_DEBUG, "SPI%u initialized at %u Hz",
|
||||
pad_select, SPI_HZ / (sck_ticks * 2));
|
||||
|
||||
/* set the timing */
|
||||
write32(®s->spi_cfg0_reg,
|
||||
((sck_ticks - 1) << SPI_CFG0_SCK_HIGH_SHIFT) |
|
||||
((sck_ticks - 1) << SPI_CFG0_SCK_LOW_SHIFT) |
|
||||
|
@ -104,200 +47,6 @@ void mtk_spi_init(unsigned int bus, unsigned int pad_select,
|
|||
((cs_ticks - 1) << SPI_CFG0_CS_SETUP_SHIFT));
|
||||
clrsetbits_le32(®s->spi_cfg1_reg, SPI_CFG1_CS_IDLE_MASK,
|
||||
((cs_ticks - 1) << SPI_CFG1_CS_IDLE_SHIFT));
|
||||
|
||||
reg_val = read32(®s->spi_cmd_reg);
|
||||
|
||||
reg_val &= ~SPI_CMD_CPHA_EN;
|
||||
reg_val &= ~SPI_CMD_CPOL_EN;
|
||||
|
||||
/* set the mlsbx and mlsbtx */
|
||||
reg_val |= SPI_CMD_TXMSBF_EN;
|
||||
reg_val |= SPI_CMD_RXMSBF_EN;
|
||||
|
||||
/* set the tx/rx endian */
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
reg_val &= ~SPI_CMD_TX_ENDIAN_EN;
|
||||
reg_val &= ~SPI_CMD_RX_ENDIAN_EN;
|
||||
#else
|
||||
reg_val |= SPI_CMD_TX_ENDIAN_EN;
|
||||
reg_val |= SPI_CMD_RX_ENDIAN_EN;
|
||||
#endif
|
||||
|
||||
/* clear pause mode */
|
||||
reg_val &= ~SPI_CMD_PAUSE_EN;
|
||||
|
||||
/* set finish interrupt always enable */
|
||||
reg_val |= SPI_CMD_FINISH_IE_EN;
|
||||
|
||||
/* set pause interrupt always enable */
|
||||
reg_val |= SPI_CMD_PAUSE_IE_EN;
|
||||
|
||||
/* disable dma mode */
|
||||
reg_val &= ~(SPI_CMD_TX_DMA_EN | SPI_CMD_RX_DMA_EN);
|
||||
|
||||
/* set deassert mode */
|
||||
reg_val &= ~SPI_CMD_DEASSERT_EN;
|
||||
|
||||
write32(®s->spi_cmd_reg, reg_val);
|
||||
|
||||
mtk_spi_set_gpio_pinmux(pad_select);
|
||||
/* pad select */
|
||||
clrsetbits_le32(®s->spi_pad_macro_sel_reg, SPI_PAD_SEL_MASK,
|
||||
pad_select);
|
||||
}
|
||||
|
||||
static void mtk_spi_dump_data(const char *name, const uint8_t *data,
|
||||
int size)
|
||||
{
|
||||
#ifdef MTK_SPI_DEBUG
|
||||
int i;
|
||||
|
||||
printk(BIOS_DEBUG, "%s: 0x ", name);
|
||||
for (i = 0; i < size; i++)
|
||||
printk(BIOS_INFO, "%#x ", data[i]);
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static int spi_ctrlr_claim_bus(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
|
||||
setbits_le32(®s->spi_cmd_reg, 1 << SPI_CMD_PAUSE_EN_SHIFT);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_fifo_transfer(const struct spi_slave *slave, void *in,
|
||||
const void *out, size_t size)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
uint8_t *inb = (uint8_t *)in;
|
||||
const uint32_t *outb = (const uint32_t *)out;
|
||||
uint32_t reg_val = 0;
|
||||
uint32_t i, word_count;
|
||||
struct stopwatch sw;
|
||||
|
||||
if (!size || size > MTK_FIFO_DEPTH)
|
||||
return -1;
|
||||
|
||||
clrsetbits_le32(®s->spi_cfg1_reg,
|
||||
SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK,
|
||||
((size - 1) << SPI_CFG1_PACKET_LENGTH_SHIFT) |
|
||||
(0 << SPI_CFG1_PACKET_LOOP_SHIFT));
|
||||
|
||||
word_count = div_round_up(size, sizeof(u32));
|
||||
if (inb) {
|
||||
/* The SPI controller will transmit in full-duplex for RX,
|
||||
* therefore we need arbitrary data on MOSI which the slave
|
||||
* must ignore.
|
||||
*/
|
||||
for (i = 0; i < word_count; i++)
|
||||
write32(®s->spi_tx_data_reg, MTK_ARBITRARY_VALUE);
|
||||
}
|
||||
if (outb) {
|
||||
for (i = 0; i < word_count; i++)
|
||||
write32(®s->spi_tx_data_reg, outb[i]);
|
||||
mtk_spi_dump_data("the outb data is",
|
||||
(const uint8_t *)outb, size);
|
||||
}
|
||||
|
||||
if (mtk_slave->state == MTK_SPI_IDLE) {
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_ACT_EN);
|
||||
mtk_slave->state = MTK_SPI_PAUSE_IDLE;
|
||||
} else if (mtk_slave->state == MTK_SPI_PAUSE_IDLE) {
|
||||
setbits_le32(®s->spi_cmd_reg, SPI_CMD_RESUME_EN);
|
||||
}
|
||||
|
||||
stopwatch_init_usecs_expire(&sw, MTK_TXRX_TIMEOUT_US);
|
||||
while ((read32(®s->spi_status1_reg) & MTK_SPI_BUSY_STATUS) == 0) {
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR,
|
||||
"Timeout waiting for status1 status.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
stopwatch_init_usecs_expire(&sw, MTK_TXRX_TIMEOUT_US);
|
||||
while ((read32(®s->spi_status0_reg) &
|
||||
MTK_SPI_PAUSE_FINISH_INT_STATUS) == 0) {
|
||||
if (stopwatch_expired(&sw)) {
|
||||
printk(BIOS_ERR,
|
||||
"Timeout waiting for status0 status.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (inb) {
|
||||
for (i = 0; i < size; i++) {
|
||||
if (i % 4 == 0)
|
||||
reg_val = read32(®s->spi_rx_data_reg);
|
||||
*(inb + i) = (reg_val >> ((i % 4) * 8)) & 0xff;
|
||||
}
|
||||
mtk_spi_dump_data("the inb data is", inb, size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
spi_sw_reset(regs);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
|
||||
size_t bytes_out, void *din, size_t bytes_in)
|
||||
{
|
||||
size_t min_size = 0;
|
||||
int ret;
|
||||
|
||||
/* Driver implementation does not support full duplex. */
|
||||
if (bytes_in && bytes_out)
|
||||
return -1;
|
||||
|
||||
while (bytes_out || bytes_in) {
|
||||
if (bytes_in && bytes_out)
|
||||
min_size = MIN(MIN(bytes_out, bytes_in), MTK_FIFO_DEPTH);
|
||||
else if (bytes_out)
|
||||
min_size = MIN(bytes_out, MTK_FIFO_DEPTH);
|
||||
else if (bytes_in)
|
||||
min_size = MIN(bytes_in, MTK_FIFO_DEPTH);
|
||||
|
||||
ret = mtk_spi_fifo_transfer(slave, din, dout, min_size);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (bytes_out) {
|
||||
bytes_out -= min_size;
|
||||
dout = (const uint8_t *)dout + min_size;
|
||||
}
|
||||
|
||||
if (bytes_in) {
|
||||
bytes_in -= min_size;
|
||||
din = (uint8_t *)din + min_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_ctrlr_release_bus(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *mtk_slave = to_mtk_spi(slave);
|
||||
struct mtk_spi_regs *regs = mtk_slave->regs;
|
||||
|
||||
clrbits_le32(®s->spi_cmd_reg, SPI_CMD_PAUSE_EN);
|
||||
spi_sw_reset(regs);
|
||||
mtk_slave->state = MTK_SPI_IDLE;
|
||||
}
|
||||
|
||||
static int spi_ctrlr_setup(const struct spi_slave *slave)
|
||||
{
|
||||
struct mtk_spi_bus *eslave = to_mtk_spi(slave);
|
||||
assert(read32(&eslave->regs->spi_cfg0_reg) != 0);
|
||||
spi_sw_reset(eslave->regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_ctrlr spi_flash_ctrlr = {
|
||||
|
@ -305,19 +54,11 @@ static const struct spi_ctrlr spi_flash_ctrlr = {
|
|||
.flash_probe = mtk_spi_flash_probe,
|
||||
};
|
||||
|
||||
static const struct spi_ctrlr spi_ctrlr = {
|
||||
.setup = spi_ctrlr_setup,
|
||||
.claim_bus = spi_ctrlr_claim_bus,
|
||||
.release_bus = spi_ctrlr_release_bus,
|
||||
.xfer = spi_ctrlr_xfer,
|
||||
.max_xfer_size = 65535,
|
||||
};
|
||||
|
||||
const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
|
||||
{
|
||||
.ctrlr = &spi_ctrlr,
|
||||
.bus_start = 0,
|
||||
.bus_end = 0,
|
||||
.bus_end = SPI_BUS_NUMBER - 1,
|
||||
},
|
||||
{
|
||||
.ctrlr = &spi_flash_ctrlr,
|
||||
|
|
Loading…
Reference in New Issue