/* * SPI controller driver for the nordic52832 SoCs * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "aphost.h" static struct js_spi_client *gspi_client; static cp_buffer_t *u_packet; static void d_packet_set_instance(cp_buffer_t *in) { if (gspi_client == NULL) pr_err("js %s: drv init err\n", __func__); spin_lock(&gspi_client->smem_lock); if (in == NULL) u_packet = NULL; else { u_packet = in; u_packet->c_head = -1; u_packet->p_head = -1; } spin_unlock(&gspi_client->smem_lock); if (in == NULL) pr_debug("js %s: release mem\n", __func__); else pr_debug("js %s: alloc mem\n", __func__); } void js_irq_enable(struct js_spi_client *spi_client, bool enable) { if (spi_client->irqstate == enable) { pr_debug("js irq already =%d\n", enable); return; } pr_debug("js irq en =%d\n", enable); if (enable) enable_irq(spi_client->js_irq); else disable_irq(spi_client->js_irq); spi_client->irqstate = enable; } static void js_set_power(int jspower) { if (gspi_client) { if (gspi_client->powerstate != jspower) { if (jspower == 0) { /* off */ gpio_set_value(gspi_client->js_v1p8en_gpio, 0); gspi_client->powerstate = 0; js_irq_enable(gspi_client, false); } else if (jspower == 1) { /* normal on */ gpio_set_value(gspi_client->js_v1p8en_gpio, 1); gspi_client->powerstate = 1; js_irq_enable(gspi_client, true); } } } } static ssize_t jsmem_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", gspi_client->memfd); } static ssize_t jsmem_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; cp_buffer_t * inbuf; ret = kstrtoint(buf, 10, &gspi_client->memfd); if (ret < 0) return ret; mutex_lock(&gspi_client->js_sm_mutex); if (gspi_client->memfd == -1) { if (IS_ERR_OR_NULL(gspi_client->vaddr)) goto __end; d_packet_set_instance(NULL); dma_buf_kunmap(gspi_client->js_buf, 0, gspi_client->vaddr); dma_buf_end_cpu_access(gspi_client->js_buf, DMA_BIDIRECTIONAL); dma_buf_put(gspi_client->js_buf); gspi_client->vaddr = NULL; gspi_client->js_buf = NULL; } else { gspi_client->js_buf = dma_buf_get(gspi_client->memfd); if (IS_ERR_OR_NULL(gspi_client->js_buf)) { ret = -ENOMEM; pr_err("[%s]dma_buf_get failed for fd: %d\n", __func__, gspi_client->memfd); goto __end; } ret = dma_buf_begin_cpu_access(gspi_client->js_buf, DMA_BIDIRECTIONAL); if (ret) { pr_err("[%s]: dma_buf_begin_cpu_access failed\n", __func__); dma_buf_put(gspi_client->js_buf); gspi_client->js_buf = NULL; goto __end; } gspi_client->vsize = gspi_client->js_buf->size; gspi_client->vaddr = dma_buf_kmap(gspi_client->js_buf, 0); if (IS_ERR_OR_NULL(gspi_client->vaddr)) { dma_buf_end_cpu_access(gspi_client->js_buf, DMA_BIDIRECTIONAL); dma_buf_put(gspi_client->js_buf); gspi_client->js_buf = NULL; pr_err("[%s]dma_buf_kmap failed for fd: %d\n", __func__, gspi_client->memfd); goto __end; } inbuf = (cp_buffer_t *)gspi_client->vaddr; d_packet_set_instance(inbuf); } __end: mutex_unlock(&gspi_client->js_sm_mutex); return count; } static DEVICE_ATTR_RW(jsmem); static ssize_t jsrequest_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned int input = 0; acknowledge_t nordicAck; int size = 0; if (gspi_client == NULL) { pr_err("invalid gspi_client\n"); return size; } mutex_lock(&gspi_client->js_mutex); memset(&nordicAck, 0, sizeof(acknowledge_t)); input = atomic_read(&gspi_client->nordicAcknowledge); atomic_set(&gspi_client->nordicAcknowledge, 0); nordicAck.acknowledgeHead.requestType = ((input & 0x7f000000) >> 24); nordicAck.acknowledgeHead.ack = ((input & 0x80000000) >> 31); nordicAck.acknowledgeData[0] = (input & 0x000000ff); nordicAck.acknowledgeData[1] = ((input & 0x0000ff00) >> 8); nordicAck.acknowledgeData[2] = ((input & 0x00ff0000) >> 16); if (nordicAck.acknowledgeHead.ack == 1) { switch (nordicAck.acknowledgeHead.requestType) { case getMasterNordicVersionRequest: size = scnprintf(buf, PAGE_SIZE, "masterNordic fwVersion:%d.%d\n", nordicAck.acknowledgeData[1], nordicAck.acknowledgeData[0]); break; case bondJoyStickRequest: case disconnectJoyStickRequest: case setVibStateRequest: case hostEnterDfuStateRequest: size = scnprintf(buf, PAGE_SIZE, "requestType:%d ack:%d\n", nordicAck.acknowledgeHead.requestType, nordicAck.acknowledgeHead.ack); break; case getJoyStickBondStateRequest: gspi_client->JoyStickBondState = (nordicAck.acknowledgeData[0] & 0x03); size = scnprintf(buf, PAGE_SIZE, "left/right joyStick bond state:%d:%d\n", (gspi_client->JoyStickBondState & 0x01), ((gspi_client->JoyStickBondState & 0x02) >> 1)); break; case getLeftJoyStickProductNameRequest: size = scnprintf(buf, PAGE_SIZE, "leftJoyStick productNameID:%d\n", nordicAck.acknowledgeData[0]); break; case getRightJoyStickProductNameRequest: size = scnprintf(buf, PAGE_SIZE, "rightJoyStick productNameID:%d\n", nordicAck.acknowledgeData[0]); break; case getLeftJoyStickFwVersionRequest: size = scnprintf(buf, PAGE_SIZE, "leftJoyStick fwVersion:%d.%d\n", nordicAck.acknowledgeData[1], nordicAck.acknowledgeData[0]); break; case getRightJoyStickFwVersionRequest: size = scnprintf(buf, PAGE_SIZE, "rightJoyStick fwVersion:%d.%d\n", nordicAck.acknowledgeData[1], nordicAck.acknowledgeData[0]); break; default: size = scnprintf(buf, PAGE_SIZE, "invalid requestType\n"); break; } } else { size = scnprintf(buf, PAGE_SIZE, "no need to ack\n"); } pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.active); gspi_client->js_ledl_state = 0; mutex_unlock(&gspi_client->js_mutex); return size; } static ssize_t jsrequest_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned int input = 0; request_t request; int vibState = 0; int err = 0; #ifdef COMPATIBLE_NOT_SUPPORT_DFU unsigned int getNorVersion = 0; #endif if (gspi_client == NULL) { pr_err("invalid gspi_client\n"); return size; } #ifdef COMPATIBLE_NOT_SUPPORT_DFU getNorVersion = (unsigned int) atomic_read(&gspi_client->probeGetNordicVersion); if (getNorVersion <= 0x81000101 && getNorVersion > 0) { pr_debug("%s DFU not supported version number:%d.%d\n", __func__, (getNorVersion >> 8) & 0xff, getNorVersion & 0xff); } else { pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledl_state = 1; pr_debug("%s DFU supported version number:%d.%d\n", __func__, (getNorVersion >> 8) & 0xff, getNorVersion & 0xff); } #else pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledl_state = 1; #endif mutex_lock(&gspi_client->js_mutex); err = kstrtouint(buf, 16, &input); if (err) { pr_err("invalid param\n"); } else { memset(&request, 0, sizeof(request_t)); request.requestHead.requestType = ((input & 0x7f000000) >> 24); if (request.requestHead.requestType == 0xc) { request.requestData[0] = (input & 0x000000ff); request.requestData[1] = ((input & 0x0000ff00) >> 8); request.requestData[2] = ((input & 0x00ff0000) >> 16); } else { request.requestData[0] = (input & 0x000000ff); request.requestData[1] = (input & 0x0000ff00); request.requestData[2] = (input & 0x00ff0000); } switch (request.requestHead.requestType) { case setVibStateRequest: vibState = ((request.requestData[1] << 8) | request.requestData[0]); if (vibState >= 0 && vibState <= 0xffff) { atomic_set( &gspi_client->userRequest, input); atomic_inc( &gspi_client->dataflag); wake_up_interruptible( &gspi_client->wait_queue); } else { pr_err("invalid vibState\n"); memset(&gspi_client->userRequest, 0, sizeof(gspi_client->userRequest)); } break; case getMasterNordicVersionRequest: case bondJoyStickRequest: case disconnectJoyStickRequest: case getJoyStickBondStateRequest: case hostEnterDfuStateRequest: case getLeftJoyStickProductNameRequest: case getRightJoyStickProductNameRequest: case getLeftJoyStickFwVersionRequest: case getRightJoyStickFwVersionRequest: case setControllerSleepMode: atomic_set(&gspi_client->userRequest, input); atomic_inc(&gspi_client->dataflag); wake_up_interruptible( &gspi_client->wait_queue); break; default: pr_err("invalid requestType\n"); memset(&gspi_client->userRequest, 0, sizeof(gspi_client->userRequest)); } } mutex_unlock(&gspi_client->js_mutex); return size; } static DEVICE_ATTR_RW(jsrequest); #ifdef MANUL_CONTROL_JOYSTICK_RLED static void js_rled_enable(u8 mask) { if (gspi_client) { mask = (3 - mask); switch (mask) { case 0: if ((gspi_client->js_ledr_state == 0) || (gspi_client->js_ledl_state == 0)) { pr_debug("enable\n"); pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.active); gspi_client->js_ledr_state = 1; gspi_client->js_ledl_state = 0; } break; case 3: if ((gspi_client->js_ledr_state == 1) || (gspi_client->js_ledl_state == 1)) { pr_debug("disable\n"); pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledr_state = 0; gspi_client->js_ledl_state = 0; } break; } } } static ssize_t jsrled_show(struct device *dev, struct device_attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", ((gspi_client->js_ledr_state << 1) | (gspi_client->js_ledl_state))); } static ssize_t jsrled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rledflag = 0; int err = 0; mutex_lock(&gspi_client->js_rled_mutex); err = kstrtouint(buf, 16, &rledflag); if (err) { pr_err("invalid param\n"); mutex_unlock(&gspi_client->js_rled_mutex); return err; } js_rled_enable(rledflag); mutex_unlock(&gspi_client->js_rled_mutex); return count; } static DEVICE_ATTR_RW(jsrled); #endif static int js_spi_txfr(struct spi_device *spi, char *txbuf, char *rxbuf, int num_byte, uint64_t *tts) { int ret = 0; struct spi_transfer txfr; struct spi_message msg; memset(&txfr, 0, sizeof(txfr)); txfr.tx_buf = txbuf; txfr.rx_buf = rxbuf; txfr.len = num_byte; spi_message_init(&msg); spi_message_add_tail(&txfr, &msg); *tts = ktime_to_ns(ktime_get_boottime()); ret = spi_sync(spi, &msg); if (ret < 0) pr_err("%s err=%d\n", __func__, ret); return ret; } static int js_thread(void *data) { int ret; unsigned char *pbuf; uint64_t tts; uint32_t tth[8]; uint64_t tto[8]; int num = 0; int pksz = 0; int index = 0; uint32_t hosttime; bool skiprport = false; unsigned int input = 0; request_t currentRequest; static request_t lastRequest; acknowledge_t nordicAck; uint8_t val = 0; struct js_spi_client *spi_client = (struct js_spi_client *)data; struct sched_param param = { .sched_priority = 88 }; sched_setscheduler(current, SCHED_FIFO, ¶m); pr_debug("%s: start\n", __func__); do { skiprport = false; ret = wait_event_interruptible(spi_client->wait_queue, atomic_read(&spi_client->dataflag) || kthread_should_stop()); if ((ret < 0) || kthread_should_stop()) { pr_err("%s: exit\n", __func__); break; } atomic_set(&spi_client->dataflag, 0); if (spi_client->powerstate != 1) { msleep(100); continue; } input = (unsigned int)atomic_read(&gspi_client->userRequest); val = gpio_get_value(spi_client->js_irq_gpio); /* Filter out the exception trigger */ if (val == 0 && input == 0) continue; memset(¤tRequest, 0, sizeof(request_t)); currentRequest.requestHead.needAck = ((input & 0x80000000) >> 31); currentRequest.requestHead.requestType = ((input & 0x7f000000) >> 24); currentRequest.requestData[0] = (input & 0x000000ff); currentRequest.requestData[1] = ((input & 0x0000ff00) >> 8); currentRequest.requestData[2] = ((input & 0x00ff0000) >> 16); memset(spi_client->txbuffer, 0, sizeof(spi_client->txbuffer)); memset(spi_client->rxbuffer, 0, sizeof(spi_client->rxbuffer)); spi_client->txbuffer[0] = CMD_REQUEST_TAG; spi_client->txbuffer[1] = ((currentRequest.requestHead.needAck << 7) | currentRequest.requestHead.requestType); switch (currentRequest.requestHead.requestType) { case setVibStateRequest: spi_client->txbuffer[2] = currentRequest.requestData[0]; spi_client->txbuffer[3] = currentRequest.requestData[1]; break; case bondJoyStickRequest: case disconnectJoyStickRequest: spi_client->txbuffer[2] = (currentRequest.requestData[0]&0x01); break; case setControllerSleepMode: spi_client->txbuffer[2] = currentRequest.requestData[0]; spi_client->txbuffer[3] = currentRequest.requestData[1]; break; default: break; } if (spi_client->powerstate == 1) { ret = js_spi_txfr(spi_client->spi_client, spi_client->txbuffer, spi_client->rxbuffer, XFR_SIZE, &tts); if (ret != 0) continue; } else continue; /* Filtering dirty Data */ if (spi_client->rxbuffer[4] == 0xff) continue; if (lastRequest.requestHead.needAck == 1) { memset(&nordicAck, 0, sizeof(acknowledge_t)); nordicAck.acknowledgeHead.ack = ((spi_client->rxbuffer[0] & 0x80) >> 7); nordicAck.acknowledgeHead.requestType = (spi_client->rxbuffer[0] & 0x7f); nordicAck.acknowledgeData[0] = spi_client->rxbuffer[1]; nordicAck.acknowledgeData[1] = spi_client->rxbuffer[2]; nordicAck.acknowledgeData[2] = spi_client->rxbuffer[3]; if (lastRequest.requestHead.requestType == nordicAck.acknowledgeHead.requestType) { unsigned int input = 0; input = ((spi_client->rxbuffer[0] << 24) | (spi_client->rxbuffer[3] << 16) | (spi_client->rxbuffer[2] << 8) | spi_client->rxbuffer[1]); atomic_set(&spi_client->nordicAcknowledge, input); #ifdef COMPATIBLE_NOT_SUPPORT_DFU if ((unsigned int) atomic_read( &gspi_client->probeGetNordicVersionFlag)) { atomic_set( &spi_client->probeGetNordicVersion, input); atomic_set( &spi_client->probeGetNordicVersionFlag, 0); } #endif } memset(&lastRequest, 0, sizeof(lastRequest)); } /* left or right joyStick are bound */ if ((gspi_client->JoyStickBondState & 0x3) != 0 && input == 0) { pksz = spi_client->rxbuffer[4]; num = spi_client->rxbuffer[5]; if (num == 0 || pksz != 30) { skiprport = true; } memcpy(&hosttime, &spi_client->rxbuffer[6], 4); tts = spi_client->tsHost; pbuf = &spi_client->rxbuffer[10]; if (!skiprport) { spin_lock(&gspi_client->smem_lock); for (index = 0; index < num; index++) { memcpy(&tth[index], pbuf, 4); tto[index] = tts - (hosttime-tth[index]) * 100000; if ((u_packet) && (spi_client->vaddr)) { int8_t p_head; d_packet_t *pdata; p_head = (u_packet->p_head + 1) % MAX_PACK_SIZE; pdata = &u_packet->data[p_head]; pdata->ts = tto[index]; pdata->size = pksz - 4; memcpy((void *)pdata->data, (void *)(pbuf+4), pksz-4); u_packet->p_head = p_head; } pbuf += pksz; } spin_unlock(&gspi_client->smem_lock); } } if (currentRequest.requestHead.requestType != 0) atomic_set(&gspi_client->userRequest, 0); memcpy(&lastRequest, ¤tRequest, sizeof(currentRequest)); } while (1); return 0; } static int js_pinctrl_init(struct js_spi_client *spi_client) { int rc = 0; spi_client->pinctrl_info.pinctrl = devm_pinctrl_get(&spi_client->spi_client->dev); if (IS_ERR_OR_NULL(spi_client->pinctrl_info.pinctrl)) { rc = PTR_ERR(spi_client->pinctrl_info.pinctrl); pr_err("failed pinctrl, rc=%d\n", rc); goto error; } spi_client->pinctrl_info.active = pinctrl_lookup_state(spi_client->pinctrl_info.pinctrl, "nordic_default"); if (IS_ERR_OR_NULL(spi_client->pinctrl_info.active)) { rc = PTR_ERR(spi_client->pinctrl_info.active); pr_err("failed pinctrl active state, rc=%d\n", rc); goto error; } spi_client->pinctrl_info.suspend = pinctrl_lookup_state(spi_client->pinctrl_info.pinctrl, "nordic_sleep"); if (IS_ERR_OR_NULL(spi_client->pinctrl_info.suspend)) { rc = PTR_ERR(spi_client->pinctrl_info.suspend); pr_err("failed pinctrl suspend state, rc=%d\n", rc); goto error; } pr_debug("%s ok\n", __func__); rc = pinctrl_select_state(spi_client->pinctrl_info.pinctrl, spi_client->pinctrl_info.active); if (rc) { pr_err("pinctrl_select_state failed:%d\n", rc); } else { spi_client->js_ledl_state = 1; spi_client->js_ledr_state = 1; } pr_debug("%s init successfully\n", __func__); error: return rc; } static int js_parse_gpios(struct js_spi_client *spi_client) { int rc = 0; struct device_node *of_node = spi_client->spi_client->dev.of_node; spi_client->js_v1p8en_gpio = of_get_named_gpio(of_node, "nordic,v1p8en-gpio", 0); if (!gpio_is_valid(spi_client->js_v1p8en_gpio)) { pr_err("failed get js_v1p8en_gpio gpio, rc=%d\n", rc); rc = -EINVAL; goto error; } spi_client->js_irq_gpio = of_get_named_gpio(of_node, "nordic,irq-gpio", 0); if (!gpio_is_valid(spi_client->js_irq_gpio)) { pr_err("failed get js_irq_gpio gpio, rc=%d\n", rc); rc = -EINVAL; goto error; } spi_client->js_ledl_gpio = of_get_named_gpio(of_node, "nordic,ledl-gpio", 0); if (!gpio_is_valid(spi_client->js_ledl_gpio)) pr_err("failed get js_ledl_gpio gpio, rc=%d\n", rc); spi_client->js_ledr_gpio = of_get_named_gpio(of_node, "nordic,ledr-gpio", 0); if (!gpio_is_valid(spi_client->js_ledr_gpio)) pr_err("failed get js_ledr_gpio gpio, rc=%d\n", rc); error: return rc; } static int js_gpio_request(struct js_spi_client *spi_client) { int rc = 0; if (gpio_is_valid(spi_client->js_v1p8en_gpio)) { rc = gpio_request(spi_client->js_v1p8en_gpio, "nordic_v1p8en_gpio"); if (rc) { pr_err("req for noric_v1p8en_gpio failed, rc=%d\n", rc); goto error; } } if (gpio_is_valid(spi_client->js_irq_gpio)) { pr_debug("request for js_irq_gpio =%d\n", spi_client->js_irq_gpio); rc = gpio_request(spi_client->js_irq_gpio, "nordic_irq_gpio"); if (rc) { gpio_free(spi_client->js_v1p8en_gpio); pr_err("request for js_irq_gpio failed, rc=%d\n", rc); goto error; } } pr_debug("%s ok\n", __func__); error: return rc; } static irqreturn_t js_irq_handler(int irq, void *dev_id) { int val = 0; struct js_spi_client *spi_client = (struct js_spi_client *)dev_id; if (spi_client->powerstate == 1) { val = gpio_get_value(spi_client->js_irq_gpio); if (val == 1) { spi_client->tsHost = ktime_to_ns(ktime_get_boottime()); atomic_inc(&spi_client->dataflag); wake_up_interruptible(&spi_client->wait_queue); } } return IRQ_HANDLED; } static int js_io_init(struct js_spi_client *spi_client) { int ret; int rc = 0; rc = pinctrl_select_state(spi_client->pinctrl_info.pinctrl, spi_client->pinctrl_info.active); if (rc) pr_err("js failed to set pin state, rc=%d\n", rc); gpio_direction_input(spi_client->js_irq_gpio); gpio_direction_output(spi_client->js_v1p8en_gpio, 0); spi_client->powerstate = 0; spi_client->js_irq = gpio_to_irq(spi_client->js_irq_gpio); if (spi_client->js_irq < 0) { spi_client->js_irq = -1; ret = -EINVAL; pr_err(" js gpio_to_irq err\n"); goto failed; } else { /* IRQF_TRIGGER_FALLING */ ret = request_irq(spi_client->js_irq, js_irq_handler, IRQF_TRIGGER_RISING, "nordic", spi_client); disable_irq_nosync(spi_client->js_irq); if (ret < 0) pr_err("js request_irq err=%d\n", spi_client->js_irq); } pr_debug("%s ok\n", __func__); return 0; failed: return ret; } #ifdef COMPATIBLE_NOT_SUPPORT_DFU static int probe_get_nordic_version(void) { unsigned int getNorVersion = 0; atomic_set(&gspi_client->probeGetNordicVersionFlag, 1); atomic_set(&gspi_client->userRequest, 0x81000000); atomic_inc(&gspi_client->dataflag); wake_up_interruptible(&gspi_client->wait_queue); pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledl_state = 1; while (1) { if (!((unsigned int) atomic_read(&gspi_client->probeGetNordicVersionFlag))) { pr_debug("nordic version flag %d userRequest 0x%x\n", (unsigned int)atomic_read( &gspi_client->probeGetNordicVersionFlag), (unsigned int)atomic_read( &gspi_client->userRequest)); getNorVersion = (unsigned int)atomic_read( &gspi_client->probeGetNordicVersion); pr_debug("nordic version %d.%d getNorVersion 0x%x\n", (getNorVersion >> 8) & 0xff, getNorVersion & 0xff, getNorVersion); pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledl_state = 0; } else { atomic_set(&gspi_client->probeGetNordicVersionFlag, 1); atomic_set(&gspi_client->userRequest, 0x81000000); atomic_inc(&gspi_client->dataflag); wake_up_interruptible(&gspi_client->wait_queue); pinctrl_select_state( gspi_client->pinctrl_info.pinctrl, gspi_client->pinctrl_info.suspend); gspi_client->js_ledl_state = 1; pr_err("Failed to get version flag %d userRequest 0x%x\n", (unsigned int)atomic_read( &gspi_client->probeGetNordicVersionFlag), (unsigned int)atomic_read( &gspi_client->userRequest)); } break; } return 0; } #endif static int js_spi_setup(struct spi_device *spi) { struct js_spi_client *spi_client; int rc = 0; if ((spi->dev.of_node) == NULL) { pr_err("js failed to check of_node\n"); return -ENOMEM; } spi_client = kzalloc(sizeof(*spi_client), GFP_KERNEL); if (!spi_client) { pr_err("js failed to malloc\n"); return -ENOMEM; } spi_client->spi_client = spi; rc = js_parse_gpios(spi_client); if (rc) { pr_err("js failed to parse gpio, rc=%d\n", rc); goto spi_free; } rc = js_pinctrl_init(spi_client); if (rc) { pr_err("js failed to init pinctrl, rc=%d\n", rc); goto spi_free; } rc = js_gpio_request(spi_client); if (rc) { pr_err("js failed to request gpios, rc=%d\n", rc); goto spi_free; } atomic_set(&spi_client->dataflag, 0); atomic_set(&spi_client->userRequest, 0); atomic_set(&spi_client->nordicAcknowledge, 0); mutex_init(&(spi_client->js_mutex)); mutex_init(&(spi_client->js_sm_mutex)); #ifdef MANUL_CONTROL_JOYSTICK_RLED mutex_init(&(spi_client->js_rled_mutex)); #endif spin_lock_init(&spi_client->smem_lock); init_waitqueue_head(&spi_client->wait_queue); dev_set_drvdata(&spi->dev, spi_client); device_create_file(&spi->dev, &dev_attr_jsmem); device_create_file(&spi->dev, &dev_attr_jsrequest); #ifdef MANUL_CONTROL_JOYSTICK_RLED device_create_file(&spi->dev, &dev_attr_jsrled); #endif spi_client->suspend = false; spi_client->vaddr = NULL; gspi_client = spi_client; spi_client->kthread = kthread_run(js_thread, spi_client, "nordicthread"); if (IS_ERR(spi_client->kthread)) pr_err("js kernel_thread failed\n"); js_io_init(spi_client); js_set_power(1); #ifdef COMPATIBLE_NOT_SUPPORT_DFU msleep(5000); probe_get_nordic_version(); #endif return rc; spi_free: kfree(spi_client); return rc; } static int js_spi_suspend(struct device *dev) { struct js_spi_client *spi_client = NULL; if (!dev) return -EINVAL; spi_client = dev_get_drvdata(dev); if (!spi_client) return -EINVAL; js_set_power(0); spi_client->suspend = true; pr_debug("%s exit\n", __func__); return 0; } static int js_spi_resume(struct device *dev) { struct js_spi_client *spi_client = NULL; if (!dev) return -EINVAL; spi_client = dev_get_drvdata(dev); if (!spi_client) return -EINVAL; js_set_power(1); spi_client->suspend = false; pr_debug("[%s] exit\n", __func__); return 0; } static int js_spi_driver_probe(struct spi_device *spi) { int ret = 0; pr_debug("%s start\n", __func__); spi->bits_per_word = 8; spi->mode = SPI_MODE_0; spi->max_speed_hz = 8 * 1000 * 1000; ret = spi_setup(spi); if (ret < 0) { pr_err("js spi_setup failed ret=%d\n", ret); return ret; } pr_debug("%s ok\n", __func__); return js_spi_setup(spi); } static int js_spi_driver_remove(struct spi_device *spi) { struct js_spi_client *spi_client = NULL; js_set_power(0); d_packet_set_instance(NULL); spi_client = dev_get_drvdata(&spi->dev); if (!IS_ERR_OR_NULL(spi_client->vaddr)) { dma_buf_end_cpu_access(spi_client->js_buf, DMA_BIDIRECTIONAL); dma_buf_put(spi_client->js_buf); spi_client->js_buf = NULL; } if (gpio_is_valid(spi_client->js_v1p8en_gpio)) gpio_free(spi_client->js_v1p8en_gpio); if (gpio_is_valid(spi_client->js_irq_gpio)) gpio_free(spi_client->js_irq_gpio); mutex_destroy(&(spi_client->js_mutex)); mutex_destroy(&(spi_client->js_sm_mutex)); #ifdef MANUL_CONTROL_JOYSTICK_RLED mutex_destroy(&(spi_client->js_rled_mutex)); #endif device_remove_file(&spi->dev, &dev_attr_jsmem); device_remove_file(&spi->dev, &dev_attr_jsrequest); #ifdef MANUL_CONTROL_JOYSTICK_RLED device_remove_file(&spi->dev, &dev_attr_jsrled); #endif kfree(spi_client); gspi_client = NULL; return 0; } static const struct of_device_id js_dt_match[] = { { .compatible = "nordic,spicontroller" }, { } }; static const struct dev_pm_ops js_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(js_spi_suspend, js_spi_resume) }; static struct spi_driver js_spi_driver = { .driver = { .name = "nordic,spicontroller", .owner = THIS_MODULE, .of_match_table = js_dt_match, .pm = &js_pm_ops, }, .probe = js_spi_driver_probe, .remove = js_spi_driver_remove, //.suspend = js_spi_suspend, //.resume = js_spi_resume, }; static int __init js_driver_init(void) { int rc = 0; pr_debug("%s start\n", __func__); rc = spi_register_driver(&js_spi_driver); if (rc < 0) { pr_err("spi_register_driver failed rc = %d\n", rc); return rc; } return rc; } static void __exit js_driver_exit(void) { spi_unregister_driver(&js_spi_driver); } module_init(js_driver_init); module_exit(js_driver_exit); MODULE_DESCRIPTION("kinetics nordic52832 driver"); MODULE_LICENSE("GPL v2");