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
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);
|