/* * Copyright (c) Hisilicon Technologies Co., Ltd.. 2010-2022. All rights reserved. * Description: */ /******************************************************************************/ /* Includes */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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);