243 lines
5.2 KiB
C
243 lines
5.2 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <spi.h>
|
|
#include <asm/arch/mailbox.h>
|
|
|
|
#define MBSPI_DEFAULT_BUS 1
|
|
#define MBSPI_DEFAULT_CS 0
|
|
|
|
struct SpiBoardInfo {
|
|
uint32_t platform_data;
|
|
uint16_t bus_num;
|
|
uint16_t chip_select;
|
|
uint32_t max_speed_hz;
|
|
int32_t latency;
|
|
uint16_t mode;
|
|
uint8_t bits_per_word;
|
|
};
|
|
|
|
struct SpiTransfer {
|
|
uint32_t tx_buf;
|
|
uint32_t rx_buf;
|
|
uint32_t len;
|
|
uint32_t cs_change:1;
|
|
uint16_t delay_usecs;
|
|
uint32_t time_consump;
|
|
};
|
|
|
|
struct MbSpiTransfer {
|
|
uint8_t bus_num;
|
|
uint8_t chip_select;
|
|
uint8_t use_dma;
|
|
struct SpiTransfer xfer;
|
|
};
|
|
|
|
static inline void mbspi_invalidate_dcache(void *buf, int len)
|
|
{
|
|
flush_dcache_range((unsigned long)buf, (unsigned long)buf + len);
|
|
}
|
|
|
|
static inline void mbspi_flush_dcache(void *buf, int len)
|
|
{
|
|
invalidate_dcache_range((unsigned long)buf, (unsigned long)buf + len);
|
|
}
|
|
|
|
static int xSpiMemCmp(void *src1, void *src2, int len)
|
|
{
|
|
u8 *d1 = (u8 *)src1;
|
|
u8 *d2 = (u8 *)src2;
|
|
int i, diff = 0;
|
|
|
|
if (!d1 || !d2) {
|
|
printf("\tnull pointer, total %d\n", len);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (*d1 != *d2) {
|
|
printf("\t%d: 0x%x, 0x%x\n", i, *d1, *d2);
|
|
diff++;
|
|
}
|
|
d1++;
|
|
d2++;
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
|
|
static void vSpiMemSet(void *dest, u8 val, int len, u8 step)
|
|
{
|
|
u8 *d = (u8 *)dest;
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
*d++ = val & 0xff;
|
|
val += step;
|
|
}
|
|
}
|
|
|
|
static int __mbspi_dev(struct SpiBoardInfo *info)
|
|
{
|
|
int ret;
|
|
|
|
ret = scpi_send_data(AOCPU_REE_CHANNEL, CMD_SPI_DEV,
|
|
info, sizeof(*info),
|
|
NULL, 0);
|
|
return ret;
|
|
}
|
|
|
|
static int __mbspi_xfer(int bus_num, int chip_select, bool use_dma,
|
|
void *tx_buf, void *rx_buf, int len)
|
|
{
|
|
void *buf, *send_buf, *recv_buf;
|
|
uint32_t send_size, recv_size;
|
|
struct MbSpiTransfer *mbxfer;
|
|
struct SpiTransfer *t;
|
|
int xfer_ret, ret;
|
|
|
|
send_size = sizeof(*mbxfer) + ((!use_dma && tx_buf) ? len : 0);
|
|
recv_size = sizeof(xfer_ret) + ((!use_dma && rx_buf) ? len : 0);
|
|
buf = kcalloc(send_size + recv_size, 1, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
send_buf = buf;
|
|
recv_buf = (uint8_t *)send_buf + send_size;
|
|
mbxfer = (struct MbSpiTransfer *)send_buf;
|
|
mbxfer->bus_num = bus_num;
|
|
mbxfer->chip_select = chip_select;
|
|
mbxfer->use_dma = use_dma;
|
|
t = &mbxfer->xfer;
|
|
t->tx_buf = (uint32_t)(uint64_t)tx_buf;
|
|
t->rx_buf = (uint32_t)(uint64_t)rx_buf;
|
|
t->len = len;
|
|
|
|
if (use_dma) {
|
|
if (tx_buf)
|
|
mbspi_flush_dcache(tx_buf, len);
|
|
if (rx_buf)
|
|
mbspi_flush_dcache(rx_buf, len);
|
|
} else if (tx_buf) {
|
|
memcpy(&mbxfer[1], tx_buf, len);
|
|
}
|
|
|
|
ret = scpi_send_data(AOCPU_REE_CHANNEL, CMD_SPI_XFER,
|
|
send_buf, send_size,
|
|
recv_buf, recv_size);
|
|
|
|
xfer_ret = *(int *)recv_buf;
|
|
if (use_dma) {
|
|
if (tx_buf)
|
|
mbspi_invalidate_dcache(tx_buf, len);
|
|
if (rx_buf)
|
|
mbspi_invalidate_dcache(rx_buf, len);
|
|
} else if (rx_buf) {
|
|
memcpy(rx_buf, (uint8_t *)recv_buf + sizeof(xfer_ret), len);
|
|
}
|
|
|
|
kfree(buf);
|
|
|
|
return ret ? ret : xfer_ret;
|
|
}
|
|
|
|
int do_mbspi_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
struct SpiBoardInfo *info;
|
|
char *cp;
|
|
struct SpiBoardInfo mbspi_info = {
|
|
.platform_data = 0,
|
|
.bus_num = MBSPI_DEFAULT_BUS,
|
|
.chip_select = MBSPI_DEFAULT_CS,
|
|
.max_speed_hz = 10000000,
|
|
.mode = SPI_MODE_3 | SPI_LOOP,
|
|
.bits_per_word = 8,
|
|
.latency = 0,
|
|
};
|
|
|
|
info = &mbspi_info;
|
|
if (argc >= 2) {
|
|
info->bus_num = simple_strtoul(argv[1], &cp, 10);
|
|
if (*cp == ':')
|
|
info->chip_select = simple_strtoul(cp + 1, &cp, 10);
|
|
if (*cp == '.')
|
|
info->mode = simple_strtoul(cp + 1, &cp, 16);
|
|
if (*cp == '.')
|
|
info->max_speed_hz = simple_strtoul(cp + 1, &cp, 10);
|
|
if (*cp == '.')
|
|
info->bits_per_word = simple_strtoul(cp + 1, &cp, 10);
|
|
}
|
|
if (argc >= 3)
|
|
info->latency = simple_strtoul(argv[2], NULL, 10);
|
|
|
|
__mbspi_dev(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_mbspi_xfer(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
uint8_t bus_num = MBSPI_DEFAULT_BUS;
|
|
uint8_t cs = MBSPI_DEFAULT_CS;
|
|
uint8_t use_dma = 0;
|
|
int len = 32;
|
|
uchar *tx_buf, *rx_buf;
|
|
uchar val = 1;
|
|
char *cp;
|
|
int diff;
|
|
int ret;
|
|
|
|
if (argc >= 2) {
|
|
bus_num = simple_strtoul(argv[1], &cp, 10);
|
|
if (*cp == ':')
|
|
cs = simple_strtoul(cp + 1, &cp, 10);
|
|
if (*cp == '.')
|
|
use_dma = !!simple_strtoul(cp + 1, &cp, 16);
|
|
}
|
|
|
|
if (argc >= 3)
|
|
len = simple_strtoul(argv[2], NULL, 10);
|
|
|
|
if (argc >= 4)
|
|
val = simple_strtoul(argv[3], NULL, 16);
|
|
|
|
tx_buf = kcalloc(len, 2, GFP_KERNEL);
|
|
if (!tx_buf) {
|
|
printf("%s: alloc failed\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rx_buf = tx_buf + len;
|
|
vSpiMemSet(tx_buf, val, len, 1);
|
|
ret = __mbspi_xfer(bus_num, cs, use_dma, tx_buf, rx_buf, len);
|
|
if (ret) {
|
|
printf("mbspi xfer failed %d\n", ret);
|
|
} else {
|
|
diff = xSpiMemCmp((void *)tx_buf, (void *)rx_buf, len);
|
|
printf("total %d, diff %d\n", len, diff);
|
|
}
|
|
|
|
kfree(tx_buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************/
|
|
U_BOOT_CMD(mbspi_dev, 3, 1, do_mbspi_dev,
|
|
"Mailbox SPI utility command",
|
|
"[<bus>:]<cs>[.<mode>.<speed>.<bw>] [latency] - New a spi device\n"
|
|
);
|
|
|
|
U_BOOT_CMD(mbspi_xfer, 4, 1, do_mbspi_xfer,
|
|
"Mailbox SPI utility command",
|
|
"[<bus>:]<cs>[.<use_dma>] [<len> <dout>] - Send and receive bits\n"
|
|
"<len> - bytes send (base 10)\n"
|
|
"<dout> - Hexadecimal string that gets sent"
|
|
);
|