Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
813 lines
21 KiB
C
813 lines
21 KiB
C
/*
|
|
* Copyright (C) 2016 MediaTek Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
|
|
*/
|
|
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/platform_data/spi-mt65xx.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/clk.h>
|
|
|
|
#if 0
|
|
/* #ifdef CONFIG_TRUSTONIC_TEE_SUPPORT */
|
|
#define SPI_TRUSTONIC_TEE_SUPPORT
|
|
#endif
|
|
|
|
#ifdef SPI_TRUSTONIC_TEE_SUPPORT
|
|
#include <mobicore_driver_api.h>
|
|
#include <tlspi_Api.h>
|
|
#endif
|
|
|
|
static struct spi_device *spi_test;
|
|
u32 speed = 10000000;
|
|
|
|
struct mtk_spi {
|
|
void __iomem *base;
|
|
u32 state;
|
|
int pad_num;
|
|
u32 *pad_sel;
|
|
struct clk *parent_clk, *sel_clk, *spi_clk;
|
|
struct spi_transfer *cur_transfer;
|
|
u32 xfer_len;
|
|
u32 num_xfered;
|
|
struct scatterlist *tx_sgl, *rx_sgl;
|
|
u32 tx_sgl_len, rx_sgl_len;
|
|
const struct mtk_spi_compatible *dev_comp;
|
|
};
|
|
|
|
static struct mtk_chip_config mtk_test_chip_info = {
|
|
.rx_mlsb = 0,
|
|
.tx_mlsb = 0,
|
|
.sample_sel = 0,
|
|
|
|
.cs_setuptime = 0,
|
|
.cs_holdtime = 0,
|
|
.cs_idletime = 0,
|
|
.deassert_mode = 0,
|
|
.tick_delay = 0,
|
|
};
|
|
|
|
#define SPI_CFG0_REG 0x0000
|
|
#define SPI_CFG1_REG 0x0004
|
|
#define SPI_TX_SRC_REG 0x0008
|
|
#define SPI_RX_DST_REG 0x000c
|
|
#define SPI_TX_DATA_REG 0x0010
|
|
#define SPI_RX_DATA_REG 0x0014
|
|
#define SPI_CMD_REG 0x0018
|
|
#define SPI_STATUS0_REG 0x001c
|
|
#define SPI_STATUS1_REG 0x0020
|
|
#define SPI_PAD_SEL_REG 0x0024
|
|
#define SPI_CFG2_REG 0x0028
|
|
#define SPI_TX_SRC_REG_64 0x002c
|
|
#define SPI_RX_DST_REG_64 0x0030
|
|
|
|
#define SPI_CFG1_CS_IDLE_OFFSET 0
|
|
#define SPI_CFG1_GET_TICK_DLY_OFFSET 29
|
|
|
|
#ifdef SPI_TRUSTONIC_TEE_SUPPORT
|
|
#define DEFAULT_HANDLES_NUM (64)
|
|
#define MAX_OPEN_SESSIONS (0xffffffff - 1)
|
|
/*
|
|
* Trustlet UUID.
|
|
*/
|
|
u8 spi_uuid[10][16] = {
|
|
{0x09, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00},
|
|
{0x09, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00} };
|
|
|
|
static struct mc_session_handle secspi_session = { 0 };
|
|
|
|
static u32 secspi_session_ref;
|
|
static u32 secspi_devid = MC_DEVICE_ID_DEFAULT;
|
|
static tciSpiMessage_t *secspi_tci;
|
|
|
|
static DEFINE_MUTEX(secspi_lock);
|
|
|
|
int secspi_session_open(u32 spinum)
|
|
{
|
|
enum mc_result mc_ret = MC_DRV_OK;
|
|
|
|
mutex_lock(&secspi_lock);
|
|
|
|
|
|
pr_info("%s() start\n", __func__);
|
|
do {
|
|
/* sessions reach max numbers ? */
|
|
|
|
if (secspi_session_ref > MAX_OPEN_SESSIONS) {
|
|
pr_notice("%s() err: secspi_session(0x%x)>MAX(0x%x)\n",
|
|
__func__, secspi_session_ref, MAX_OPEN_SESSIONS);
|
|
break;
|
|
}
|
|
|
|
if (secspi_session_ref > 0) {
|
|
secspi_session_ref++;
|
|
break;
|
|
}
|
|
|
|
/* open device */
|
|
mc_ret = mc_open_device(secspi_devid);
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_open_device failed: %d\n", __func__,
|
|
mc_ret);
|
|
break;
|
|
}
|
|
|
|
/* allocating WSM for DCI */
|
|
mc_ret = mc_malloc_wsm(secspi_devid, 0,
|
|
sizeof(tciSpiMessage_t), (uint8_t **) &secspi_tci, 0);
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_malloc_wsm failed: %d\n", __func__,
|
|
mc_ret);
|
|
mc_close_device(secspi_devid);
|
|
break;
|
|
}
|
|
|
|
/* open session */
|
|
secspi_session.device_id = secspi_devid;
|
|
mc_ret = mc_open_session(&secspi_session,
|
|
(struct mc_uuid_t *)&spi_uuid[spinum][0],
|
|
(uint8_t *)secspi_tci, sizeof(tciSpiMessage_t));
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_open_session fail: %d\n", __func__,
|
|
mc_ret);
|
|
mc_free_wsm(secspi_devid, (uint8_t *) secspi_tci);
|
|
mc_close_device(secspi_devid);
|
|
secspi_tci = NULL;
|
|
break;
|
|
}
|
|
secspi_session_ref = 1;
|
|
|
|
} while (0);
|
|
|
|
mutex_unlock(&secspi_lock);
|
|
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() Done. Fail! ret=%d, ref=%d\n", __func__,
|
|
mc_ret, secspi_session_ref);
|
|
return -ENXIO;
|
|
}
|
|
|
|
pr_info("%s() Done. Success! ret=%d, ref=%d\n", __func__, mc_ret,
|
|
secspi_session_ref);
|
|
return 0;
|
|
}
|
|
|
|
static int secspi_session_close(void)
|
|
{
|
|
enum mc_result mc_ret = MC_DRV_OK;
|
|
|
|
mutex_lock(&secspi_lock);
|
|
|
|
do {
|
|
/* session is already closed ? */
|
|
if (secspi_session_ref == 0) {
|
|
pr_notice("%s() spi_session already closed\n",
|
|
__func__);
|
|
break;
|
|
}
|
|
|
|
if (secspi_session_ref > 1) {
|
|
secspi_session_ref--;
|
|
break;
|
|
}
|
|
|
|
/* close session */
|
|
mc_ret = mc_close_session(&secspi_session);
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_close_session failed: %d\n",
|
|
__func__, mc_ret);
|
|
break;
|
|
}
|
|
|
|
/* free WSM for DCI */
|
|
mc_ret = mc_free_wsm(secspi_devid, (uint8_t *) secspi_tci);
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_free_wsm failed: %d\n", __func__,
|
|
mc_ret);
|
|
break;
|
|
}
|
|
secspi_tci = NULL;
|
|
secspi_session_ref = 0;
|
|
|
|
/* close device */
|
|
mc_ret = mc_close_device(secspi_devid);
|
|
if (mc_ret != MC_DRV_OK)
|
|
pr_notice("%s() mc_close_device failed: %d\n",
|
|
__func__, mc_ret);
|
|
|
|
} while (0);
|
|
|
|
mutex_unlock(&secspi_lock);
|
|
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() Done. Fail! ret=%d, ref=%d\n", __func__,
|
|
mc_ret, secspi_session_ref);
|
|
return -ENXIO;
|
|
}
|
|
|
|
pr_info("%s() Done. Success! ret=%d, ref=%d\n", __func__, mc_ret,
|
|
secspi_session_ref);
|
|
return 0;
|
|
|
|
}
|
|
|
|
void secspi_enable_clk(struct spi_device *spidev)
|
|
{
|
|
int ret;
|
|
struct spi_master *master;
|
|
struct mtk_spi *ms;
|
|
|
|
master = spidev->master;
|
|
ms = spi_master_get_devdata(master);
|
|
/*
|
|
* prepare the clock source
|
|
*/
|
|
ret = clk_prepare_enable(ms->spi_clk);
|
|
}
|
|
|
|
int secspi_execute(u32 cmd, tciSpiMessage_t *param)
|
|
{
|
|
enum mc_result mc_ret;
|
|
|
|
pr_info("%s() start.\n", __func__);
|
|
mutex_lock(&secspi_lock);
|
|
|
|
if (secspi_tci == NULL) {
|
|
mutex_unlock(&secspi_lock);
|
|
pr_notice("%s() secspi_tci not exist\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*set transfer data para */
|
|
if (param == NULL) {
|
|
pr_notice("%s() parameter is NULL !!\n", __func__);
|
|
} else {
|
|
secspi_tci->tx_buf = param->tx_buf;
|
|
secspi_tci->rx_buf = param->rx_buf;
|
|
secspi_tci->len = param->len;
|
|
secspi_tci->is_dma_used = param->is_dma_used;
|
|
secspi_tci->tx_dma = param->tx_dma;
|
|
secspi_tci->rx_dma = param->rx_dma;
|
|
secspi_tci->tl_chip_config = param->tl_chip_config;
|
|
}
|
|
|
|
secspi_tci->cmd_spi.header.commandId = (tciCommandId_t) cmd;
|
|
secspi_tci->cmd_spi.len = 0;
|
|
|
|
/* enable_clock(MT_CG_PERI_SPI0, "spi"); */
|
|
/* enable_clk(ms); */
|
|
|
|
mc_ret = mc_notify(&secspi_session);
|
|
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() mc_notify failed: %d", __func__, mc_ret);
|
|
goto exit;
|
|
}
|
|
|
|
mc_ret = mc_wait_notification(&secspi_session, -1);
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() SPI mc_wait_notification failed: %d", __func__,
|
|
mc_ret);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
mutex_unlock(&secspi_lock);
|
|
|
|
if (mc_ret != MC_DRV_OK) {
|
|
pr_notice("%s() Done. Fail. ret:%d\n", __func__, mc_ret);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
pr_info("%s() Done. Success. ret:%d\n", __func__, mc_ret);
|
|
return 0;
|
|
}
|
|
|
|
/*used for REE to detach IRQ of TEE*/
|
|
void spi_detach_irq_tee(u32 spinum)
|
|
{
|
|
secspi_session_open(spinum);
|
|
secspi_execute(2, NULL);
|
|
pr_info("%s() Done.\n", __func__);
|
|
}
|
|
#endif
|
|
|
|
void mt_spi_disable_master_clk(struct spi_device *spidev)
|
|
{
|
|
struct mtk_spi *ms;
|
|
|
|
ms = spi_master_get_devdata(spidev->master);
|
|
|
|
clk_disable_unprepare(ms->spi_clk);
|
|
}
|
|
EXPORT_SYMBOL(mt_spi_disable_master_clk);
|
|
|
|
void mt_spi_enable_master_clk(struct spi_device *spidev)
|
|
{
|
|
int ret;
|
|
struct mtk_spi *ms;
|
|
|
|
ms = spi_master_get_devdata(spidev->master);
|
|
|
|
ret = clk_prepare_enable(ms->spi_clk);
|
|
}
|
|
EXPORT_SYMBOL(mt_spi_enable_master_clk);
|
|
|
|
static int spi_setup_xfer(struct device *dev, struct spi_transfer *xfer,
|
|
u32 len, u32 flag)
|
|
{
|
|
u32 tx_buffer = 0x12345678;
|
|
u32 cnt, i;
|
|
|
|
xfer->speed_hz = speed;
|
|
pr_info("%s() flag:%d speed:%d\n", __func__, flag, xfer->speed_hz);
|
|
|
|
/* Instead of using kzalloc, if using below
|
|
* dma_alloc_coherent to allocate tx_dma/rx_dma, remember:
|
|
* 1. remove kfree in spi_recv_check_all, otherwise KE reboot
|
|
* 2. set msg.is_dma_mapped = 1 before calling spi_sync();
|
|
*/
|
|
|
|
if ((xfer->tx_buf == NULL) || (xfer->rx_buf == NULL))
|
|
return -1;
|
|
|
|
cnt = (len%4)?(len/4 + 1):(len/4);
|
|
|
|
if (flag == 0)
|
|
for (i = 0; i < cnt; i++)
|
|
*((u32 *)xfer->tx_buf + i) = tx_buffer;
|
|
else if (flag == 1)
|
|
for (i = 0; i < cnt; i++)
|
|
*((u32 *)xfer->tx_buf + i) = tx_buffer + i;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int spi_recv_check(struct spi_device *spi, struct spi_message *msg)
|
|
{
|
|
struct mtk_chip_config *chip_config;
|
|
struct spi_transfer *xfer;
|
|
u32 i, err = 0;
|
|
int j;
|
|
u8 t, rec_cac;
|
|
|
|
chip_config = (struct mtk_chip_config *) spi->controller_data;
|
|
|
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
if (!xfer) {
|
|
pr_notice("%s() rv msg is NULL.\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
pr_info("%s xfer->len:%d chip_config->tx_mlsb:%d rx_mlsb:%d\n",
|
|
__func__, xfer->len, chip_config->tx_mlsb,
|
|
chip_config->rx_mlsb);
|
|
for (i = 0; i < xfer->len; i++) {
|
|
if (chip_config->tx_mlsb ^ chip_config->rx_mlsb) {
|
|
rec_cac = 0;
|
|
for (j = 7; j >= 0; j--) {
|
|
t = *((u8 *)xfer->tx_buf + i) & (1<<j);
|
|
t = (t >> j) << (7-j);
|
|
rec_cac |= t;
|
|
}
|
|
} else
|
|
rec_cac = *((u8 *) xfer->tx_buf + i);
|
|
|
|
if (*((u8 *) xfer->rx_buf + i) != rec_cac) {
|
|
pr_notice("%s() tx xfer %d is:%x\n", __func__,
|
|
i, *((u8 *) xfer->tx_buf + i));
|
|
pr_notice("%s() rx xfer %d is:%x\n", __func__,
|
|
i, *((u8 *) xfer->rx_buf + i));
|
|
err++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pr_info("%s() Done. error %d,actual xfer len:%d\n", __func__, err,
|
|
msg->actual_length);
|
|
|
|
return err;
|
|
}
|
|
|
|
static ssize_t spi_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct spi_device *spi;
|
|
struct mtk_spi *mdata = NULL;
|
|
struct mtk_chip_config *chip_config;
|
|
u32 cs_idletime, pad_sel;
|
|
int cpol, cpha, tx_mlsb, rx_mlsb;
|
|
int sample_sel, tckdly;
|
|
u32 reg_val;
|
|
int dump_all, dump;
|
|
int index;
|
|
#if 0
|
|
/* #ifdef CONFIG_TRUSTONIC_TEE_SUPPORT */
|
|
u32 spinum;
|
|
#endif
|
|
|
|
spi = container_of(dev, struct spi_device, dev);
|
|
pr_info("%s() SPIDEV name is:%s\n", __func__, spi->modalias);
|
|
|
|
chip_config = (struct mtk_chip_config *) spi->controller_data;
|
|
|
|
if (!chip_config) {
|
|
pr_notice("%s() chip_config is NULL.\n", __func__);
|
|
chip_config = kzalloc(sizeof(struct mtk_chip_config),
|
|
GFP_KERNEL);
|
|
if (!chip_config)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (!buf) {
|
|
pr_notice("%s() buf is NULL.Exit!\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
#if 0
|
|
/* #ifdef CONFIG_TRUSTONIC_TEE_SUPPORT */
|
|
if (!strncmp(buf, "send", 4)) {
|
|
if (sscanf(buf + 4, "%d", &spinum) == 1) {
|
|
pr_info("%s() start to access TL SPI driver.\n",
|
|
__func__);
|
|
secspi_session_open(spinum);
|
|
secspi_enable_clk(spi);
|
|
secspi_execute(1, NULL);
|
|
secspi_session_close();
|
|
pr_info("%s() secspi_execute 1 finished!\n", __func__);
|
|
}
|
|
} else if (!strncmp(buf, "config", 6)) {
|
|
if (sscanf(buf + 6, "%d", &spinum) == 1) {
|
|
pr_info("start to access TL SPI driver.\n");
|
|
secspi_session_open(spinum);
|
|
secspi_execute(2, NULL);
|
|
secspi_session_close();
|
|
pr_info("secspi_execute 2 finished!!!\n");
|
|
}
|
|
} else if (!strncmp(buf, "debug", 5)) {
|
|
if (sscanf(buf + 5, "%d", &spinum) == 1) {
|
|
pr_info("start to access TL SPI driver.\n");
|
|
secspi_session_open(spinum);
|
|
secspi_execute(3, NULL);
|
|
secspi_session_close();
|
|
pr_info("secspi_execute 3 finished!!!\n");
|
|
}
|
|
} else if (!strncmp(buf, "test", 4)) {
|
|
if (sscanf(buf + 4, "%d", &spinum) == 1) {
|
|
pr_info("start to access TL SPI driver.\n");
|
|
secspi_session_open(spinum);
|
|
secspi_execute(4, NULL);
|
|
secspi_session_close();
|
|
pr_info("secspi_execute 4 finished!!!\n");
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strncmp(buf, "-w", 2))
|
|
goto set;
|
|
else
|
|
goto out;
|
|
|
|
set:
|
|
buf += 3;
|
|
mdata = spi_master_get_devdata(spi->master);
|
|
mt_spi_enable_master_clk(spi);
|
|
if (!strncmp(buf, "setuptime=", 10))
|
|
pr_info("%s() setuptime cannot be set by cmd.\n", __func__);
|
|
else if (!strncmp(buf, "holdtime=", 9))
|
|
pr_info("%s() holdtime cannot be set by cmd.\n", __func__);
|
|
else if (!strncmp(buf, "high_time=", 10))
|
|
pr_info("%s() high_time cannot be set by cmd.\n", __func__);
|
|
else if (!strncmp(buf, "low_time=", 9))
|
|
pr_info("%s() low_time cannot be set by cmd.\n", __func__);
|
|
else if (!strncmp(buf, "tckdly=", 7)) {
|
|
if (sscanf(buf+7, "%d", &tckdly) == 1) {
|
|
pr_info("%s() set tckdly=%d to SPI_CFG1_REG.\n",
|
|
__func__, tckdly);
|
|
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
|
reg_val &= 0x1FFFFFFF;
|
|
reg_val |= (tckdly << SPI_CFG1_GET_TICK_DLY_OFFSET);
|
|
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
|
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
|
pr_info("%s() now SPI_CFG1_REG=0x%x.\n", __func__,
|
|
reg_val);
|
|
}
|
|
} else if (!strncmp(buf, "cs_idletime=", 12)) {
|
|
if (sscanf(buf+12, "%d", &cs_idletime) == 1) {
|
|
pr_info("%s() set cs_idletime=%d to SPI_CFG1_REG\n",
|
|
__func__, cs_idletime);
|
|
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
|
reg_val &= 0xFFFFFF00;
|
|
reg_val |= (cs_idletime << SPI_CFG1_CS_IDLE_OFFSET);
|
|
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
|
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
|
pr_info("%s() now SPI_CFG1_REG=0x%x.\n", __func__,
|
|
reg_val);
|
|
}
|
|
} else if (!strncmp(buf, "cpol=", 5)) {
|
|
if (sscanf(buf+5, "%d", &cpol) == 1) {
|
|
pr_info("%s() Set cpol=%d to spi->mode.\n", __func__,
|
|
cpol);
|
|
spi->mode &= 0xFD;
|
|
spi->mode |= cpol<<1;
|
|
pr_info("%s() mow spi->mode:0x%.4x\n", __func__,
|
|
spi->mode);
|
|
}
|
|
} else if (!strncmp(buf, "cpha=", 5)) {
|
|
if (sscanf(buf+5, "%d", &cpha) == 1) {
|
|
pr_info("%s() Set cpha=%d to spi->mode.\n", __func__,
|
|
cpha);
|
|
spi->mode &= 0xFE;
|
|
spi->mode |= cpha;
|
|
pr_info("%s() mow spi->mode:0x%.4x\n", __func__,
|
|
spi->mode);
|
|
}
|
|
} else if (!strncmp(buf, "tx_mlsb=", 8)) {
|
|
if (sscanf(buf+8, "%d", &tx_mlsb) == 1) {
|
|
pr_info("%s() Set tx_mlsb=%d to chip_config\n",
|
|
__func__, tx_mlsb);
|
|
chip_config->tx_mlsb = tx_mlsb;
|
|
}
|
|
} else if (!strncmp(buf, "rx_mlsb=", 8)) {
|
|
if (sscanf(buf+8, "%d", &rx_mlsb) == 1) {
|
|
pr_info("%s() Set rx_mlsb=%d to chip_config\n",
|
|
__func__, rx_mlsb);
|
|
chip_config->rx_mlsb = rx_mlsb;
|
|
}
|
|
} else if (!strncmp(buf, "tx_endian=", 10))
|
|
pr_info("%s() tx_endian cannot be set.See __LTTTLE_ENDIAN\n",
|
|
__func__);
|
|
else if (!strncmp(buf, "rx_endian=", 10))
|
|
pr_info("%s() rx_endian cannot be set.See __LTTTLE_ENDIAN\n",
|
|
__func__);
|
|
else if (!strncmp(buf, "pause=", 6))
|
|
pr_info("%s() pause cannot be set due to sw auto select\n",
|
|
__func__);
|
|
else if (!strncmp(buf, "deassert=", 9))
|
|
pr_info("%s() deassert cannot be set, sw default disable\n",
|
|
__func__);
|
|
else if (!strncmp(buf, "sample_sel=", 11)) {
|
|
if (sscanf(buf + 11, "%d", &sample_sel) == 1) {
|
|
pr_info("%s() Set sample_sel=%d to chip_config\n",
|
|
__func__, sample_sel);
|
|
chip_config->sample_sel = sample_sel;
|
|
}
|
|
} else if (!strncmp(buf, "pad_sel=", 8)) {
|
|
if (sscanf(buf + 8, "%d", &pad_sel) == 1) {
|
|
pr_info("%s() Set pad select=%d to mdata->pad_sel.\n",
|
|
__func__, pad_sel);
|
|
mdata->pad_sel[spi->chip_select] = pad_sel;
|
|
}
|
|
} else if (!strncmp(buf, "speed=", 6)) {
|
|
if (sscanf(buf + 6, "%d", &speed) == 1)
|
|
pr_info("%s() Set speed=%d as global parameter.\n",
|
|
__func__, speed);
|
|
} else if (!strncmp(buf, "dump_all=", 9)) {
|
|
if (sscanf(buf + 9, "%d", &dump_all) == 1) {
|
|
pr_info("%s() dump all register.\n", __func__);
|
|
pr_info("||* spi_dump_reg *******************||\n");
|
|
pr_info("cfg0:0x%.8x\n",
|
|
readl(mdata->base + SPI_CFG0_REG));
|
|
pr_info("cfg1:0x%.8x\n",
|
|
readl(mdata->base + SPI_CFG1_REG));
|
|
pr_info("tx_s:0x%.8x\n",
|
|
readl(mdata->base + SPI_TX_SRC_REG));
|
|
pr_info("rx_d:0x%.8x\n",
|
|
readl(mdata->base + SPI_RX_DST_REG));
|
|
pr_info("tx_data:0x%.8x\n",
|
|
readl(mdata->base + SPI_TX_DATA_REG));
|
|
pr_info("rx_data:0x%.8x\n",
|
|
readl(mdata->base + SPI_RX_DATA_REG));
|
|
pr_info("cmd :0x%.8x\n",
|
|
readl(mdata->base + SPI_CMD_REG));
|
|
pr_info("status0:0x%.8x\n",
|
|
readl(mdata->base + SPI_STATUS0_REG));
|
|
pr_info("status1:0x%.8x\n",
|
|
readl(mdata->base + SPI_STATUS1_REG));
|
|
pr_info("pad_sel:0x%.8x\n",
|
|
readl(mdata->base + SPI_PAD_SEL_REG));
|
|
pr_info("cfg2:0x%.8x\n",
|
|
readl(mdata->base + SPI_CFG2_REG));
|
|
pr_info("tx_s64:0x%.8x\n",
|
|
readl(mdata->base + SPI_TX_SRC_REG_64));
|
|
pr_info("rx_d64:0x%.8x\n",
|
|
readl(mdata->base + SPI_RX_DST_REG_64));
|
|
pr_info("||*****************************************||\n");
|
|
}
|
|
} else if (!strncmp(buf, "dump=", 5)) {
|
|
if (sscanf(buf + 5, "%d", &dump) == 1) {
|
|
pr_info("%s() dump TX FIFO.\n", __func__);
|
|
for (index = 1; index <= 8; index++)
|
|
pr_info("%d tx_data:0x%.8x\n",
|
|
index, readl(mdata->base + SPI_TX_DATA_REG));
|
|
pr_info("%s() dump RX FIFO.\n", __func__);
|
|
for (index = 1; index <= 8; index++)
|
|
pr_info("%d rx_data:0x%.8x\n",
|
|
index, readl(mdata->base + SPI_RX_DATA_REG));
|
|
}
|
|
} else {
|
|
pr_notice("%s() Wrong parameters.Do nothing.\n", __func__);
|
|
mt_spi_disable_master_clk(spi);
|
|
goto out;
|
|
}
|
|
mt_spi_disable_master_clk(spi);
|
|
out:
|
|
if (!spi->controller_data)
|
|
kfree(chip_config);
|
|
return count;
|
|
}
|
|
|
|
static ssize_t
|
|
spi_msg_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret = 0;
|
|
struct spi_device *spi;
|
|
|
|
struct spi_transfer transfer = {0,};
|
|
struct spi_message msg;
|
|
u32 len = 4;
|
|
|
|
spi = container_of(dev, struct spi_device, dev);
|
|
|
|
if (unlikely(!spi)) {
|
|
pr_notice("%s() spi device is invalid\n", __func__);
|
|
goto out;
|
|
}
|
|
if (unlikely(!buf)) {
|
|
pr_notice("%s() buf is invalid\n", __func__);
|
|
goto out;
|
|
}
|
|
spi_message_init(&msg);
|
|
|
|
if (strncmp(buf, "-func", 5)) {
|
|
pr_notice("%s() Wrong parameters.Do nothing.\n", __func__);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
buf += 6;
|
|
if (!strncmp(buf, "len=", 4) && 1 == sscanf(buf + 4, "%d", &len)) {
|
|
pr_info("%s() start transfer,dataLen=%d.-----------------\n",
|
|
__func__, len);
|
|
transfer.len = len;
|
|
transfer.tx_buf = kzalloc(len, GFP_KERNEL);
|
|
transfer.rx_buf = kzalloc(len, GFP_KERNEL);
|
|
pr_info("%s() transfer.tx_buf:%p, transfer.rx_buf:%p\n",
|
|
__func__, transfer.tx_buf, transfer.rx_buf);
|
|
|
|
spi_setup_xfer(&spi->dev, &transfer, len, 1);
|
|
spi_message_add_tail(&transfer, &msg);
|
|
ret = spi_sync(spi, &msg);
|
|
if (ret < 0) {
|
|
pr_notice("%s() spi_sync err:%d\n", __func__, ret);
|
|
} else {
|
|
ret = spi_recv_check(spi, &msg);
|
|
kfree(transfer.tx_buf);
|
|
kfree(transfer.rx_buf);
|
|
if (ret != 0) {
|
|
ret = -ret;
|
|
pr_notice("%s Message transfer err:%d\n",
|
|
__func__, ret);
|
|
goto out;
|
|
}
|
|
}
|
|
pr_info("%s() spi transfer Done.-----------------\n",
|
|
__func__);
|
|
}
|
|
|
|
ret = count;
|
|
|
|
out:
|
|
return count;
|
|
//return ret;
|
|
|
|
}
|
|
|
|
static DEVICE_ATTR(spi, 0200, NULL, spi_store);
|
|
static DEVICE_ATTR(spi_msg, 0200, NULL, spi_msg_store);
|
|
|
|
static struct device_attribute *spi_attribute[] = {
|
|
&dev_attr_spi,
|
|
&dev_attr_spi_msg,
|
|
};
|
|
|
|
static int spi_create_attribute(struct device *dev)
|
|
{
|
|
int num, idx;
|
|
int err = 0;
|
|
|
|
num = (int)ARRAY_SIZE(spi_attribute);
|
|
for (idx = 0; idx < num; idx++) {
|
|
err = device_create_file(dev, spi_attribute[idx]);
|
|
if (err)
|
|
break;
|
|
}
|
|
return err;
|
|
|
|
}
|
|
|
|
static void spi_remove_attribute(struct device *dev)
|
|
{
|
|
int num, idx;
|
|
|
|
num = (int)ARRAY_SIZE(spi_attribute);
|
|
for (idx = 0; idx < num; idx++)
|
|
device_remove_file(dev, spi_attribute[idx]);
|
|
}
|
|
|
|
static int spi_test_remove(struct spi_device *spi)
|
|
{
|
|
|
|
pr_info("%s().\n", __func__);
|
|
spi_remove_attribute(&spi->dev);
|
|
return 0;
|
|
}
|
|
|
|
static int spi_test_probe(struct spi_device *spi)
|
|
{
|
|
pr_info("%s() enter.\n", __func__);
|
|
spi_test = spi;
|
|
spi->mode = SPI_MODE_3;
|
|
spi->bits_per_word = 8;
|
|
spi->controller_data = (void *)&mtk_test_chip_info;
|
|
return spi_create_attribute(&spi->dev);
|
|
}
|
|
|
|
struct spi_device_id spi_id_table[] = {
|
|
{"spi-ut", 0},
|
|
{},
|
|
};
|
|
|
|
static const struct of_device_id spidev_dt_ids[] = {
|
|
{ .compatible = "mediatek,spi-mt65xx-test" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
|
|
|
static struct spi_driver spi_test_driver = {
|
|
.driver = {
|
|
.name = "test_spi",
|
|
.bus = &spi_bus_type,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = spidev_dt_ids,
|
|
},
|
|
.probe = spi_test_probe,
|
|
.remove = spi_test_remove,
|
|
.id_table = spi_id_table,
|
|
};
|
|
|
|
static int __init spi_dev_init(void)
|
|
{
|
|
pr_info("%s().\n", __func__);
|
|
return spi_register_driver(&spi_test_driver);
|
|
}
|
|
|
|
static void __exit spi_test_exit(void)
|
|
{
|
|
pr_info("%s().\n", __func__);
|
|
spi_unregister_driver(&spi_test_driver);
|
|
}
|
|
|
|
module_init(spi_dev_init);
|
|
module_exit(spi_test_exit);
|
|
|
|
MODULE_DESCRIPTION("mt SPI test device driver");
|
|
MODULE_AUTHOR("ZH Chen <zh.chen@mediatek.com>");
|
|
MODULE_LICENSE("GPL");
|