6db4831e98
Android 14
660 lines
18 KiB
C
660 lines
18 KiB
C
/*
|
|
* Copyright (C) 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 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 the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#define HIFI4DSP_SPI_DRV_NAME "hifi4dsp-spi"
|
|
#define pr_fmt(fmt) HIFI4DSP_SPI_DRV_NAME ": " fmt
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/cache.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/err.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/spi/spidev.h>
|
|
#include <linux/platform_data/spi-mt65xx.h>
|
|
#include "hifi4dsp_spi.h"
|
|
/*
|
|
* SPI command description.
|
|
*/
|
|
#define CMD_PWOFF 0x02 /* Power Off */
|
|
#define CMD_PWON 0x04 /* Power On */
|
|
#define CMD_RS 0x06 /* Read Status */
|
|
#define CMD_WS 0x08 /* Write Status */
|
|
#define CMD_CR 0x0a /* Config Read */
|
|
#define CMD_CW 0x0c /* Config Write */
|
|
#define CMD_RD 0x81 /* Read Data */
|
|
#define CMD_WD 0x0e /* Write Data */
|
|
#define CMD_CT 0x10 /* Config Type */
|
|
/*
|
|
* SPI slave status register (to master).
|
|
*/
|
|
#define SLV_ON BIT(0)
|
|
#define SR_CFG_SUCCESS BIT(1)
|
|
#define SR_TXRX_FIFO_RDY BIT(2)
|
|
#define SR_RD_ERR BIT(3)
|
|
#define SR_WR_ERR BIT(4)
|
|
#define SR_RDWR_FINISH BIT(5)
|
|
#define SR_TIMEOUT_ERR BIT(6)
|
|
#define SR_CMD_ERR BIT(7)
|
|
#define CONFIG_READY ((SR_CFG_SUCCESS | SR_TXRX_FIFO_RDY))
|
|
/*
|
|
* hardware limit for once transfter.
|
|
*/
|
|
#define MAX_SPI_XFER_SIZE_ONCE (64 * 1024 - 1)
|
|
#define MAX_SPI_TRY_CNT (10)
|
|
/*
|
|
* default never pass more than 32 bytes
|
|
*/
|
|
#define MTK_SPI_BUFSIZ min(32, SMP_CACHE_BYTES)
|
|
#define SPI_READ true
|
|
#define SPI_WRITE false
|
|
#define SPI_READ_STA_ERR_RET (1)
|
|
#define DSP_SPIS1_CLKSEL_ADDR (0x1d00e0cc)
|
|
#define SPI_FREQ_52M (52*1000*1000)
|
|
#define SPI_FREQ_26M (26*1000*1000)
|
|
#define SPI_FREQ_13M (13*1000*1000)
|
|
/* HIFI4DSP specific SPI data */
|
|
struct mtk_hifi4dsp_spi_data {
|
|
int spi_bus_idx;
|
|
int reserved;
|
|
void *spi_bus_data[2];
|
|
};
|
|
|
|
static DEFINE_MUTEX(hifi4dsp_bus_lock);
|
|
static struct mtk_hifi4dsp_spi_data hifi4dsp_spi_data;
|
|
static int hifi4dsp_spi_init_done;
|
|
static int default_spi_trans_mode = 2;
|
|
static int spi_config_type_wr(struct spi_device *spi, int type, u32 addr,
|
|
int len, bool wr, u32 speed)
|
|
{
|
|
int status, i, try = 0;
|
|
u8 tx_cmd_type_single[] = {CMD_CT, 0x04}; // config type
|
|
u8 tx_cmd_type_dual[] = {CMD_CT, 0x05};
|
|
u8 tx_cmd_type_quad[] = {CMD_CT, 0x06};
|
|
u8 cmd_config[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
|
|
u8 tx_cmd_read_sta[2] = {CMD_RS, 0x00};
|
|
u8 rx_cmd_read_sta[2] = {0, 0};
|
|
u8 read_status;
|
|
void *buffer;
|
|
struct spi_transfer x[3];
|
|
struct spi_message message;
|
|
loop:
|
|
spi_message_init(&message);
|
|
memset(x, 0, sizeof(x));
|
|
memset(rx_cmd_read_sta, 0, ARRAY_SIZE(rx_cmd_read_sta));
|
|
if (type == 2) {
|
|
buffer = tx_cmd_type_quad;
|
|
} else if (type == 1) {
|
|
buffer = tx_cmd_type_dual;
|
|
} else if (type == 0) {
|
|
buffer = tx_cmd_type_single;
|
|
} else {
|
|
status = -EINVAL;
|
|
pr_notice("Input wrong type!\n");
|
|
goto tail;
|
|
}
|
|
x[0].tx_buf = buffer;
|
|
x[0].len = ARRAY_SIZE(tx_cmd_type_quad);
|
|
x[0].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[0].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[0].speed_hz = speed;
|
|
x[0].cs_change = 1;
|
|
spi_message_add_tail(&x[0], &message);
|
|
if (wr)
|
|
cmd_config[0] = CMD_CR;
|
|
else
|
|
cmd_config[0] = CMD_CW;
|
|
for (i = 0; i < 4; i++) {
|
|
cmd_config[1 + i] = (addr & (0xff << (i * 8))) >> (i * 8);
|
|
cmd_config[5 + i] = ((len - 1) & (0xff << (i * 8))) >> (i * 8);
|
|
}
|
|
x[1].tx_buf = cmd_config;
|
|
x[1].len = ARRAY_SIZE(cmd_config);
|
|
x[1].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].speed_hz = speed;
|
|
x[1].cs_change = 1;
|
|
spi_message_add_tail(&x[1], &message);
|
|
x[2].tx_buf = tx_cmd_read_sta;
|
|
x[2].rx_buf = rx_cmd_read_sta;
|
|
x[2].len = ARRAY_SIZE(tx_cmd_read_sta);
|
|
x[2].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[2].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[2].speed_hz = speed;
|
|
spi_message_add_tail(&x[2], &message);
|
|
status = spi_sync(spi, &message);
|
|
if (status)
|
|
goto tail;
|
|
read_status = rx_cmd_read_sta[1];
|
|
if ((read_status & CONFIG_READY) != CONFIG_READY) {
|
|
pr_notice("SPI slave status error: 0x%x, line:%d\n",
|
|
read_status, __LINE__);
|
|
if (try++ <= MAX_SPI_TRY_CNT)
|
|
goto loop;
|
|
}
|
|
tail:
|
|
if (status) {
|
|
pr_notice("config type & addr & len err, line(%d), type(%d), ret(%d)\n",
|
|
__LINE__, type, status);
|
|
}
|
|
return status;
|
|
}
|
|
static int spi_trigger_wr_data(struct spi_device *spi,
|
|
int type, int len, bool wr, void *buf_store, u32 speed)
|
|
{
|
|
int status;
|
|
struct spi_message msg;
|
|
struct spi_transfer x[3];
|
|
size_t size;
|
|
void *local_buf = NULL;
|
|
u8 mtk_spi_buffer[MTK_SPI_BUFSIZ];
|
|
u8 tx_cmd_read_sta[2] = {CMD_RS, 0x00};
|
|
u8 rx_cmd_read_sta[2] = {0, 0};
|
|
u8 tx_cmd_write_sta[2] = {CMD_WS, 0x01};
|
|
u8 rx_cmd_write_sta[2] = {0, 0};
|
|
u8 read_status;
|
|
u32 retry_count = 0;
|
|
|
|
memset(x, 0, sizeof(x));
|
|
if (!buf_store) {
|
|
status = -EINVAL;
|
|
goto tail;
|
|
}
|
|
size = len + 1;
|
|
if (size > MTK_SPI_BUFSIZ) {
|
|
local_buf = kzalloc(size, GFP_KERNEL);
|
|
if (!local_buf) {
|
|
status = -ENOMEM;
|
|
pr_notice("tx/rx malloc fail!, line:%d\n", __LINE__);
|
|
goto tail;
|
|
}
|
|
} else {
|
|
local_buf = mtk_spi_buffer;
|
|
memset(local_buf, 0, MTK_SPI_BUFSIZ);
|
|
}
|
|
x[0].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[0].rx_nbits = SPI_NBITS_SINGLE;
|
|
if (type == 1) {
|
|
x[0].tx_nbits = SPI_NBITS_DUAL;
|
|
x[0].rx_nbits = SPI_NBITS_DUAL;
|
|
} else if (type == 2) {
|
|
x[0].tx_nbits = SPI_NBITS_QUAD;
|
|
x[0].rx_nbits = SPI_NBITS_QUAD;
|
|
}
|
|
x[0].len = size;
|
|
x[0].speed_hz = speed;
|
|
x[0].cs_change = 1;
|
|
if (wr) {// read data
|
|
*((u8 *)local_buf) = CMD_RD;
|
|
x[0].tx_buf = local_buf;
|
|
x[0].rx_buf = local_buf;
|
|
} else {
|
|
*((u8 *)local_buf) = CMD_WD;// write data
|
|
memcpy((u8 *)local_buf + 1, buf_store, len); // CMD + data
|
|
x[0].tx_buf = local_buf;
|
|
}
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&x[0], &msg);
|
|
/*
|
|
* Check SPI-Slave Read Status,
|
|
* SR_RDWR_FINISH = 1 & RD_ERR/WR_ERR = 0 ???
|
|
*/
|
|
memset(rx_cmd_read_sta, 0, ARRAY_SIZE(rx_cmd_read_sta));
|
|
x[1].tx_buf = tx_cmd_read_sta; // read status
|
|
x[1].rx_buf = rx_cmd_read_sta;
|
|
x[1].len = ARRAY_SIZE(tx_cmd_read_sta);
|
|
x[1].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].speed_hz = speed;
|
|
spi_message_add_tail(&x[1], &msg);
|
|
status = spi_sync(spi, &msg);
|
|
if (status)
|
|
goto tail;
|
|
read_status = rx_cmd_read_sta[1];
|
|
if (((read_status & SR_RD_ERR) == SR_RD_ERR) ||
|
|
((read_status & SR_WR_ERR) == SR_WR_ERR) ||
|
|
((read_status & SR_TIMEOUT_ERR) == SR_TIMEOUT_ERR)) {
|
|
pr_notice("SPI slave status error: 0x%x, line:%d\n",
|
|
read_status, __LINE__);
|
|
x[2].tx_buf = tx_cmd_write_sta; // write status
|
|
x[2].rx_buf = rx_cmd_write_sta;
|
|
x[2].len = ARRAY_SIZE(tx_cmd_write_sta);
|
|
x[2].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[2].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[2].speed_hz = speed;
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&x[2], &msg);
|
|
status = spi_sync(spi, &msg);
|
|
if (status)
|
|
goto tail;
|
|
do {
|
|
memset(rx_cmd_read_sta, 0, ARRAY_SIZE(rx_cmd_read_sta));
|
|
x[1].tx_buf = tx_cmd_read_sta; // read status
|
|
x[1].rx_buf = rx_cmd_read_sta;
|
|
x[1].len = ARRAY_SIZE(tx_cmd_read_sta);
|
|
x[1].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].speed_hz = speed;
|
|
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&x[1], &msg);
|
|
status = spi_sync(spi, &msg);
|
|
if (status)
|
|
goto tail;
|
|
retry_count++;
|
|
read_status = rx_cmd_read_sta[1];
|
|
} while ((((read_status & SR_RD_ERR) == SR_RD_ERR) ||
|
|
((read_status & SR_WR_ERR) == SR_WR_ERR) ||
|
|
((read_status & SR_TIMEOUT_ERR) == SR_TIMEOUT_ERR)) &&
|
|
(retry_count < 100000));
|
|
status = SPI_READ_STA_ERR_RET;
|
|
} else {
|
|
while (((read_status & SR_RDWR_FINISH) != SR_RDWR_FINISH) &&
|
|
(retry_count < 100000)) {
|
|
pr_notice("SPI slave r/w not finished: 0x%x, line:%d\n",
|
|
read_status, __LINE__);
|
|
memset(rx_cmd_read_sta, 0, ARRAY_SIZE(rx_cmd_read_sta));
|
|
x[1].tx_buf = tx_cmd_read_sta; // read status
|
|
x[1].rx_buf = rx_cmd_read_sta;
|
|
x[1].len = ARRAY_SIZE(tx_cmd_read_sta);
|
|
x[1].tx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].rx_nbits = SPI_NBITS_SINGLE;
|
|
x[1].speed_hz = speed;
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&x[1], &msg);
|
|
status = spi_sync(spi, &msg);
|
|
if (status)
|
|
goto tail;
|
|
retry_count++;
|
|
read_status = rx_cmd_read_sta[1];
|
|
}
|
|
if (retry_count >= 100000)
|
|
status = SPI_READ_STA_ERR_RET;
|
|
}
|
|
tail:
|
|
/* Only for successful read */
|
|
if (wr && !status)
|
|
memcpy(buf_store, ((u8 *)x[0].rx_buf + 1), len);
|
|
if (local_buf != mtk_spi_buffer)
|
|
kfree(local_buf);
|
|
if (status)
|
|
pr_notice("write/read to slave err, line(%d), len(%d), ret(%d)\n",
|
|
__LINE__, len, status);
|
|
return status;
|
|
}
|
|
int dsp_spi_write(u32 addr, void *value, int len, u32 speed)
|
|
{
|
|
int ret, try = 0, xfer_speed;
|
|
int type = default_spi_trans_mode;
|
|
struct spi_device *spi = hifi4dsp_spi_data.spi_bus_data[0];
|
|
void *tx_store;
|
|
|
|
pr_notice("%s addr = 0x%08x, len = %d\n", __func__, addr, len);
|
|
xfer_speed = speed;
|
|
mutex_lock(&hifi4dsp_bus_lock);
|
|
spi_config_write:
|
|
ret = spi_config_type_wr(spi, type, addr, len, SPI_WRITE, xfer_speed);
|
|
if (ret < 0) {
|
|
pr_notice("SPI config write fail! line:%d\n", __LINE__);
|
|
goto tail;
|
|
}
|
|
tx_store = value;
|
|
ret = spi_trigger_wr_data(spi, type, len, SPI_WRITE, tx_store,
|
|
xfer_speed);
|
|
if (ret < 0) {
|
|
pr_notice("SPI write data error! line:%d\n", __LINE__);
|
|
goto tail;
|
|
}
|
|
if (ret > 0) {
|
|
if (try++ < MAX_SPI_TRY_CNT)
|
|
goto spi_config_write;
|
|
else
|
|
pr_notice("SPI write fail, retry count > %d, line:%d\n",
|
|
MAX_SPI_TRY_CNT, __LINE__);
|
|
}
|
|
tail:
|
|
mutex_unlock(&hifi4dsp_bus_lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(dsp_spi_write);
|
|
int dsp_spi_write_ex(u32 addr, void *value, int len, u32 speed)
|
|
{
|
|
int ret = 0;
|
|
int res_len;
|
|
int once_len;
|
|
int loop;
|
|
int cycle;
|
|
u32 new_addr;
|
|
u8 *new_buf;
|
|
|
|
once_len = MAX_SPI_XFER_SIZE_ONCE;
|
|
cycle = len / once_len;
|
|
res_len = len % once_len;
|
|
for (loop = 0; loop < cycle; loop++) {
|
|
new_addr = addr + once_len * loop;
|
|
new_buf = (u8 *)value + once_len * loop;
|
|
ret = dsp_spi_write(new_addr, new_buf, once_len, speed);
|
|
if (ret)
|
|
pr_notice("dsp_spi_write() fail! line:%d\n", __LINE__);
|
|
}
|
|
if (res_len) {
|
|
new_addr = addr + once_len * loop;
|
|
new_buf = (u8 *)value + once_len * loop;
|
|
ret = dsp_spi_write(new_addr, new_buf, res_len, speed);
|
|
if (ret)
|
|
pr_notice("dsp_spi_write() fail! line:%d\n", __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(dsp_spi_write_ex);
|
|
int dsp_spi_read(u32 addr, void *value, int len, u32 speed)
|
|
{
|
|
int ret, try = 0, xfer_speed;
|
|
int type = default_spi_trans_mode;
|
|
struct spi_device *spi = hifi4dsp_spi_data.spi_bus_data[0];
|
|
|
|
pr_notice("%s addr = 0x%08x, len = %d\n", __func__, addr, len);
|
|
xfer_speed = speed;
|
|
mutex_lock(&hifi4dsp_bus_lock);
|
|
spi_config_read:
|
|
ret = spi_config_type_wr(spi, type, addr, len, SPI_READ, xfer_speed);
|
|
if (ret < 0) {
|
|
pr_notice("SPI config write fail! line:%d\n", __LINE__);
|
|
goto tail;
|
|
}
|
|
ret = spi_trigger_wr_data(spi, type, len, SPI_READ, value, xfer_speed);
|
|
if (ret < 0) {
|
|
pr_notice("SPI read data error! line:%d\n", __LINE__);
|
|
goto tail;
|
|
}
|
|
if (ret > 0) {
|
|
if (try++ < MAX_SPI_TRY_CNT)
|
|
goto spi_config_read;
|
|
else
|
|
pr_debug("SPI read fail, retry count > %d, line:%d\n",
|
|
MAX_SPI_TRY_CNT, __LINE__);
|
|
}
|
|
pr_notice("[mt6382] spi read regiter %d", *((u32 *)value));
|
|
tail:
|
|
mutex_unlock(&hifi4dsp_bus_lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(dsp_spi_read);
|
|
int dsp_spi_read_ex(u32 addr, void *value, int len, u32 speed)
|
|
{
|
|
int ret = 0;
|
|
int res_len;
|
|
int once_len;
|
|
int loop;
|
|
int cycle;
|
|
u32 new_addr;
|
|
u8 *new_buf;
|
|
|
|
once_len = MAX_SPI_XFER_SIZE_ONCE;
|
|
cycle = len / once_len;
|
|
res_len = len % once_len;
|
|
for (loop = 0; loop < cycle; loop++) {
|
|
new_addr = addr + once_len * loop;
|
|
new_buf = (u8 *)value + once_len * loop;
|
|
ret = dsp_spi_read(new_addr, new_buf, once_len, speed);
|
|
if (ret)
|
|
pr_notice("dsp_spi_read() fail! line:%d\n", __LINE__);
|
|
}
|
|
if (res_len) {
|
|
new_addr = addr + once_len * loop;
|
|
new_buf = (u8 *)value + once_len * loop;
|
|
ret = dsp_spi_read(new_addr, new_buf, res_len, speed);
|
|
if (ret)
|
|
pr_notice("dsp_spi_read() fail! line:%d\n", __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(dsp_spi_read_ex);
|
|
int spi_read_register(u32 addr, u32 *val, u32 speed)
|
|
{
|
|
return dsp_spi_read(addr, (u8 *)val, 4, speed);
|
|
}
|
|
int spi_write_register(u32 addr, u32 val, u32 speed)
|
|
{
|
|
return dsp_spi_write(addr, (u8 *)&val, 4, speed);
|
|
}
|
|
int spi_set_register32(u32 addr, u32 val, u32 speed)
|
|
{
|
|
u32 read_val;
|
|
|
|
spi_read_register(addr, &read_val, speed);
|
|
spi_write_register(addr, read_val | val, speed);
|
|
return 0;
|
|
}
|
|
int spi_clr_register32(u32 addr, u32 val, u32 speed)
|
|
{
|
|
u32 read_val;
|
|
|
|
spi_read_register(addr, &read_val, speed);
|
|
spi_write_register(addr, read_val & (~val), speed);
|
|
return 0;
|
|
}
|
|
int spi_write_register_mask(u32 addr, u32 val, u32 msk, u32 speed)
|
|
{
|
|
u32 read_val;
|
|
|
|
spi_read_register(addr, &read_val, speed);
|
|
spi_write_register(addr, ((read_val & (~(msk))) | ((val) & (msk))),
|
|
speed);
|
|
return 0;
|
|
}
|
|
//#define DSP_ADDR 0x1fc00000
|
|
static u32 dsp_addr = 0x1fc00000;
|
|
int spi_multipin_loopback_transfer(int len, int xfer_speed)
|
|
{
|
|
int ret = 0;
|
|
void *tx_buf;
|
|
void *rx_buf;
|
|
int i, err = 0;
|
|
|
|
pr_info("%s entry...\n", __func__);
|
|
tx_buf = kzalloc(len, GFP_KERNEL);
|
|
rx_buf = kzalloc(len, GFP_KERNEL);
|
|
for (i = 0; i < len; i++)
|
|
*((char *)tx_buf + i) = i%255;
|
|
memset(rx_buf, 0, len);
|
|
ret = dsp_spi_write_ex(dsp_addr, tx_buf, len, xfer_speed*1000*1000);
|
|
if (ret < 0) {
|
|
pr_debug("Write transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
ret = dsp_spi_read_ex(dsp_addr, rx_buf, len, xfer_speed*1000*1000);
|
|
if (ret < 0) {
|
|
pr_debug("Read transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
#if 0
|
|
if (xfer_speed == 13) {
|
|
ret = dsp_spi_write_ex(dsp_addr, tx_buf, len, SPI_SPEED_LOW);
|
|
if (ret < 0) {
|
|
pr_debug("Write transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
ret = dsp_spi_read_ex(dsp_addr, rx_buf, len, SPI_SPEED_LOW);
|
|
if (ret < 0) {
|
|
pr_debug("Read transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
} else if (xfer_speed == 52) {
|
|
dsp_spi_write_ex(dsp_addr, tx_buf, len, SPI_SPEED_HIGH);
|
|
if (ret < 0) {
|
|
pr_debug("Write transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
dsp_spi_read_ex(dsp_addr, rx_buf, len, SPI_SPEED_HIGH);
|
|
if (ret < 0) {
|
|
pr_debug("Read transfer err,line(%d):%d\n", __LINE__,
|
|
ret);
|
|
goto tail;
|
|
}
|
|
} else {
|
|
pr_debug("Unavailabel speed!\n");
|
|
goto tail;
|
|
}
|
|
#endif
|
|
for (i = 0; i < len; i++) {
|
|
if (*((char *)tx_buf+i) != *((char *)rx_buf+i)) {
|
|
pr_debug("tx[%d]:0x%x, rx[%d]:0x%x\r\n",
|
|
i, *((char *)tx_buf+i), i,
|
|
*((char *)rx_buf + i));
|
|
err++;
|
|
}
|
|
}
|
|
pr_debug("total length %d bytes, err %d bytes.\n", len, err);
|
|
pr_info("%s quit...\n", __func__);
|
|
tail:
|
|
kfree(tx_buf);
|
|
kfree(rx_buf);
|
|
if (ret < 0)
|
|
return ret;
|
|
return err;
|
|
}
|
|
static ssize_t hifi4dsp_spi_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int len, xfer_speed, ret;
|
|
|
|
if (!strncmp(buf, "addr=", 5) &&
|
|
(sscanf(buf + 5, "%x", &dsp_addr) == 1)) {
|
|
buf += 16;
|
|
if (!strncmp(buf, "speed=", 6) &&
|
|
(sscanf(buf + 6, "%d", &xfer_speed) == 1)) {
|
|
buf += 9;
|
|
if (!strncmp(buf, "len=", 4) &&
|
|
(sscanf(buf + 4, "%d", &len) == 1)) {
|
|
pr_info("**dump set**\n addr = 0x%x, speed = %d, len = %d\n",
|
|
dsp_addr, xfer_speed, len);
|
|
ret = spi_multipin_loopback_transfer(len,
|
|
xfer_speed);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR(hifi4dsp_spi, 0200, NULL, hifi4dsp_spi_store);
|
|
static struct device_attribute *spi_attribute[] = {
|
|
&dev_attr_hifi4dsp_spi,
|
|
};
|
|
static void spi_create_attribute(struct device *dev)
|
|
{
|
|
int size, idx, ret;
|
|
|
|
size = ARRAY_SIZE(spi_attribute);
|
|
for (idx = 0; idx < size; idx++) {
|
|
ret = device_create_file(dev, spi_attribute[idx]);
|
|
if (ret != 0)
|
|
pr_info("device_create_file fail!\n");
|
|
}
|
|
}
|
|
int hifi4dsp_spi_get_status(void)
|
|
{
|
|
return hifi4dsp_spi_init_done;
|
|
}
|
|
static int hifi4dsp_spi_probe(struct spi_device *spi)
|
|
{
|
|
int err = 0, ret = 0, tick_delay = 0;
|
|
static struct task_struct *dsp_task;
|
|
struct device_node *nc = spi->dev.of_node;
|
|
struct mtk_chip_config *data;
|
|
struct mtk_hifi4dsp_spi_data *pri_data = &hifi4dsp_spi_data;
|
|
|
|
pr_info("%s() enter.\n", __func__);
|
|
data = kzalloc(sizeof(struct mtk_chip_config), GFP_KERNEL);
|
|
if (!data) {
|
|
err = -ENOMEM;
|
|
goto tail;
|
|
}
|
|
ret = of_property_read_u32(nc, "tick-dly", &tick_delay);
|
|
if (ret) {
|
|
pr_info("tick-dly isn't setting!\n");
|
|
tick_delay = 0;
|
|
} else
|
|
pr_info("tick-dly = %d\n", tick_delay);
|
|
ret = of_property_read_u32(nc, "spi-pin-mode", &default_spi_trans_mode);
|
|
if (ret) {
|
|
pr_info("spi-pin-mode isn't setting!\n");
|
|
default_spi_trans_mode = 2;
|
|
} else
|
|
pr_info("spi-pin-mode = %d\n", default_spi_trans_mode);
|
|
/*
|
|
* Structure filled with mtk-spi crtical values.
|
|
*/
|
|
spi->bits_per_word = 8;
|
|
data->rx_mlsb = 0;
|
|
data->tx_mlsb = 0;
|
|
//data->command_cnt = 1; //for six pin spi
|
|
//data->dummy_cnt = 0; //for six pin spi
|
|
data->tick_delay = tick_delay;
|
|
spi->controller_data = (void *)data;
|
|
/* Fill structure mtk_hifi4dsp_spi_data */
|
|
pri_data->spi_bus_data[pri_data->spi_bus_idx++] = spi;
|
|
dsp_task = NULL;
|
|
spi_create_attribute(&spi->dev);
|
|
hifi4dsp_spi_init_done = 1;
|
|
tail:
|
|
return err;
|
|
}
|
|
static int hifi4dsp_spi_remove(struct spi_device *spi)
|
|
{
|
|
pr_info("%s().\n", __func__);
|
|
if (spi && spi->controller_data)
|
|
kfree(spi->controller_data);
|
|
return 0;
|
|
}
|
|
static const struct spi_device_id hifi4dsp_spi_ids[] = {
|
|
{ "mt8570" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, hifi4dsp_spi_ids);
|
|
static const struct of_device_id hifi4dsp_spi_of_ids[] = {
|
|
{ .compatible = "mediatek,hifi4dsp-spi" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, hifi4dsp_spi_of_ids);
|
|
static struct spi_driver hifi4dsp_spi_drv = {
|
|
.driver = {
|
|
.name = HIFI4DSP_SPI_DRV_NAME,
|
|
.bus = &spi_bus_type,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = hifi4dsp_spi_of_ids,
|
|
},
|
|
.id_table = hifi4dsp_spi_ids,
|
|
.probe = hifi4dsp_spi_probe,
|
|
.remove = hifi4dsp_spi_remove,
|
|
};
|
|
module_spi_driver(hifi4dsp_spi_drv);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Dehui Sun <dehui.sun@mediatek.com>");
|
|
MODULE_DESCRIPTION("SPI driver for hifi4dsp chip");
|