/* * Copyright (c) Hisilicon Technologies Co., Ltd.. 2021-2021. All rights reserved. * Description: loopback test for spi. * Author: Hisilicon * Create: 2021-11-08 */ /********************************************************************************************/ /* Includes */ /********************************************************************************************/ #include #include #include #include #include #include #include "drv_dev_ext.h" #include "drv_ioctl_spi.h" #include "uapi_spi.h" #include "uapi_system.h" /********************************************************************************************/ /* Defines */ /********************************************************************************************/ #define sample_spi_check_param(ret, errno, cmd, param, fmt, ...) do { \ if ((param)) { \ printf("[error] "); \ printf((fmt), ##__VA_ARGS__); \ printf("\n"); \ (ret) = (errno); \ cmd; \ } \ } while (0) #define sample_spi_func_call(ret, func, cmd) do { \ (ret) = (func); \ if ((ret) != TD_SUCCESS) { \ printf("failed!\n"); \ cmd; \ } \ } while (0) #define sample_spi_mutex_lock(mutex) pthread_mutex_lock(mutex) #define sample_spi_mutex_unlock(mutex) pthread_mutex_unlock(mutex) /********************************************************************************************/ /* Enums */ /********************************************************************************************/ typedef enum { SAMPLE_SPI_DEV_0 = 0, SAMPLE_SPI_DEV_1 = 1, SAMPLE_SPI_DEV_2 = 2, SAMPLE_SPI_DEV_3 = 3, SAMPLE_SPI_DEV_MAX } sample_spi_device; typedef enum { SAMPLE_SPI_LOGIC_CS = 0, SAMPLE_SPI_GPIO_CS = 1, } sample_spi_config_cs; typedef enum { SAMPLE_SPI_FORMAT_MOTO = 0, SAMPLE_SPI_FORMAT_TI = 1, SAMPLE_SPI_FORMAT_NM = 2, SAMPLE_SPI_FORMAT_MAX = 3 } sample_spi_frame_format; typedef enum { SAMPLE_SPI_SPO_0 = 0, SAMPLE_SPI_SPO_1 = 1, SAMPLE_SPI_SPO_2 = 2, SAMPLE_SPI_SPO_3 = 3, } sample_spi_clockout_polarity; typedef enum { SAMPLE_SPI_SPH_0 = 0, SAMPLE_SPI_SPH_1 = 1, SAMPLE_SPI_SPH_2 = 2, SAMPLE_SPI_SPH_3 = 3, } sample_spi_clockout_phase; /********************************************************************************************/ /* Structures */ /********************************************************************************************/ typedef struct { sample_spi_clockout_polarity spo; sample_spi_clockout_phase sph; } sample_spi_attr_motorola; typedef struct { td_bool wait_enable; td_u32 wait_value; } sample_spi_attr_national_microwire; /* SPI additional attribute union */ /* CNcomment: Motorola SPI/NM 协议专有属性 */ typedef union { sample_spi_attr_motorola motorola; sample_spi_attr_national_microwire national_microwire; } sample_spi_attr_ext; typedef struct { /* chip select */ sample_spi_device device; /* cs select */ sample_spi_config_cs cs_cfg; /* baud rate */ td_u32 baud; /* frame format */ sample_spi_frame_format frame_format; /* number of bits per transfer, 4-15bit, value of dss : [4, 15]. */ td_u8 dss; /* little or big endian */ td_bool is_bigend; /* addition attr when frame_format is motorola or national_microwire. */ sample_spi_attr_ext ext_attr; /* loopback mode */ td_bool loopback; } sample_spi_attr; /********************************************************************************************/ /* Globals */ /********************************************************************************************/ static td_s32 g_spi_dev_fd = -1; static pthread_mutex_t g_spi_mutex = PTHREAD_MUTEX_INITIALIZER; static sample_spi_attr g_attr = { .device = SAMPLE_SPI_DEV_MAX, .cs_cfg = SAMPLE_SPI_LOGIC_CS, .baud = 0x8, .frame_format = SAMPLE_SPI_FORMAT_MOTO, .dss = 0x8, .is_bigend = TD_TRUE, .loopback = TD_FALSE }; /******************************************************************************************** Function Implementation ********************************************************************************************/ static td_s32 sample_spi_check_attr(const sample_spi_attr *attr) { if (attr == TD_NULL) { soc_log_err("attr is null!\n"); return SOC_ERR_SPI_NULL_PTR; } if (attr->device >= SAMPLE_SPI_DEV_MAX) { soc_log_err("invalid device id %d\n", attr->device); return SOC_ERR_SPI_INVALID_PARA; } if ((attr->cs_cfg != SAMPLE_SPI_LOGIC_CS) && (attr->cs_cfg != SAMPLE_SPI_GPIO_CS)) { soc_log_err("invalid cs cfg %d\n", attr->cs_cfg); return SOC_ERR_SPI_INVALID_PARA; } if (attr->frame_format >= SAMPLE_SPI_FORMAT_MAX) { soc_log_err("invalid frame format %d\n", attr->frame_format); return SOC_ERR_SPI_INVALID_PARA; } /* dss is between 4 and 15 */ if ((attr->dss < 4) || (attr->dss > 15)) { soc_log_err("invalid dss %d\n", attr->dss); return SOC_ERR_SPI_INVALID_PARA; } if ((attr->is_bigend != TD_TRUE) && (attr->is_bigend != TD_FALSE)) { soc_log_err("invalid bigend %d\n", attr->is_bigend); return SOC_ERR_SPI_INVALID_PARA; } return TD_SUCCESS; } static td_s32 sample_spi_check_open(td_void) { td_s32 ret = TD_SUCCESS; td_s32 ret1; ret1 = sample_spi_mutex_lock(&g_spi_mutex); if (ret1 != 0) { soc_log_err("spi lock failed! ret = 0x%x\n", ret1); goto out; } if (g_spi_dev_fd < 0) { soc_log_err("spi is not opened!\n"); ret = SOC_ERR_SPI_NOT_INIT; goto unlock; } unlock: ret1 = sample_spi_mutex_unlock(&g_spi_mutex); if (ret1 != 0) { soc_log_err("spi unlock failed! ret = 0x%x\n", ret1); goto out; } out: return (ret != TD_SUCCESS) ? ret : ((ret1 != 0) ? ret1 : ret); } static td_s32 sample_spi_set_fform(sample_spi_device device, const sample_spi_attr *attr) { td_s32 ret; sample_spi_attr tmp_attr = {0}; spi_fform_s fform = {0}; tmp_attr = *attr; fform.dev_id = device; fform.mode = tmp_attr.frame_format; fform.spo = tmp_attr.ext_attr.motorola.spo; fform.sph = tmp_attr.ext_attr.motorola.sph; fform.dss = tmp_attr.dss; fform.cscfg = tmp_attr.cs_cfg; ret = ioctl(g_spi_dev_fd, CMD_SPI_SET_ATTR, &fform); if (ret != 0) { soc_log_err("set frame failed! ret = 0x%x\n", ret); return ret; } return ret; } static td_s32 sample_spi_set_blend(sample_spi_device device, const sample_spi_attr *attr) { td_s32 ret; sample_spi_attr tmp_attr = {0}; spi_blend_s bigend = {0}; tmp_attr = *attr; bigend.dev_id = device; bigend.set_bend = tmp_attr.is_bigend; ret = ioctl(g_spi_dev_fd, CMD_SPI_SET_BLEND, &bigend); if (ret != 0) { soc_log_err("set big-litten endian failed! ret = 0x%x\n", ret); return ret; } return ret; } static td_s32 sample_spi_set_loopback(sample_spi_device device, const sample_spi_attr *attr) { td_s32 ret; sample_spi_attr tmp_attr = {0}; spi_loop_s loopback = {0}; tmp_attr = *attr; loopback.dev_id = device; loopback.set_loop = (td_u8)tmp_attr.loopback; ret = ioctl(g_spi_dev_fd, CMD_SPI_SET_LOOP, &loopback); if (ret != 0) { soc_log_err("set loopback mode failed!\n"); return ret; } return ret; } static td_s32 sample_spi_loopback(sample_spi_device device, const td_u8 *write, td_u32 write_count, td_u8 *read, td_u32 read_count) { td_s32 ret; spi_dataex_s data = {0}; data.dev_id = device; data.s_data = (td_u8 *)write; data.s_data_cnt = write_count; data.r_data = read; data.r_data_cnt = read_count; ret = ioctl(g_spi_dev_fd, CMD_SPI_RW_LOOP, &data); if (ret != 0) { soc_log_err("spi loopback failed! ret = 0x%x\n", ret); return ret; } return ret; } static td_s32 sample_spi_init(td_void) { return TD_SUCCESS; } static td_s32 sample_spi_deinit(td_void) { return TD_SUCCESS; } static td_s32 sample_spi_open(sample_spi_device device) { td_s32 ret; if (device >= SAMPLE_SPI_DEV_MAX) { soc_log_err("[spi_open] invalid device %d\n", device); ret = SOC_ERR_SPI_INVALID_PARA; goto out; } ret = sample_spi_mutex_lock(&g_spi_mutex); if (ret != 0) { soc_log_err("spi lock failed! ret = 0x%x\n", ret); goto out; } if (g_spi_dev_fd > 0) { ret = TD_SUCCESS; goto unlock; } g_spi_dev_fd = open("/dev/" SOC_DEV_SPI_NAME, O_RDWR, 0); if (g_spi_dev_fd < 0) { soc_log_fatal("open spi failed!\n"); ret = SOC_ERR_SPI_OPEN_ERR; goto unlock; } ret = ioctl(g_spi_dev_fd, CMD_SPI_OPEN, device); if (ret != 0) { soc_log_err("open spi failed! ret = 0x%x\n", ret); ret = SOC_ERR_SPI_OPEN_ERR; goto unlock; } unlock: ret = sample_spi_mutex_unlock(&g_spi_mutex); if (ret != 0) { soc_log_err("spi unlock failed! ret = 0x%x\n", ret); goto out; } out: return ret; } static td_s32 sample_spi_close(sample_spi_device device) { td_s32 ret; if (device >= SAMPLE_SPI_DEV_MAX) { soc_log_err("[spi_close] invalid device %d\n", device); ret = SOC_ERR_SPI_INVALID_PARA; goto out; } ret = sample_spi_mutex_lock(&g_spi_mutex); if (ret != 0) { soc_log_err("spi lock failed! ret = 0x%x\n", ret); goto out; } if (g_spi_dev_fd < 0) { ret = TD_SUCCESS; goto unlock; } ret = ioctl(g_spi_dev_fd, CMD_SPI_CLOSE, device); if (ret != 0) { soc_log_err("close spi failed! ret = 0x%x\n", ret); ret = SOC_ERR_SPI_CLOSE_ERR; goto unlock; } ret = close(g_spi_dev_fd); if (ret != 0) { soc_log_err("close spi failed! ret = 0x%x\n", ret); ret = SOC_ERR_SPI_CLOSE_ERR; goto unlock; } g_spi_dev_fd = -1; unlock: ret = sample_spi_mutex_unlock(&g_spi_mutex); if (ret != 0) { soc_log_err("spi unlock failed! ret = 0x%x\n", ret); goto out; } out: return ret; } static td_s32 sample_spi_set_attr(sample_spi_device device) { td_s32 ret; g_attr.device = device; if (device >= SAMPLE_SPI_DEV_MAX) { soc_log_err("invalid device %d\n", device); return SOC_ERR_SPI_INVALID_PARA; } ret = sample_spi_check_attr(&g_attr); if (ret != TD_SUCCESS) { soc_log_err("sample_spi_check_attr failed! ret = 0x%x\n", ret); return ret; } ret = sample_spi_check_open(); if (ret != TD_SUCCESS) { soc_log_err("sample_spi_check_open failed! ret = 0x%x\n", ret); return ret; } ret = sample_spi_set_fform(device, &g_attr); if (ret != TD_SUCCESS) { soc_log_err("sample_spi_set_fform failed! ret = 0x%x\n", ret); return ret; } ret = sample_spi_set_blend(device, &g_attr); if (ret != TD_SUCCESS) { soc_log_err("sample_spi_set_blend failed! ret = 0x%x\n", ret); return ret; } return ret; } static td_void sample_spi_help(td_void) { printf("Usage: sample_spi_loopback spi_channel data_num byte0 [... byten]\n"); } static td_s32 sample_spi_get_param(td_s32 argc, td_char **argv, td_u32 *spi_num, td_u32 *data_num) { td_s32 ret = TD_SUCCESS; sample_spi_check_param(ret, TD_FAILURE, goto help, argc < 0x3, "arg must be bigger than 3"); sample_spi_check_param(ret, TD_FAILURE, goto help, argv == TD_NULL, "argv is null"); sample_spi_check_param(ret, TD_FAILURE, goto help, spi_num == TD_NULL, "spi_num is null"); sample_spi_check_param(ret, TD_FAILURE, goto help, data_num == TD_NULL, "data_num is null"); *spi_num = (td_u32)strtoul(argv[0x1], TD_NULL, 0); sample_spi_check_param(ret, TD_FAILURE, goto help, *spi_num >= UAPI_SPI_DEV_MAX, "spi num must be less than %d", UAPI_SPI_DEV_MAX); *data_num = (td_u32)strtoul(argv[0x2], TD_NULL, 0); sample_spi_check_param(ret, TD_FAILURE, goto help, *data_num == 0, "data number is 0!"); sample_spi_check_param(ret, TD_FAILURE, goto help, (*data_num + 0x3) > (td_u32)argc, "data number do not match the following data"); help: sample_spi_help(); return ret; } td_s32 main(td_s32 argc, td_char **argv) { td_s32 ret, ret1, i; td_u8 *send_data = TD_NULL; td_u8 *recv_data = TD_NULL; td_u32 spi_num, data_num, loop; sample_spi_func_call(ret, sample_spi_get_param(argc, argv, &spi_num, &data_num), goto out); send_data = (td_u8 *)malloc(data_num); sample_spi_check_param(ret, TD_FAILURE, goto out, send_data == TD_NULL, "Allocate send data failed!"); recv_data = (td_u8 *)malloc(data_num); sample_spi_check_param(ret, TD_FAILURE, goto free_s_data, recv_data == TD_NULL, "Allocate recv data failed!"); sample_spi_func_call(ret, uapi_sys_init(), goto free_r_data); sample_spi_func_call(ret, sample_spi_init(), goto sys_deinit); sample_spi_func_call(ret, sample_spi_open(spi_num), goto spi_deinit); sample_spi_func_call(ret, sample_spi_set_attr(spi_num), goto spi_close); for (loop = 0; loop < data_num; loop++) { send_data[loop] = (td_u8)strtoul(argv[loop + 0x3], TD_NULL, 0); } g_attr.loopback = TD_TRUE; sample_spi_func_call(ret, sample_spi_set_loopback(spi_num, &g_attr), goto spi_close); sample_spi_func_call(ret, sample_spi_loopback(spi_num, send_data, data_num, recv_data, data_num), goto spi_close); printf("spi read: "); for (i = 0; i < data_num; i++) { printf("0x%x ", recv_data[i]); } printf("\n"); spi_close: sample_spi_func_call(ret1, sample_spi_close(spi_num), goto spi_deinit); spi_deinit: sample_spi_func_call(ret1, sample_spi_deinit(), goto sys_deinit); sys_deinit: sample_spi_func_call(ret1, uapi_sys_deinit(), goto free_r_data); free_r_data: free(recv_data); recv_data = TD_NULL; free_s_data: free(send_data); send_data = TD_NULL; out: return ret; }