You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1953 lines
56 KiB

/*
* Copyright (c) Hisilicon Technologies Co., Ltd.. 2010-2022. All rights reserved.
* Description:
*/
/******************************************************************************/
/* Includes */
/******************************************************************************/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/amba/pl022.h>
#include <linux/gpio.h>
#include <linux/atomic.h>
#include <linux/compat.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include "linux/huanglong/securec.h"
#include "soc_log.h"
#include "osal_ext.h"
#include "drv_mem_ext.h"
#include "drv_module_ext.h"
#include "drv_mem_ext.h"
#include "drv_sys_ext.h"
#include "drv_spi_ext.h"
#include "drv_ioctl_spi.h"
#include "drv_spi.h"
/******************************************************************************/
/* Defines */
/******************************************************************************/
#define SPI0_BASE 0x00A78000
#define SPI1_BASE 0x00A79000
#define SPI2_BASE 0x00A7A000
#define SPI3_BASE 0x00A7B000
/* SPI register definition */
#define SPI0_SR (SPI0_BASE + 0x0C)
#define SPI0_TXFIFOCR (SPI0_BASE + 0x28)
#define SPI0_RXFIFOCR (SPI0_BASE + 0x2C)
#define SPI1_SR (SPI1_BASE + 0x0C)
#define SPI1_TXFIFOCR (SPI1_BASE + 0x28)
#define SPI1_RXFIFOCR (SPI1_BASE + 0x2C)
#define SPI2_SR (SPI2_BASE + 0x0C)
#define SPI2_TXFIFOCR (SPI2_BASE + 0x28)
#define SPI2_RXFIFOCR (SPI2_BASE + 0x2C)
#define SPI3_SR (SPI3_BASE + 0x0C)
#define SPI3_TXFIFOCR (SPI3_BASE + 0x28)
#define SPI3_RXFIFOCR (SPI3_BASE + 0x2C)
/* for reserved13 cs */
#define SPI0_CSN0_GPIO_RESERVED13_RESERVED9 (8 * 3 + 2)
/* for reserved5 cs */
#define SPI0_CSN0_GPIO_RESERVED5_RESERVED2 (0 * 8 + 4)
#define SPI1_CSN0_GPIO_RESERVED5_RESERVED2 (4 * 8 + 3)
/* for reserved19 cs */
#define SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811 (1 * 8 + 5)
#define SPI1_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811 (0 * 8 + 2)
#define MAX_SPI_TRANSFER_SIZE (4 * 1024 * 1024)
#define MAX_BPW 16
#define MIN_BPW 4
#define CPSDVR_MIN 0x02
#define CPSDVR_MAX 0xFE
#define SCR_MIN 0x00
#define SCR_MAX 0xFF
/******************************************************************************/
/* Structures */
/******************************************************************************/
struct ext_spi_dev {
struct semaphore lock;
struct spi_device *spi_dev;
struct spi_board_info chip;
struct pl022_config_chip pl022_config_info;
u8 bits_per_word;
};
typedef struct {
ext_chip_name_id chip_name_id;
td_u32 spi_id;
td_s32 gpio_csn;
} spi_chip_to_gpio_csn_table;
/******************************************************************************/
/* Globals */
/******************************************************************************/
static struct ext_spi_dev g_spi_dummy[MAX_SPI_CHANNEL];
static td_u32 g_spi_gpiocs;
static atomic_t g_spi_init_counter = ATOMIC_INIT(0);
static spi_chip_to_gpio_csn_table g_spi_chip_to_gpio_csn_table[] = {
{CHIP_NAME_RESERVED13, 0, SPI0_CSN0_GPIO_RESERVED13_RESERVED9},
{CHIP_NAME_RESERVED13, 1, -1},
{CHIP_NAME_RESERVED13, 2, -1},
{CHIP_NAME_RESERVED13, 3, -1},
{CHIP_NAME_RESERVED6, 0, -1},
{CHIP_NAME_RESERVED6, 1, -1},
{CHIP_NAME_RESERVED6, 2, -1},
{CHIP_NAME_RESERVED6, 3, -1},
{CHIP_NAME_RESERVED9, 0, SPI0_CSN0_GPIO_RESERVED13_RESERVED9},
{CHIP_NAME_RESERVED9, 1, -1},
{CHIP_NAME_RESERVED9, 2, -1},
{CHIP_NAME_RESERVED9, 3, -1},
{CHIP_NAME_RESERVED8, 0, -1},
{CHIP_NAME_RESERVED8, 1, -1},
{CHIP_NAME_RESERVED8, 2, -1},
{CHIP_NAME_RESERVED8, 3, -1},
{CHIP_NAME_RESERVED5, 0, SPI0_CSN0_GPIO_RESERVED5_RESERVED2},
{CHIP_NAME_RESERVED5, 1, SPI1_CSN0_GPIO_RESERVED5_RESERVED2},
{CHIP_NAME_RESERVED5, 2, -1},
{CHIP_NAME_RESERVED5, 3, -1},
{CHIP_NAME_RESERVED2, 0, SPI0_CSN0_GPIO_RESERVED5_RESERVED2},
{CHIP_NAME_RESERVED2, 1, SPI1_CSN0_GPIO_RESERVED5_RESERVED2},
{CHIP_NAME_RESERVED2, 2, -1},
{CHIP_NAME_RESERVED2, 3, -1},
{CHIP_NAME_RESERVED17, 0, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_RESERVED17, 1, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_RESERVED17, 2, -1},
{CHIP_NAME_RESERVED17, 3, -1},
{CHIP_NAME_RESERVED19, 0, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_RESERVED19, 1, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_RESERVED19, 2, -1},
{CHIP_NAME_RESERVED19, 3, -1},
{CHIP_NAME_HI3751V811, 0, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_HI3751V811, 1, SPI0_CSN0_GPIO_RESERVED17_RESERVED19_HI3751V811},
{CHIP_NAME_HI3751V811, 2, -1},
{CHIP_NAME_HI3751V811, 3, -1},
};
/******************************************************************************
* Function Implementation
*******************************************************************************/
static td_s32 spi_double_cs(td_void)
{
return 0;
}
td_void spi_write_reg(td_u32 addr, td_u32 value)
{
void *__iomem ioaddr = ioremap_nocache(addr, 0x4);
if (ioaddr == NULL) {
soc_log_err("ioremap failed\n");
return;
}
writel(value, ioaddr);
iounmap(ioaddr);
}
td_void spi_read_reg(td_u32 addr, td_u32 *value)
{
void *__iomem ioaddr;
if (value == TD_NULL) {
soc_log_err("null pointer\n");
return;
}
ioaddr = ioremap_nocache(addr, 0x4);
if (ioaddr == NULL) {
soc_log_err("ioremap failed\n");
return;
}
*value = readl(ioaddr);
iounmap(ioaddr);
}
td_u8 ext_drv_spi_get_cs_config(td_void)
{
return g_spi_gpiocs ? 1 : 0;
}
td_void ext_drv_spi_set_cs_config(td_u8 gpio_cs)
{
g_spi_gpiocs = gpio_cs ? 1 : 0;
}
static td_s32 spi_get_gpio_csn(td_u8 id, td_s32 *gpio_no, td_bool *is_find)
{
td_s32 ret;
ext_chip_name_id chip_name_id = 0xFFFF;
td_s32 i;
if (gpio_no == TD_NULL) {
soc_log_err("gpio_no is NULL!\n");
return TD_FAILURE;
}
if (is_find == TD_NULL) {
soc_log_err("is_find is NULL!\n");
return TD_FAILURE;
}
ret = ext_drv_sys_get_chip_name_id(&chip_name_id);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_sys_get_chip_name_id() failed!\n");
return ret;
}
for (i = 0; i < sizeof(g_spi_chip_to_gpio_csn_table) / sizeof(spi_chip_to_gpio_csn_table); i++) {
if (chip_name_id != g_spi_chip_to_gpio_csn_table[i].chip_name_id ||
id != g_spi_chip_to_gpio_csn_table[i].spi_id) {
continue;
}
*is_find = TD_TRUE;
*gpio_no = g_spi_chip_to_gpio_csn_table[i].gpio_csn;
return TD_SUCCESS;
}
return TD_FAILURE;
}
td_void ext_drv_spi_set_cs_gpio(td_u8 id)
{
td_s32 ret;
td_s32 gpio_no = -1;
td_bool is_find = TD_FALSE;
if (!g_spi_gpiocs) {
return;
}
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, return);
ret = spi_get_gpio_csn(id, &gpio_no, &is_find);
if (ret != TD_SUCCESS || is_find == TD_FALSE) {
soc_log_err("[set_cs_gpio] spi_get_gpio_csn() failed!\n");
return;
}
if (gpio_no < 0) {
return;
}
/* set gpio direction output and high level to disable chipselect */
gpio_direction_output(gpio_no, 1);
}
td_void ext_drv_spi_set_cs_level(td_u8 id, td_u32 level)
{
td_s32 ret;
td_s32 gpio_no = 0;
td_bool is_find = TD_FALSE;
if (!g_spi_gpiocs) {
return;
}
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, return);
ret = spi_get_gpio_csn(id, &gpio_no, &is_find);
if (ret != TD_SUCCESS || is_find == TD_FALSE) {
soc_log_err("[set_cs_level] spi_get_gpio_csn() failed!\n");
return;
}
if (gpio_no < 0) {
return;
}
gpio_set_value(gpio_no, level ? 1 : 0);
}
/*
* Set Loopback Mode.
*
* @param[IN] id SPI channel id.
* @param[IN] set Normal mode if loopback is 0.
* Loopback mode if loopback is 1.
*
* @return 0 success
* @return -1 failed
*/
td_s32 ext_drv_spi_set_loop(td_u8 id, td_u8 set)
{
td_s32 ret = 0;
struct spi_device *dev = NULL;
struct spi_board_info *chip = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
chip = &g_spi_dummy[id].chip;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, chip == NULL, goto out);
if (set == 1) {
dev->mode |= SPI_LOOP;
chip->mode |= SPI_LOOP;
} else {
dev->mode &= ~SPI_LOOP;
chip->mode &= ~SPI_LOOP;
}
ret = spi_setup(dev);
if (ret < 0) {
soc_log_err("spi_setup failed! ret = %d\n", ret);
goto out;
}
out:
return ret;
}
td_void ext_drv_spi_set_fifo(td_u8 id, td_u8 size)
{
td_s32 ret = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, return);
if (id == 0x0) {
spi_write_reg(SPI0_RXFIFOCR, size << 0x3);
} else if (id == 0x1) {
if (spi_double_cs())
spi_write_reg(SPI1_RXFIFOCR, size << 0x3);
} else if (id == 0x2) {
spi_write_reg(SPI2_RXFIFOCR, size << 0x3);
} else if (id == 0x3) {
spi_write_reg(SPI3_RXFIFOCR, size << 0x3);
}
}
/*
* set SPI frame form routine.
*
* @param framemode: see enum ssp_td_s32erface
* @param frf: frame format(0:MOTO/1:TI/2:NM)
* @param spo: SSPCLKOUT voltage level (0/1)
* @param sph: SSPCLKOUT phase (0/1)
* @param dss: data bit
* 0000: reserved 0001: reserved 0010: reserved 0011: 4bit data
* 0100: 5bit data 0101: 6bit data 0110:7bit data 0111: 8bit data
* 1000: 9bit data 1001: 10bit data 1010:11bit data 1011: 12bit data
* 1100: 13bit data 1101: 14bit data 1110:15bit data 1111: 16bit data
*
* @return value: 0--success; -1--error.
*
*/
td_s32 ext_drv_spi_set_from(td_u8 id, td_u8 frf, td_u8 spo, td_u8 sph, td_u8 dss)
{
td_s32 ret = 0;
struct pl022_config_chip *pl022 = NULL;
struct spi_device *dev = NULL;
struct spi_board_info *chip = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, frf > SSP_INTERFACE_NATIONAL_MICROWIRE ||
frf < SSP_INTERFACE_MOTOROLA_SPI, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, dss > MAX_BPW || dss < MIN_BPW, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
chip = &g_spi_dummy[id].chip;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, chip == NULL, goto out);
pl022 = &g_spi_dummy[id].pl022_config_info;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, pl022 == NULL, goto out);
pl022->iface = frf;
if (spo) {
dev->mode |= SPI_CPOL;
chip->mode |= SPI_CPOL;
} else {
dev->mode &= ~SPI_CPOL;
chip->mode &= ~SPI_CPOL;
}
if (sph) {
dev->mode |= SPI_CPHA;
chip->mode |= SPI_CPHA;
} else {
dev->mode &= ~SPI_CPHA;
chip->mode &= ~SPI_CPHA;
}
dev->bits_per_word = dss;
g_spi_dummy[id].bits_per_word = dss;
ret = spi_setup(dev);
if (ret < 0) {
soc_log_err("spi_setup failed! ret = %d\n", ret);
goto out;
}
out:
return ret;
}
td_s32 ext_drv_spi_get_from(td_u8 id, td_u8 *frf, td_u8 *spo, td_u8 *sph, td_u8 *dss)
{
td_s32 ret = 0;
struct spi_device *dev = NULL;
struct pl022_config_chip *pl022 = NULL;
struct spi_board_info *chip = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, frf == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, spo == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, sph == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dss == NULL, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
chip = &g_spi_dummy[id].chip;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, chip == NULL, goto out);
pl022 = &g_spi_dummy[id].pl022_config_info;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, pl022 == NULL, goto out);
*frf = pl022->iface;
*spo = (chip->mode & SPI_CPOL) ? 1 : 0;
*sph = (chip->mode & SPI_CPHA) ? 1 : 0;
*dss = dev->bits_per_word;
out:
return ret;
}
/*
* set SPI serial clock rate routine.
*
* @param scr: scr value.(0-255,usually it is 0)
* @param cpsdvsr: Clock prescale divisor.(2-254 even)
*
* @return value: 0--success; -1--error.
*
*/
td_s32 ext_drv_spi_set_clk(td_u8 id, td_u8 scr, td_u8 cpsdvsr)
{
td_s32 ret = 0;
struct spi_device *dev = NULL;
struct pl022_config_chip *pl022 = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, cpsdvsr > CPSDVR_MAX ||
cpsdvsr < CPSDVR_MIN, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, scr > SCR_MAX ||
scr < SCR_MIN, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
pl022 = &g_spi_dummy[id].pl022_config_info;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, pl022 == NULL, goto out);
if (dev->controller_data != pl022) {
soc_log_err("controller_data error\n");
return -1;
}
pl022->clk_freq.cpsdvsr = cpsdvsr;
pl022->clk_freq.scr = scr;
ret = spi_setup(dev);
if (ret < 0) {
soc_log_err("spi_setup failed! ret = %d\n", ret);
goto out;
}
out:
return ret;
}
td_void ext_drv_spi_set_blend(td_u8 id, td_bool is_bigend)
{
td_s32 ret = 0;
struct spi_device *dev = NULL;
struct spi_board_info *chip = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
chip = &g_spi_dummy[id].chip;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, chip == NULL, goto out);
if (!is_bigend) {
dev->mode |= SPI_LSB_FIRST;
chip->mode |= SPI_LSB_FIRST;
} else {
dev->mode &= ~SPI_LSB_FIRST;
chip->mode &= ~SPI_LSB_FIRST;
}
ret = spi_setup(dev);
if (ret < 0) {
soc_log_err("spi_setup failed! ret = %d\n", ret);
return;
}
out:
return;
}
td_bool ext_drv_spi_get_blend(td_u8 id)
{
struct spi_device *dev = NULL;
struct spi_board_info *chip = NULL;
if (id >= MAX_SPI_CHANNEL) {
soc_log_err("spi channel %d not support\n", id);
return -1;
}
dev = g_spi_dummy[id].spi_dev;
if (dev == NULL) {
soc_log_err("open spi channel %d firstly\n", id);
return -1;
}
chip = &g_spi_dummy[id].chip;
return (chip && (chip->mode & SPI_LSB_FIRST)) ? 0 : 1;
}
td_s32 ext_drv_spi_fifo_empty(td_u8 id, td_s32 send)
{
td_s32 ret = 0;
td_u32 value = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
if (id == 0x0) {
spi_read_reg(SPI0_SR, &value);
} else if (id == 0x1) {
if (spi_double_cs()) {
spi_read_reg(SPI1_SR, &value);
} else {
return 2; /* 2 fifo empty */
}
} else if (id == 0x2) {
spi_read_reg(SPI2_SR, &value);
} else if (id == 0x3) {
spi_read_reg(SPI3_SR, &value);
}
if (send) {
return (value & 0x1) ? 1 : 0; /* send fifo */
} else {
return (value & 0x4) ? 0 : 1; /* receive fifo */
}
out:
return ret;
}
/*
* write SPI_DR register rountine.
* @param sdata: data of SSP_DR register
*/
td_s32 ext_drv_spi_write_query(td_u8 id, const td_u8 *send, td_u32 send_cnt)
{
td_s32 ret = 0;
struct spi_message msg;
struct spi_transfer xfer = {0};
struct spi_device *dev = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
spi_message_init(&msg);
xfer.tx_buf = send;
xfer.rx_buf = NULL;
xfer.len = send_cnt;
spi_message_add_tail(&xfer, &msg);
ext_drv_spi_set_cs_level(id, 0);
ret = spi_sync(dev, &msg);
if (ret) {
soc_log_err("[write_query] spi_sync failed! ret = %d\n", ret);
}
ext_drv_spi_set_cs_level(id, 1);
out:
return ret;
}
td_s32 ext_drv_spi_write_isr(td_u8 id, const td_u8 *send, td_u32 send_cnt)
{
td_s32 ret = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
ret = ext_drv_spi_write_query(id, send, send_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_write_query failed\n");
goto out;
}
out:
return ret;
}
td_s32 ext_drv_spi_read_query(td_u8 id, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
struct spi_message msg;
struct spi_transfer xfer = {0};
struct spi_device *dev = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
spi_message_init(&msg);
xfer.tx_buf = NULL;
xfer.rx_buf = read;
xfer.len = read_cnt;
spi_message_add_tail(&xfer, &msg);
ext_drv_spi_set_cs_level(id, 0);
ret = spi_sync(dev, &msg);
if (ret) {
soc_log_err("[read_query] spi_sync failed! ret = %d\n", ret);
}
ext_drv_spi_set_cs_level(id, 1);
out:
return ret;
}
td_s32 ext_drv_spi_read_isr(td_u8 id, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
ret = ext_drv_spi_read_query(id, read, read_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_read_query failed\n");
goto out;
}
out:
return ret;
}
/*
* read SPI_DR register rountine.
*
* @return value: data from SSP_DR register readed
*
*/
td_s32 ext_drv_spi_read_ex(td_u8 id, const td_u8 *send, td_u32 send_cnt, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
struct spi_message msg;
struct spi_transfer xfer[0x2];
struct spi_transfer *p = NULL;
struct spi_transfer *q = NULL;
struct spi_device *dev = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
spi_message_init(&msg);
xfer[0].tx_buf = send;
xfer[0].rx_buf = NULL;
xfer[0].len = send_cnt;
p = &xfer[0];
spi_message_add_tail(p, &msg);
xfer[1].tx_buf = NULL;
xfer[1].rx_buf = read;
xfer[1].len = read_cnt;
q = &xfer[1];
spi_message_add_tail(q, &msg);
ext_drv_spi_set_cs_level(id, 0);
ret = spi_sync(dev, &msg);
if (ret) {
soc_log_err("spi_sync failed! ret = %d\n", ret);
}
ext_drv_spi_set_cs_level(id, 1);
out:
return ret;
}
td_s32 ext_drv_spi_read_ex_isr(td_u8 id, const td_u8 *send, td_u32 send_cnt, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
ret = ext_drv_spi_read_ex(id, send, send_cnt, read, read_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_read_ex failed\n");
goto out;
}
out:
return ret;
}
/*
* write and read in one transfer. (full duplex)
*/
td_s32 ext_drv_spi_message(td_u8 id, const td_u8 *send, td_u32 send_cnt, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
struct spi_message msg;
struct spi_transfer xfer = {0};
struct spi_transfer *p = NULL;
struct spi_device *dev = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
spi_message_init(&msg);
xfer.tx_buf = send;
xfer.rx_buf = read;
xfer.len = send_cnt;
p = &xfer;
spi_message_add_tail(p, &msg);
ext_drv_spi_set_cs_level(id, 0);
ret = spi_sync(dev, &msg);
if (ret) {
soc_log_err("spi_sync failed! ret = %d\n", ret);
}
ext_drv_spi_set_cs_level(id, 1);
out:
return ret;
}
td_s32 ext_drv_spi_rw_loop(td_u8 id, const td_u8 *send, td_u32 send_cnt, td_u8 *read, td_u32 read_cnt)
{
td_s32 ret = 0;
struct spi_message msg;
struct spi_transfer xfer = {0};
struct spi_device *dev = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, send == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, read == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, read_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, send_cnt != read_cnt, goto out);
dev = g_spi_dummy[id].spi_dev;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, dev == NULL, goto out);
spi_message_init(&msg);
xfer.tx_buf = send;
xfer.rx_buf = read;
xfer.len = send_cnt;
spi_message_add_tail(&xfer, &msg);
ext_drv_spi_set_cs_level(id, 0);
ret = spi_sync(dev, &msg);
if (ret) {
soc_log_err("[rw_loop] spi_sync failed! ret = %d\n", ret);
}
ext_drv_spi_set_cs_level(id, 1);
out:
return ret;
}
static td_s32 _spi_write(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_sdata = NULL;
spi_data_s data;
spi_data_s __user *p_data = (spi_data_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data == NULL, goto out);
if (copy_from_user(&data, p_data, sizeof(spi_data_s))) {
soc_log_info("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data.s_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out);
if (copy_from_user(p_sdata, data.s_data, data.s_data_cnt)) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out1;
}
ret = ext_drv_spi_write_query(data.dev_id, p_sdata, data.s_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_write_query timeout\n");
ret = SOC_ERR_SPI_WRITE_TIMEOUT;
goto out1;
}
out1:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out:
return ret;
}
static td_s32 _spi_read(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_rdata = NULL;
spi_data_s data = {0};
spi_data_s __user *p_data = (spi_data_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data == NULL, goto out);
if (copy_from_user(&data, p_data, sizeof(spi_data_s))) {
soc_log_info("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data.s_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt == 0, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out);
ret = ext_drv_spi_read_query(data.dev_id, p_rdata, data.s_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_read_query failed\n");
ret = SOC_ERR_SPI_READ_TIMEOUT;
goto out1;
}
if (copy_to_user(data.s_data, p_rdata, data.s_data_cnt)) {
soc_log_err("copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out1;
}
out1:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out:
return ret;
}
static td_s32 _spi_read_ex(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_sdata = NULL;
td_u8 *p_rdata = NULL;
spi_dataex_s data_ex = {0};
spi_dataex_s __user *p_data_ex = (spi_dataex_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data_ex == NULL, goto out);
if (copy_from_user(&data_ex, p_data_ex, sizeof(spi_dataex_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data_ex.s_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data_ex.r_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.r_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out1);
if (copy_from_user(p_sdata, data_ex.s_data, data_ex.s_data_cnt)) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
ret = ext_drv_spi_read_ex(data_ex.dev_id, p_sdata, data_ex.s_data_cnt, p_rdata, data_ex.r_data_cnt);
if (ret != TD_SUCCESS) {
ret = SOC_ERR_SPI_READ_TIMEOUT;
goto out2;
}
if (copy_to_user(data_ex.r_data, p_rdata, data_ex.r_data_cnt)) {
ret = SOC_ERR_SPI_COPY_DATA_ERR;
soc_log_err("copy_to_user failed\n");
goto out2;
}
out2:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out1:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out:
return ret;
}
static td_s32 _spi_open(const struct file *file, td_size_t arg, td_size_t *dev_id)
{
td_s32 ret;
ret = ext_drv_spi_open(arg);
if (ret != 0) {
soc_log_err("ext_drv_spi_open failed\n");
return ret;
}
dev_id = file->private_data;
if ((dev_id != NULL) && (arg < MAX_SPI_CHANNEL)) {
dev_id[arg] = 1;
}
return ret;
}
static td_s32 _spi_close(const struct file *file, td_size_t arg, td_size_t *dev_id)
{
td_s32 ret;
ret = ext_drv_spi_close(arg);
if (ret != 0) {
soc_log_err("ext_drv_spi_close failed\n");
return ret;
}
dev_id = file->private_data;
if ((dev_id != NULL) && (arg < MAX_SPI_CHANNEL)) {
dev_id[arg] = 0;
}
return ret;
}
static td_s32 _spi_set_attr(td_size_t arg)
{
spi_fform_s fform = {0};
td_s32 ret = 0;
spi_fform_s __user *p_fform = (spi_fform_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_fform == NULL, goto out);
if (copy_from_user(&fform, p_fform, sizeof(spi_fform_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
if (fform.cscfg) {
ext_drv_spi_set_cs_config(1);
} else {
ext_drv_spi_set_cs_config(0);
}
ext_drv_spi_set_cs_gpio(fform.dev_id);
ret = ext_drv_spi_set_from(fform.dev_id, fform.mode, fform.spo, fform.sph, fform.dss);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_set_from failed\n");
goto out;
}
out:
return ret;
}
static td_s32 _spi_get_attr(td_size_t arg)
{
td_s32 ret = 0;
spi_fform_s fform = {0};
spi_fform_s __user *p_fform = (spi_fform_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_fform == NULL, goto out);
if (copy_from_user(&fform, p_fform, sizeof(spi_fform_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
fform.cscfg = ext_drv_spi_get_cs_config();
ret = ext_drv_spi_get_from(fform.dev_id, &fform.mode, &fform.spo, &fform.sph, &fform.dss);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_get_from failed\n");
goto out;
}
if (copy_to_user(p_fform, &fform, sizeof(spi_fform_s))) {
soc_log_err("copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
out:
return ret;
}
static td_s32 _spi_set_blend(td_size_t arg)
{
td_s32 ret = 0;
spi_blend_s blend = {0};
spi_blend_s __user *p_blend = (spi_blend_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_blend == NULL, goto out);
if (copy_from_user(&blend, p_blend, sizeof(spi_blend_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
ext_drv_spi_set_blend(blend.dev_id, blend.set_bend);
out:
return ret;
}
static td_s32 _spi_get_blend(td_size_t arg)
{
spi_blend_s blend = {0};
td_s32 ret = 0;
spi_blend_s __user *p_blend = (spi_blend_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_blend == NULL, goto out);
if (copy_from_user(&blend, p_blend, sizeof(spi_blend_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
blend.set_bend = ext_drv_spi_get_blend(blend.dev_id);
if (copy_to_user(p_blend, &blend, sizeof(spi_blend_s))) {
soc_log_err("copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
out:
return ret;
}
static td_s32 _spi_set_loop(td_size_t arg)
{
td_s32 ret = 0;
spi_loop_s loop = {0};
spi_loop_s __user *p_loop = (spi_loop_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_loop == NULL, goto out);
if (copy_from_user(&loop, p_loop, sizeof(spi_loop_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
ret = ext_drv_spi_set_loop(loop.dev_id, loop.set_loop);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_set_loop failed\n");
goto out;
}
out:
return ret;
}
static td_s32 _spi_rw_loop(td_size_t arg)
{
td_s32 ret;
td_u8 *p_rdata = NULL;
td_u8 *p_sdata = NULL;
spi_dataex_s data_ex = {0};
spi_dataex_s __user *p_data_ex = (spi_dataex_s __user *)(uintptr_t)arg;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data_ex == NULL, goto out);
if (copy_from_user(&data_ex, p_data_ex, sizeof(spi_dataex_s))) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data_ex.r_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, data_ex.s_data == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.r_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out1);
if (copy_from_user(p_sdata, data_ex.s_data, data_ex.s_data_cnt)) {
soc_log_err("copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
ret = ext_drv_spi_rw_loop(data_ex.dev_id, p_sdata, data_ex.s_data_cnt, p_rdata, data_ex.r_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_rw_loop failed\n");
ret = SOC_ERR_SPI_READ_TIMEOUT;
goto out2;
}
if (copy_to_user(data_ex.r_data, p_rdata, data_ex.r_data_cnt)) {
soc_log_err("copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
out2:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out1:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out:
return ret;
}
#ifdef CONFIG_COMPAT
static td_s32 _spi_compat_write(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_sdata = NULL;
spi_data_compat_s data = {0};
spi_data_compat_s __user *p_data = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data == NULL, goto out);
if (copy_from_user(&data, p_data, sizeof(spi_data_compat_s))) {
soc_log_info("[compat] copy_from_user failed!\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data.s_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out);
if (copy_from_user(p_sdata, (td_u8 *)compat_ptr(data.s_data), data.s_data_cnt)) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out1;
}
ret = ext_drv_spi_write_query(data.dev_id, p_sdata, data.s_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_write_query timeout\n");
ret = SOC_ERR_SPI_WRITE_TIMEOUT;
goto out1;
}
out1:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out:
return ret;
}
static td_s32 _spi_compat_read(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_rdata = NULL;
spi_data_compat_s data = {0};
spi_data_compat_s __user *p_data = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data == NULL, goto out);
if (copy_from_user(&data, p_data, sizeof(spi_data_compat_s))) {
soc_log_info("[compat] ssp read copy data from user failed!\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data.s_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out);
ret = ext_drv_spi_read_query(data.dev_id, p_rdata, data.s_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_read_query timeout\n");
ret = SOC_ERR_SPI_WRITE_TIMEOUT;
goto out1;
}
if (copy_to_user((td_u8 *)compat_ptr(data.s_data), p_rdata, data.s_data_cnt)) {
soc_log_err("[compat] copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out1;
}
out1:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out:
return ret;
}
static td_s32 _spi_compat_read_ex(td_size_t arg)
{
td_s32 ret = 0;
td_u8 *p_sdata = NULL;
td_u8 *p_rdata = NULL;
spi_dataex_compat_s data_ex = {0};
spi_dataex_compat_s __user *p_data_ex = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data_ex == NULL, goto out);
if (copy_from_user(&data_ex, p_data_ex, sizeof(spi_dataex_compat_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data_ex.s_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data_ex.r_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.r_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out1);
if (copy_from_user(p_sdata, (td_u8 *)compat_ptr(data_ex.s_data), data_ex.s_data_cnt)) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
ret = ext_drv_spi_read_ex(data_ex.dev_id, p_sdata, data_ex.s_data_cnt, p_rdata, data_ex.r_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_read_ex failed\n");
ret = SOC_ERR_SPI_READ_TIMEOUT;
goto out2;
}
if (copy_to_user((td_u8 *)compat_ptr(data_ex.r_data), p_rdata, data_ex.r_data_cnt)) {
soc_log_err("[compat] copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
out2:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out1:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out:
return TD_SUCCESS;
}
static td_s32 _spi_compat_set_attr(td_size_t arg)
{
spi_fform_s fform = {0};
td_s32 ret = 0;
spi_fform_s __user *p_fform = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_fform == NULL, goto out);
if (copy_from_user(&fform, p_fform, sizeof(spi_fform_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
if (!fform.cscfg) {
ext_drv_spi_set_cs_config(0);
} else {
ext_drv_spi_set_cs_config(1);
}
ext_drv_spi_set_cs_gpio(fform.dev_id);
ret = ext_drv_spi_set_from(fform.dev_id, fform.mode, fform.spo, fform.sph, fform.dss);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_set_from failed\n");
goto out;
}
out:
return ret;
}
static td_s32 _spi_compat_get_attr(td_size_t arg)
{
td_s32 ret = 0;
spi_fform_s fform = {0};
spi_fform_s __user *p_fform = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_fform == NULL, goto out);
if (copy_from_user(&fform, p_fform, sizeof(spi_fform_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
fform.cscfg = ext_drv_spi_get_cs_config();
ret = ext_drv_spi_get_from(fform.dev_id, &fform.mode, &fform.spo, &fform.sph, &fform.dss);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_get_from failed\n");
goto out;
}
if (copy_to_user(p_fform, &fform, sizeof(spi_fform_s))) {
soc_log_err("[compat] copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
out:
return ret;
}
static td_s32 _spi_compat_set_blend(td_size_t arg)
{
td_s32 ret = 0;
spi_blend_s blend = {0};
spi_blend_s __user *p_blend = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_blend == NULL, goto out);
if (copy_from_user(&blend, p_blend, sizeof(spi_blend_s))) {
ret = SOC_ERR_SPI_COPY_DATA_ERR;
soc_log_err("[compat] copy_from_user failed\n");
goto out;
}
ext_drv_spi_set_blend(blend.dev_id, blend.set_bend);
out:
return ret;
}
static td_s32 _spi_compat_get_blend(td_size_t arg)
{
td_s32 ret = 0;
spi_blend_s blend = {0};
spi_blend_s __user *p_blend = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_blend == NULL, goto out);
if (copy_from_user(&blend, p_blend, sizeof(spi_blend_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
blend.set_bend = ext_drv_spi_get_blend(blend.dev_id);
if (copy_to_user(p_blend, &blend, sizeof(spi_blend_s))) {
soc_log_err("[compat] copy_to_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
out:
return ret;
}
static td_s32 _spi_compat_set_loop(td_size_t arg)
{
td_s32 ret = 0;
spi_loop_s loop = {0};
spi_loop_s __user *p_loop = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_loop == NULL, goto out);
if (copy_from_user(&loop, p_loop, sizeof(spi_loop_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
ret = ext_drv_spi_set_loop(loop.dev_id, loop.set_loop);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_set_loop failed\n");
goto out;
}
out:
return ret;
}
static td_s32 _spi_compat_rw_loop(td_size_t arg)
{
td_u8 *p_sdata = NULL;
td_u8 *p_rdata = NULL;
spi_dataex_compat_s data_ex = {0};
td_s32 ret = 0;
spi_dataex_compat_s __user *p_data_ex = compat_ptr(arg);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, p_data_ex == NULL, goto out);
if (copy_from_user(&data_ex, p_data_ex, sizeof(spi_dataex_compat_s))) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out;
}
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data_ex.r_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.r_data_cnt == 0, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, (td_u8 *)compat_ptr(data_ex.s_data) == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt >= MAX_SPI_TRANSFER_SIZE, goto out);
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, data_ex.s_data_cnt == 0, goto out);
p_sdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.s_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_sdata == NULL, goto out);
p_rdata = SOC_KZALLOC(SOC_ID_SPI, data_ex.r_data_cnt, GFP_KERNEL);
spi_check_param(ret, SOC_ERR_SPI_MALLOC_ERR, p_rdata == NULL, goto out1);
if (copy_from_user(p_sdata, (td_u8 *)compat_ptr(data_ex.s_data), data_ex.s_data_cnt)) {
soc_log_err("[compat] copy_from_user failed\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
ret = ext_drv_spi_rw_loop(data_ex.dev_id, p_sdata, data_ex.s_data_cnt, p_rdata, data_ex.r_data_cnt);
if (ret != TD_SUCCESS) {
soc_log_err("[compat] ext_drv_spi_rw_loop failed\n");
ret = SOC_ERR_SPI_READ_TIMEOUT;
goto out2;
}
if (copy_to_user((td_u8 *)compat_ptr(data_ex.r_data), p_rdata, data_ex.r_data_cnt)) {
soc_log_err("[compat] copy_to_user failed!\n");
ret = SOC_ERR_SPI_COPY_DATA_ERR;
goto out2;
}
out2:
SOC_KFREE(SOC_ID_SPI, p_rdata);
out1:
SOC_KFREE(SOC_ID_SPI, p_sdata);
out:
return ret;
}
#endif
td_s32 ext_drv_spi_ioctl(const struct file *file, td_u32 cmd, td_size_t arg)
{
td_s32 ret = 0;
td_size_t *p_dev_id = NULL;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, file == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, file->private_data == NULL, goto out);
switch (cmd) {
case CMD_SPI_CLOSE:
ret = _spi_close(file, arg, p_dev_id);
break;
case CMD_SPI_OPEN:
ret = _spi_open(file, arg, p_dev_id);
break;
case CMD_SPI_WRITE:
ret = _spi_write(arg);
break;
case CMD_SPI_READ:
ret = _spi_read(arg);
break;
case CMD_SPI_READEX:
ret = _spi_read_ex(arg);
break;
case CMD_SPI_SET_ATTR:
ret = _spi_set_attr(arg);
break;
case CMD_SPI_GET_ATTR:
ret = _spi_get_attr(arg);
break;
case CMD_SPI_SET_BLEND:
ret = _spi_set_blend(arg);
break;
case CMD_SPI_GET_BLEND:
ret = _spi_get_blend(arg);
break;
case CMD_SPI_SET_LOOP:
ret = _spi_set_loop(arg);
break;
case CMD_SPI_RW_LOOP:
ret = _spi_rw_loop(arg);
break;
default:
return -ENOIOCTLCMD;
}
out:
return ret;
}
#ifdef CONFIG_COMPAT
td_s32 ext_drv_spi_compat_ioctl(const struct file *file, td_u32 cmd, td_size_t arg)
{
td_s32 ret = 0;
td_size_t *p_dev_id = NULL;
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, file == NULL, goto out);
spi_check_param(ret, SOC_ERR_SPI_NULL_PTR, file->private_data == NULL, goto out);
switch (cmd) {
case CMD_SPI_OPEN:
ret = _spi_open(file, arg, p_dev_id);
break;
case CMD_SPI_CLOSE:
ret = _spi_close(file, arg, p_dev_id);
break;
case CMD_COMPAT_SPI_WRITE:
ret = _spi_compat_write(arg);
break;
case CMD_COMPAT_SPI_READ:
ret = _spi_compat_read(arg);
break;
case CMD_COMPAT_SPI_READEX:
ret = _spi_compat_read_ex(arg);
break;
case CMD_SPI_SET_ATTR:
ret = _spi_compat_set_attr(arg);
break;
case CMD_SPI_GET_ATTR:
ret = _spi_compat_get_attr(arg);
break;
case CMD_SPI_SET_BLEND:
ret = _spi_compat_set_blend(arg);
break;
case CMD_SPI_GET_BLEND:
ret = _spi_compat_get_blend(arg);
break;
case CMD_SPI_SET_LOOP:
ret = _spi_compat_set_loop(arg);
break;
case CMD_COMPAT_SPI_RW_LOOP:
ret = _spi_compat_rw_loop(arg);
break;
default:
return -ENOIOCTLCMD;
}
out:
return ret;
}
#endif
static td_u32 get_spi_dev_id(const td_char *devname)
{
td_u32 id = 0;
if (devname == NULL) {
soc_log_err("get_spi_dev_id error:%s\n", devname);
return 0;
}
if (sscanf_s(devname, "soc_spi_%u", &id) != 1) {
soc_log_err("get_spi_dev_id sscanf error:%s\n", devname);
return 0;
}
return id;
}
static td_s32 _spi_get_table_from_dts(struct spi_device *spi, const td_char *name, td_u32 *table, td_s32 count)
{
td_s32 ret;
struct device_node *np = spi->controller->dev.of_node;
if (name == NULL || table == NULL) {
soc_log_err("param invalid! \n");
return TD_FAILURE;
}
if (np == NULL) {
soc_log_err("invalid device tree node\n");
return TD_FAILURE;
}
ret = of_property_count_u32_elems(np, name);
if (ret < 0) {
soc_log_err("read %s failed!\n", name);
return TD_FAILURE;
}
if (ret != count) {
soc_log_err("invalid param of %s\n", name);
return TD_FAILURE;
}
ret = of_property_read_u32_array(np, name, table, count);
if (ret < 0) {
soc_log_err("%s of_property_read_u32_array fail!\n", name);
return TD_FAILURE;
}
return TD_SUCCESS;
}
static td_s32 _spi_set_default_clk_rate(struct spi_device *spi)
{
td_s32 ret, count = 0x2;
td_u32 id, table[0x2] = {0};
const td_char *name = "clock-rate";
ret = _spi_get_table_from_dts(spi, name, table, count);
if (ret != TD_SUCCESS) {
soc_log_err("_spi_get_clock_rate_from_dts failed!\n");
return TD_FAILURE;
}
id = get_spi_dev_id(spi->modalias);
if (id >= MAX_SPI_CHANNEL) {
soc_log_err("get_spi_dev_id failed!\n");
return TD_FAILURE;
}
ret = ext_drv_spi_set_clk((td_u8)id, table[0], table[1]);
if (ret != TD_SUCCESS) {
soc_log_err("ext_drv_spi_set_clk failed!\n");
return TD_FAILURE;
}
return TD_SUCCESS;
}
static td_void _spi_set_peri_io_ctrl(struct spi_device *spi)
{
td_s32 ret, count = 0x2;
td_u32 table[0x2] = {0}, *reg_addr = NULL, value, mask;
const td_char *name = "io-ctrl-reg-base-bit";
ret = _spi_get_table_from_dts(spi, name, table, count);
if (ret == TD_SUCCESS) {
reg_addr = osal_ioremap_nocache(table[0], 0x4); /* 0x4 is a size */
value = osal_readl(reg_addr);
mask = BIT(table[1]);
value |= mask;
osal_writel(value, reg_addr);
osal_iounmap(reg_addr, OSAL_IOUNMAP_SIZE);
}
}
/* spi_dev_probe will be called after calling ext_drv_spi_open -> spi_add_device */
static td_s32 spi_dev_probe(struct spi_device *spi)
{
td_s32 ret;
td_u32 id;
if (spi == NULL || spi->modalias == NULL) {
soc_log_err("pointer spi is NULL! \n");
return -1;
}
id = get_spi_dev_id(spi->modalias);
if (id >= MAX_SPI_CHANNEL) {
soc_log_err("spi_dev_probe failed, id:%d\n", id);
return -1;
}
if (down_interruptible(&g_spi_dummy[id].lock)) {
soc_log_err("spi_dev_probe lock failed\n");
return -ERESTARTSYS;
}
g_spi_dummy[id].spi_dev = spi;
up(&g_spi_dummy[id].lock);
_spi_set_peri_io_ctrl(spi);
ret = _spi_set_default_clk_rate(spi);
if (ret != TD_SUCCESS) {
soc_log_err("_spi_set_default_clk_rate failed!\n");
return TD_FAILURE;
}
return 0;
}
static td_s32 spi_dev_remove(struct spi_device *spi)
{
td_u32 id;
if (spi == NULL) {
soc_log_err("pointer spi is NULL! \n");
return -1;
}
id = get_spi_dev_id(spi->modalias);
if (id >= MAX_SPI_CHANNEL) {
soc_log_err("spi_dev_remove failed, id:%d\n", id);
return -1;
}
if (down_interruptible(&g_spi_dummy[id].lock)) {
soc_log_err("spi_dev_remove lock failed\n");
return -ERESTARTSYS;
}
g_spi_dummy[id].spi_dev = NULL;
up(&g_spi_dummy[id].lock);
return 0;
}
static struct spi_device_id g_spi_dev_id_table[] = {
{"soc_spi_0", 0},
{"soc_spi_1", 1},
{"soc_spi_2", 2},
{"soc_spi_3", 3},
{},
};
static struct spi_driver g_spi_driver = {
.driver = {
.name = "soc_spi",
.owner = THIS_MODULE,
},
.id_table = g_spi_dev_id_table,
.probe = spi_dev_probe,
.remove = spi_dev_remove,
};
td_s32 ext_drv_spi_open(td_u8 id)
{
td_s32 ret = 0;
struct spi_master *spi_master = NULL;
struct spi_device *spi_device = NULL;
struct spi_board_info *chip = NULL;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
spi_master = spi_busnum_to_master(id);
spi_check_param(ret, -ENODEV, spi_master == NULL, goto out);
spi_master->rt = true;
spi_device = spi_alloc_device(spi_master);
spi_check_param(ret, -ENOMEM, spi_device == NULL, goto out1);
chip = &g_spi_dummy[id].chip;
spi_device->controller_data = &g_spi_dummy[id].pl022_config_info;
spi_device->max_speed_hz = chip->max_speed_hz;
spi_device->chip_select = chip->chip_select;
spi_device->mode = chip->mode;
spi_device->irq = chip->irq;
spi_device->bits_per_word = g_spi_dummy[id].bits_per_word;
spi_device->controller_state = NULL;
ret = snprintf_s(spi_device->modalias, SPI_NAME_SIZE, SPI_NAME_SIZE - 1, "soc_spi_%d", id);
if (ret < 0) {
soc_log_err("snprintf_s failed\n");
goto out2;
}
ret = spi_add_device(spi_device);
if (ret < 0) {
soc_log_err("spi_add_device failed\n");
goto out2;
}
spi_master_put(spi_master);
goto out;
out2:
spi_dev_put(spi_device);
out1:
spi_master_put(spi_master);
out:
return ret;
}
td_s32 ext_drv_spi_close(td_u8 id)
{
td_s32 ret = 0;
spi_check_param(ret, SOC_ERR_SPI_INVALID_PARA, id >= MAX_SPI_CHANNEL, goto out);
if (g_spi_dummy[id].spi_dev) {
spi_unregister_device(g_spi_dummy[id].spi_dev);
}
out:
return ret;
}
td_s32 ext_drv_spi_init(td_void)
{
td_s32 ret;
td_s32 i;
struct pl022_config_chip *pl022 = NULL;
struct spi_board_info *chip = NULL;
if (atomic_inc_return(&g_spi_init_counter) != 1) {
soc_log_warn("EXT_DRV_SPI already registered:%d\n", atomic_read(&g_spi_init_counter));
return TD_SUCCESS;
}
ret = osal_exportfunc_register(SOC_ID_SPI, "SOC_SPI", NULL);
if (ret != TD_SUCCESS) {
soc_log_err("register failed 0x%x.\n", ret);
return TD_FAILURE;
}
ret = spi_register_driver(&g_spi_driver);
if (ret < 0) {
soc_log_err("spi_register_driver returned %d\n", ret);
return ret;
}
for (i = 0; i < MAX_SPI_CHANNEL; i++) {
sema_init(&g_spi_dummy[i].lock, 1);
pl022 = &g_spi_dummy[i].pl022_config_info;
pl022->com_mode = POLLING_TRANSFER;
pl022->iface = SSP_INTERFACE_MOTOROLA_SPI;
pl022->hierarchy = SSP_MASTER;
pl022->slave_tx_disable = 0;
pl022->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
pl022->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
pl022->ctrl_len = SSP_BITS_8;
pl022->wait_state = SSP_MWIRE_WAIT_ZERO;
pl022->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX;
pl022->clk_freq.cpsdvsr = 0x4;
pl022->clk_freq.scr = 0x1;
chip = &g_spi_dummy[i].chip;
chip->max_speed_hz = 16000000; /* 16000000 : 16MHz */
chip->chip_select = 0; /* chip_select was numberd 0...max */
chip->mode = SPI_MODE_0;
chip->irq = -1;
g_spi_dummy[i].bits_per_word = 0x8;
}
pr_info("ext_drv_spi_init success\n");
return ret;
}
td_s32 ext_drv_spi_deinit(td_void)
{
td_s32 i;
td_s32 ret;
if (!atomic_dec_and_test(&g_spi_init_counter)) {
soc_log_warn("ext_drv_spi_deinit counter:%d\n", atomic_read(&g_spi_init_counter));
return -1;
}
for (i = 0; i < MAX_SPI_CHANNEL; i++) {
if (g_spi_dummy[i].spi_dev) {
spi_unregister_device(g_spi_dummy[i].spi_dev);
}
}
spi_unregister_driver(&g_spi_driver);
ret = osal_exportfunc_unregister(SOC_ID_SPI);
if (ret != TD_SUCCESS) {
soc_log_err("SPI Module unregister failed 0x%x.\n", ret);
return TD_FAILURE;
}
pr_info("ext_drv_spi_deinit success\n");
return ret;
}
EXPORT_SYMBOL(ext_drv_spi_init);
EXPORT_SYMBOL(ext_drv_spi_deinit);
EXPORT_SYMBOL(ext_drv_spi_open);
EXPORT_SYMBOL(ext_drv_spi_close);
EXPORT_SYMBOL(ext_drv_spi_set_blend);
EXPORT_SYMBOL(ext_drv_spi_get_cs_config);
EXPORT_SYMBOL(ext_drv_spi_set_cs_config);
EXPORT_SYMBOL(ext_drv_spi_set_cs_gpio);
EXPORT_SYMBOL(ext_drv_spi_set_cs_level);
EXPORT_SYMBOL(ext_drv_spi_write_query);
EXPORT_SYMBOL(ext_drv_spi_write_isr);
EXPORT_SYMBOL(ext_drv_spi_read_query);
EXPORT_SYMBOL(ext_drv_spi_read_isr);
EXPORT_SYMBOL(ext_drv_spi_read_ex);
EXPORT_SYMBOL(ext_drv_spi_read_ex_isr);
EXPORT_SYMBOL(ext_drv_spi_message);
EXPORT_SYMBOL(ext_drv_spi_set_from);
EXPORT_SYMBOL(ext_drv_spi_set_clk);
EXPORT_SYMBOL(ext_drv_spi_set_loop);
EXPORT_SYMBOL(ext_drv_spi_rw_loop);