Files
kernel_xiaomi_sm8250/drivers/misc/kxrctrl/aphost.c
Wei Tan 9741fb8b3f misc: update nordic DFU function check
Internal with two kinds of reference devices;
One is the Nordic DFU/OTA supported and
the other is DFU/OTA not upported.

Add this DFU func to check device type to
adjust all test cases.

Change-Id: I2ae93fa05eff0e332fad30760efbdde5081f1bd8
Signed-off-by: Wei Tan <quic_weitan@quicinc.com>
2023-02-26 17:55:03 -08:00

1041 lines
26 KiB
C

/*
* 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, &param);
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(&currentRequest, 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, &currentRequest, 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");