android_kernel_samsung_sm8650/drivers/uwb/sr200.c
2024-10-20 20:09:27 +02:00

1010 lines
36 KiB
C
Executable File

/*====================================================================================*/
/* */
/* Copyright 2022 NXP */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/*====================================================================================*/
/**
* \addtogroup spi_driver
*
* @{ */
#include "sr200.h"
#include "../uwb/uwb_logger/uwb_logger.h"
#define DEBUG_LOG
/* Cold reset Feature in case of Secure Element tx timeout */
#define ESE_COLD_RESET 1
#if ESE_COLD_RESET
#ifdef CONFIG_NFC_NXP_COMBINED
#include "../nfc/nxp_combined/common_ese.h"
#else
#include "../nfc/common_ese.h"
#endif
/*Invoke cold reset if no response from eSE*/
extern int perform_ese_cold_reset(unsigned long source);
#endif
static bool read_abort_requested = false;
static bool is_fw_dwnld_enabled = false;
/* Variable to store current debug level request by ioctl */
static unsigned char debug_level;
#define SR200_DBG_MSG(msg...) \
switch (debug_level) { \
case SR200_DEBUG_OFF: \
break; \
case SR200_FULL_DEBUG: \
UWB_LOG_INFO(msg); \
break; \
default: \
printk(KERN_ERR "[NXP-sr200] : wrong debug level %d", debug_level); \
break; \
}
#define SR200_ERR_MSG(msg...) UWB_LOG_ERR(msg);
#if 1//MW WA
#define WRITE_GUARD_TIME 500
static bool isNtfReceived = false;
#endif
/******************************************************************************
* Function : sr200_dev_open
*
* Description : Open sr200 device node and returns instance to the user space
*
* Parameters : inode : sr200 device node path
* filep : File pointer to structure of sr200 device
*
* Returns : Returns file descriptor for sr200 device
* otherwise indicate each error code
****************************************************************************/
static int sr200_dev_open(struct inode *inode, struct file *filp) {
struct sr200_dev *sr200_dev =
container_of(filp->private_data, struct sr200_dev, sr200_device);
filp->private_data = sr200_dev;
SR200_DBG_MSG("%s : major No: %d, minor No: %d\n", __func__, imajor(inode),
iminor(inode));
if (device_can_wakeup(&sr200_dev->spi->dev)) {
device_wakeup_enable(&sr200_dev->spi->dev);
}
return 0;
}
/******************************************************************************
* Function : sr200_disable_irq
*
* Description : To disable IR
*
* Parameters : sr200_dev : sr200 device structure pointer
*
* Returns : Returns void
****************************************************************************/
static void sr200_disable_irq(struct sr200_dev *sr200_dev) {
unsigned long flags;
#ifdef DEBUG_LOG
UWB_LOG_REC("d i\n");
#endif
spin_lock_irqsave(&sr200_dev->irq_enabled_lock, flags);
if ((sr200_dev->irq_enabled)) {
disable_irq_nosync(sr200_dev->spi->irq);
sr200_dev->irq_received = true;
sr200_dev->irq_enabled = false;
}
spin_unlock_irqrestore(&sr200_dev->irq_enabled_lock, flags);
}
/******************************************************************************
* Function : sr200_enable_irq
*
* Description : Set the irq flag status
*
* Parameters : sr200_dev : sr200 device structure pointer
*
* Returns : Returns void
****************************************************************************/
static void sr200_enable_irq(struct sr200_dev *sr200_dev) {
unsigned long flags;
#ifdef DEBUG_LOG
UWB_LOG_REC("e i\n");
#endif
spin_lock_irqsave(&sr200_dev->irq_enabled_lock, flags);
if (!sr200_dev->irq_enabled) {
enable_irq(sr200_dev->spi->irq);
sr200_dev->irq_enabled = true;
sr200_dev->irq_received = false;
}
spin_unlock_irqrestore(&sr200_dev->irq_enabled_lock, flags);
}
/******************************************************************************
* Function : sr200_dev_irq_handler
*
* Description : Will get called when interrupt line asserted from sr200
*
* Parameters : irq : IRQ Number
* dev_id : sr200 device Id
*
* Returns : Returns IRQ Handler
****************************************************************************/
static irqreturn_t sr200_dev_irq_handler(int irq, void *dev_id) {
struct sr200_dev *sr200_dev = dev_id;
#ifdef DEBUG_LOG
UWB_LOG_REC("h\n");
#endif
sr200_disable_irq(sr200_dev);
/* Wake up waiting readers */
wake_up(&sr200_dev->read_wq);
return IRQ_HANDLED;
}
/******************************************************************************
* Function : sr200_dev_iotcl
*
* Description : Input/OutPut control from user space to perform required
* operation on sr200 device.
*
* Parameters : cmd : Indicates what operation needs to be done sr200
* arg : Value to be passed to sr200 to do the required
* opeation
*
* Returns : 0 on success and (-1) on error
****************************************************************************/
static long sr200_dev_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) {
int ret = 0;
struct sr200_dev *sr200_dev = NULL;
SR200_DBG_MSG("sr200 - %s\n", __FUNCTION__);
sr200_dev = filp->private_data;
if(sr200_dev == NULL) {
ret = -EINVAL;
SR200_ERR_MSG("sr200_dev is NULL");
return ret;
}
switch (cmd) {
case SR200_SET_PWR:
if (arg == PWR_ENABLE) {
SR200_DBG_MSG(" enable power request...\n");
gpio_set_value(sr200_dev->reset_gpio, 1);
msleep(10);
} else if (arg == PWR_DISABLE) {
SR200_DBG_MSG("disable power request...\n");
gpio_set_value(sr200_dev->reset_gpio, 0);
sr200_disable_irq(sr200_dev);
msleep(10);
} else if (arg == ABORT_READ_PENDING) {
SR200_ERR_MSG("%s abort read pending\n", __func__);
read_abort_requested = true;
sr200_disable_irq(sr200_dev);
/* Wake up waiting readers */
wake_up(&sr200_dev->read_wq);
}
break;
case SR200_SET_FWD:
if (arg == 1) {
is_fw_dwnld_enabled = true;
read_abort_requested = false;
SR200_DBG_MSG("%s fw download enabled.\n", __func__);
} else if (arg == 0) {
is_fw_dwnld_enabled = false;
SR200_DBG_MSG("%s fw download disabled.\n", __func__);
}
break;
case SR200_GET_THROUGHPUT:
if (arg == 0) {
SR200_DBG_MSG("%s measure throughput disabled.\n", __func__);
}
break;
#if ESE_COLD_RESET
case SR200_ESE_RESET:
SR200_DBG_MSG("%s SR200_ESE_RESET enter\n", __func__);
ret = perform_ese_cold_reset(ESE_CLD_RST_OTHER);
break;
#endif
case SR200_GET_ANT_CONNECTION_STATUS:
SR200_DBG_MSG("SR200_GET_ANT_CONNECTION_STATUS Enter\n");
if((int)sr200_dev->ant_connection_status_gpio > 0) {
ret = !gpio_get_value(sr200_dev->ant_connection_status_gpio);
} else {
ret = -1;
}
SR200_DBG_MSG("SR200_GET_ANT_CONNECTION_STATUS ret =%d \n", ret);
break;
default:
SR200_ERR_MSG(" error case\n");
ret = -EINVAL; // ToDo: After adding proper switch cases we have to
// return with error statusi here
}
return ret;
}
/******************************************************************************
* Function : sr200_dev_transceive
*
* Description : Used to Write/read data from sr200
*
* Parameters : sr200_dev :sr200 device structure pointer
* op_mode :Indicates write/read mode
* count : Number of bytes to be write/read
* Returns : return status code as per spi_status_codes enum,
* need added each status code and indicate
* when each status code returns
****************************************************************************/
static int sr200_dev_transceive(struct sr200_dev *sr200_dev, int op_mode,
int count) {
int ret;
mutex_lock(&sr200_dev->sr200_access_lock);
sr200_dev->mode = op_mode;
sr200_dev->total_bytes_to_read = 0;
sr200_dev->is_ext_payload_len_bit_set = 0;
ret = -1;
switch (sr200_dev->mode) {
case SR200_WRITE_MODE: {
#if 1 //MW WA
if (!is_fw_dwnld_enabled && isNtfReceived) {
isNtfReceived = false;
usleep_range(WRITE_GUARD_TIME, WRITE_GUARD_TIME + 10);
}
#endif
sr200_dev->write_count = 0;
ret = spi_write(sr200_dev->spi, sr200_dev->tx_buffer, count);
if (ret < 0) {
ret = -EIO;
SR200_ERR_MSG("spi_write: failed.\n");
goto transcive_end;
}
sr200_dev->write_count = count;
ret = spi_transcive_success;
} break;
case SR200_READ_MODE: {
int hdr_length = UCI_HEADER_LEN;
int payloadLen = 0;
struct spi_transfer hdr_transfer;
if (!gpio_get_value(sr200_dev->irq_gpio)) {
SR200_ERR_MSG("IRQ might have gone low due to write\n");
ret = spi_irq_wait_request;
goto transcive_end;
}
sr200_dev->read_count = 0;
// set directional byte to 0XFF for read operation.
sr200_dev->rx_dir_byte_buff[0] = 0xFF;
if (is_fw_dwnld_enabled) {
hdr_length = HDLL_MODE_HEADER_LEN;
}
hdr_transfer.len = (hdr_length + DIRECTIONAL_BYTE_LEN); // Total header bytes to read
// including directional byte.
hdr_transfer.rx_buf = sr200_dev->rx_buffer;
hdr_transfer.tx_buf = sr200_dev->rx_dir_byte_buff;
ret = spi_sync_transfer(sr200_dev->spi, &hdr_transfer, 1);
if (ret < 0) {
SR200_ERR_MSG("spi_sync_transfer: spi transfer error.. %d\n ", ret);
goto transcive_end;
}
if (is_fw_dwnld_enabled) {
sr200_dev->total_bytes_to_read =
(((sr200_dev->rx_buffer[1] & 0x1F) << 8) |
sr200_dev->rx_buffer[2]); // reading 13 bits length from HDLL header
sr200_dev->total_bytes_to_read += CRC_BYTES_LEN;
payloadLen =
sr200_dev->total_bytes_to_read + CRC_BYTES_LEN + DIRECTIONAL_BYTE_LEN;
} else {
if ((sr200_dev->rx_buffer[1] & UCI_MT_MASK) == 0) {
sr200_dev->total_bytes_to_read =
sr200_dev->rx_buffer[NORMAL_MODE_LEN_OFFSET + DIRECTIONAL_BYTE_LEN];
sr200_dev->total_bytes_to_read =
((sr200_dev->total_bytes_to_read << 8) |
sr200_dev->rx_buffer[UCI_EXTENDED_LENGTH_OFFSET + DIRECTIONAL_BYTE_LEN]);
}else {
#if 1 //MW WA
if ((sr200_dev->rx_buffer[1] & UCI_MT_MASK) == 0x60) {
isNtfReceived = true;
}
#endif
sr200_dev->is_ext_payload_len_bit_set =
(sr200_dev->rx_buffer[UCI_EXTND_LEN_INDICATOR_OFFSET +
DIRECTIONAL_BYTE_LEN] &
UCI_EXTND_LEN_INDICATOR_OFFSET_MASK);
sr200_dev->total_bytes_to_read =
sr200_dev->rx_buffer[NORMAL_MODE_LEN_OFFSET + DIRECTIONAL_BYTE_LEN];
if (sr200_dev->is_ext_payload_len_bit_set) {
sr200_dev->total_bytes_to_read =
((sr200_dev->total_bytes_to_read << 8) |
sr200_dev->rx_buffer[UCI_EXTENDED_LENGTH_OFFSET +
DIRECTIONAL_BYTE_LEN]);
}
}
payloadLen = sr200_dev->total_bytes_to_read + DIRECTIONAL_BYTE_LEN;
}
if (sr200_dev->total_bytes_to_read > (MAX_UCI_PKT_SIZE - UCI_HEADER_LEN)) {
SR200_ERR_MSG("length %d exceeds the max limit %d....",
(int)sr200_dev->total_bytes_to_read, (int)MAX_UCI_PKT_SIZE);
ret = -1;
goto transcive_end;
}
if (sr200_dev->total_bytes_to_read > 0) {
struct spi_transfer payload_transfer = {
.len = payloadLen, // Total bytes to read including directional byte
.rx_buf = &sr200_dev->rx_buffer[hdr_length + DIRECTIONAL_BYTE_LEN],
.tx_buf = sr200_dev->rx_dir_byte_buff,
};
ret = spi_sync_transfer(sr200_dev->spi, &payload_transfer, 1);
if (ret < 0) {
SR200_ERR_MSG("spi_sync_transfer: spi transfer error.. %d\n ", ret);
goto transcive_end;
}
sr200_dev->total_bytes_to_read += (2 * DIRECTIONAL_BYTE_LEN);
}
sr200_dev->read_count =
(unsigned int)(sr200_dev->total_bytes_to_read + hdr_length);
} break;
default:
SR200_ERR_MSG("invalid operation .....\n");
break;
}
transcive_end:
mutex_unlock(&sr200_dev->sr200_access_lock);
return ret;
}
/******************************************************************************
* Function : sr200_dev_write
*
* Description : Write Data to sr200 on SPI line
*
* Parameters : filp : Device Node File Pointer
* buf : Buffer which contains data to be sent to sr200
* count : Number of bytes to be send
* offset : Pointer to a object that indicates file position
* user is accessing.
* Returns : Number of bytes writen if write is success else (-1)
* otherwise indicate each error code
****************************************************************************/
static ssize_t sr200_dev_write(struct file *filp, const char *buf, size_t count,
loff_t *offset) {
int ret = -1;
struct sr200_dev *sr200_dev;
sr200_dev = filp->private_data;
#ifdef DEBUG_LOG
UWB_LOG_REC("w\n");
#endif
if (sr200_dev == NULL) {
SR200_ERR_MSG("%s : sr200_dev is null \n", __func__);
return ret;
}
if (sr200_dev->tx_buffer == NULL ) {
SR200_ERR_MSG("sr200_dev->tx_buffer is null %s \n", __func__);
return ret;
}
if (count >= SR200_TXBUF_SIZE) {
SR200_ERR_MSG("%s : write size exceeds\n", __func__);
ret = -ENOBUFS;
goto write_end;
}
memset(sr200_dev->tx_buffer, 0x00, count + DIRECTIONAL_BYTE_LEN);
if (copy_from_user(sr200_dev->tx_buffer + DIRECTIONAL_BYTE_LEN, buf, count)) {
SR200_ERR_MSG("%s : failed to copy from user space \n", __func__);
return -EFAULT;
}
count += DIRECTIONAL_BYTE_LEN; // Including Direction byte
ret = sr200_dev_transceive(sr200_dev, SR200_WRITE_MODE, count);
if (ret == spi_transcive_success) {
ret = sr200_dev->write_count;
} else {
SR200_ERR_MSG("write failed......\n");
}
#ifdef DEBUG_LOG
UWB_LOG_REC("w %d\n", ret);
#endif
write_end:
return ret;
}
/******************************************************************************
* Function : sr200_dev_read
*
* Description : Used to read data from sr200
*
* Parameters : filp : Device Node File Pointer
* buf : Buffer which contains data to be read from sr200
* count : Number of bytes to be read
* offset : Pointer to a object that indicates file position
* user is accessing.
* Returns : Number of bytes read if read is success else (-1)
* otherwise indicate each error code
****************************************************************************/
static ssize_t sr200_dev_read(struct file *filp, char *buf, size_t count,
loff_t *offset) {
struct sr200_dev *sr200_dev = filp->private_data;
int ret = -EIO;
int retry_count = 0;
int hdr_length = UCI_HEADER_LEN;
#ifdef DEBUG_LOG
UWB_LOG_REC("r\n");
#endif
if(sr200_dev == NULL) {
SR200_ERR_MSG("sr200_dev is null %s \n", __func__);
return ret;
}
if (sr200_dev->rx_buffer == NULL || sr200_dev->rx_dir_byte_buff == NULL) {
SR200_ERR_MSG("sr200_dev->rx_buffer is null %s \n", __func__);
return ret;
}
/*500ms timeout in jiffies*/
sr200_dev->timeout_in_ms = ((500 * HZ) / 2000);
memset(sr200_dev->rx_buffer, 0x00, SR200_RXBUF_SIZE);
memset(sr200_dev->rx_dir_byte_buff, 0x00, SR200_TXBUF_SIZE);
if (!gpio_get_value(sr200_dev->irq_gpio)) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
return ret;
}
}
/*FW download packet read*/
if (is_fw_dwnld_enabled) {
hdr_length = HDLL_MODE_HEADER_LEN;
}
first_irq_wait:
sr200_enable_irq(sr200_dev);
if (!read_abort_requested) {
ret = wait_event_interruptible(sr200_dev->read_wq, sr200_dev->irq_received);
if (ret) {
if (ret != -ERESTARTSYS) {
SR200_ERR_MSG("read_wq wait_event_interruptible() :%d Failed.\n", ret);
}
return ret;
}
}
if (read_abort_requested) {
read_abort_requested = false;
SR200_ERR_MSG("abort Read pending......\n");
return ret;
}
mutex_lock(&sr200_dev->sr200_read_count_lock);
ret = sr200_dev_transceive(sr200_dev, SR200_READ_MODE, count);
if (ret == spi_transcive_success) {
sr200_dev->read_count -= DIRECTIONAL_BYTE_LEN;
if (copy_to_user(buf, &sr200_dev->rx_buffer[1], hdr_length)) {
SR200_ERR_MSG("sr200_dev_read: copy to user failed for hdr_length\n");
ret = -EFAULT;
goto read_end;
}
if (sr200_dev->read_count > hdr_length) {
sr200_dev->read_count -= DIRECTIONAL_BYTE_LEN;
if (copy_to_user(&buf[hdr_length], &sr200_dev->rx_buffer[hdr_length+2*DIRECTIONAL_BYTE_LEN], (sr200_dev->read_count-hdr_length))) {
SR200_ERR_MSG("sr200_dev_read: copy to user failed for payload\n");
ret = -EFAULT;
goto read_end;
}
}
ret = sr200_dev->read_count;
} else if (ret == spi_irq_wait_request) {
SR200_DBG_MSG(" irg is low due to write hence irq is requested again...\n");
mutex_unlock(&sr200_dev->sr200_read_count_lock);
goto first_irq_wait;
} else if (ret == spi_irq_wait_timeout) {
SR200_DBG_MSG("second irq is not received..time out...\n");
ret = -1;
} else {
SR200_ERR_MSG("spi read failed...%d", ret);
ret = -1;
}
#ifdef DEBUG_LOG
UWB_LOG_REC("r %d\n", ret);
#endif
read_end:
mutex_unlock(&sr200_dev->sr200_read_count_lock);
retry_count = 0;
return ret;
}
/******************************************************************************
* Function : sr200_hw_setup
*
* Description : Used to read data from sr200
*
* Parameters : platform_data : struct sr200_spi_platform_data *
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static int sr200_hw_setup(struct sr200_spi_platform_data *platform_data) {
int ret;
SR200_DBG_MSG("entry : %s\n", __FUNCTION__);
ret = gpio_request(platform_data->irq_gpio, "sr200 irq");
if (ret < 0) {
SR200_ERR_MSG("gpio request failed gpio = 0x%x\n", platform_data->irq_gpio);
goto fail;
}
ret = gpio_direction_input(platform_data->irq_gpio);
if (ret < 0) {
SR200_ERR_MSG("gpio request failed gpio = 0x%x\n", platform_data->irq_gpio);
goto fail_irq;
}
ret = gpio_request(platform_data->reset_gpio, "sr200 reset");
if (ret < 0) {
SR200_ERR_MSG("gpio request failed gpio = 0x%x\n", platform_data->reset_gpio);
goto fail;
}
ret = gpio_direction_output(platform_data->reset_gpio, 0);
if (ret < 0) {
SR200_ERR_MSG("sr200 - failed setting ce gpio - %d\n", platform_data->reset_gpio);
goto fail_gpio;
}
if((int)platform_data->ant_connection_status_gpio > 0) {
ret = gpio_request(platform_data->ant_connection_status_gpio, "sr200 ant connection status");
if (ret < 0) {
SR200_ERR_MSG("sr200 - Failed requesting ant connection status gpio - %d\n", platform_data->ant_connection_status_gpio);
goto fail_gpio;
}
ret = gpio_direction_input(platform_data->ant_connection_status_gpio);
if (ret < 0) {
SR200_ERR_MSG("sr200 - Failed setting ant connection status gpio - %d\n", platform_data->ant_connection_status_gpio);
goto fail_gpio;
}
}
ret = 0;
SR200_DBG_MSG("Exit : %s\n", __FUNCTION__);
return ret;
fail_gpio:
if((int)platform_data->ant_connection_status_gpio > 0) {
gpio_free(platform_data->ant_connection_status_gpio);
}
gpio_free(platform_data->reset_gpio);
fail_irq:
gpio_free(platform_data->irq_gpio);
fail:
SR200_ERR_MSG("sr200_hw_setup failed\n");
return ret;
}
/******************************************************************************
* Function : sr200_set_data
*
* Description : Set the sr200 device specific context for future use
*
* Parameters : spi : struct spi_device *
* data: void*
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static inline void sr200_set_data(struct spi_device *spi, void *data) {
dev_set_drvdata(&spi->dev, data);
}
/******************************************************************************
* Function : sr200_get_data
*
* Description : Get the sr200 device specific context
*
* Parameters : spi : struct spi_device *
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static inline void *sr200_get_data(const struct spi_device *spi) {
return dev_get_drvdata(&spi->dev);
}
/* possible fops on the sr200 device */
static const struct file_operations sr200_dev_fops = {
.owner = THIS_MODULE,
.read = sr200_dev_read,
.write = sr200_dev_write,
.open = sr200_dev_open,
.unlocked_ioctl = sr200_dev_ioctl,
};
/******************************************************************************
* Function : sr200_parse_dt
*
* Description : Parse the dtsi configartion
*
* Parameters : dev : struct spi_device *
* pdata: Ponter to platform data
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static int sr200_parse_dt(struct device *dev,
struct sr200_spi_platform_data *pdata) {
struct device_node *np = dev->of_node;
SR200_DBG_MSG("sr200 - %s\n", __FUNCTION__);
pdata->irq_gpio = of_get_named_gpio(np, "nxp,sr200-irq", 0);
if (!gpio_is_valid(pdata->irq_gpio)) {
return -EINVAL;
}
pdata->reset_gpio = of_get_named_gpio(np, "nxp,sr200-reset", 0);
if (!gpio_is_valid(pdata->reset_gpio)) {
return -EINVAL;
}
pdata->ant_connection_status_gpio = of_get_named_gpio(np, "nxp,sr200-ant-connection-status", 0);
if (!gpio_is_valid(pdata->ant_connection_status_gpio)) {
SR200_ERR_MSG("nxp,sr100-ant-connection-status not found\n");
// return -EINVAL;
}
if (of_property_read_string(np, "nxp,vdd-io", &pdata->uwb_vdd_io) < 0) {
SR200_ERR_MSG("uwb_vdd_io not found\n");
pdata->uwb_vdd_io = NULL;
} else {
SR200_ERR_MSG("uwb_vdd_io :%s\n", pdata->uwb_vdd_io);
}
if (of_property_read_string(np, "nxp,vdd-sw", &pdata->uwb_vdd_sw) < 0) {
SR200_ERR_MSG("uwb_vdd_sw not found\n");
pdata->uwb_vdd_sw = NULL;
} else {
SR200_ERR_MSG("uwb_vdd_sw :%s\n", pdata->uwb_vdd_sw);
}
SR200_DBG_MSG("sr200 : irq_gpio = %d, reset_gpio = %d \n", pdata->irq_gpio,
pdata->reset_gpio);
SR200_DBG_MSG("sr200 : ant_connection_status_gpio = %u \n", pdata->ant_connection_status_gpio);
return 0;
}
static int sr200_regulator_onoff(struct device *dev, struct sr200_dev* pdev, bool onoff)
{
int rc = 0;
struct regulator *regulator_uwb_vdd_io = NULL, *regulator_uwb_vdd_sw = NULL;
if(pdev->uwb_vdd_io != NULL) {
regulator_uwb_vdd_io = regulator_get(dev, pdev->uwb_vdd_io);
if (IS_ERR(regulator_uwb_vdd_io) || regulator_uwb_vdd_io == NULL) {
SR200_ERR_MSG("regulator_uwb_vdd_io regulator_get fail\n");
return -ENODEV;
}
} else {
regulator_uwb_vdd_io = NULL;
SR200_ERR_MSG("regulator_uwb_vdd_io not support\n");
}
if(pdev->uwb_vdd_sw != NULL) {
regulator_uwb_vdd_sw = regulator_get(dev, pdev->uwb_vdd_sw);
if (IS_ERR(regulator_uwb_vdd_sw) || regulator_uwb_vdd_sw == NULL) {
SR200_ERR_MSG("regulator_uwb_vdd_sw regulator_get fail\n");
return -ENODEV;
}
} else {
regulator_uwb_vdd_sw = NULL;
SR200_ERR_MSG("regulator_uwb_vdd_sw not support\n");
}
SR200_DBG_MSG("sr200_regulator_onoff = %d\n", onoff);
if (onoff == true) {
if(regulator_uwb_vdd_io != NULL) {
rc = regulator_set_load(regulator_uwb_vdd_io, 400000);
if (rc) {
SR200_ERR_MSG("regulator_uwb_vdd_io set_load failed, rc=%d\n", rc);
goto regulator_err;
}
regulator_set_voltage(regulator_uwb_vdd_io, 1800000, 1800000);
rc = regulator_enable(regulator_uwb_vdd_io);
if (rc) {
SR200_ERR_MSG("regulator_uwb_vdd_io enable failed, rc=%d\n", rc);
goto regulator_err;
}
}
if(regulator_uwb_vdd_sw != NULL) {
rc = regulator_enable(regulator_uwb_vdd_sw);
if (rc) {
SR200_ERR_MSG("regulator_uwb_vdd_sw enable failed, rc=%d\n", rc);
goto regulator_err;
}
}
} else {
if(regulator_uwb_vdd_io != NULL) {
rc = regulator_disable(regulator_uwb_vdd_io);
if (rc) {
SR200_ERR_MSG("regulator_uwb_vdd_io disable failed, rc=%d\n", rc);
goto regulator_err;
}
}
if(regulator_uwb_vdd_sw != NULL) {
rc = regulator_disable(regulator_uwb_vdd_sw);
if (rc) {
SR200_ERR_MSG("regulator_uwb_vdd_sw disable failed, rc=%d\n", rc);
goto regulator_err;
}
}
regulator_err:
if(regulator_uwb_vdd_io != NULL) regulator_put(regulator_uwb_vdd_io);
if(regulator_uwb_vdd_sw != NULL) regulator_put(regulator_uwb_vdd_sw);
}
return rc;
}
/******************************************************************************
* Function : sr200_probe
*
* Description : To probe for sr200 SPI interface. If found initialize the SPI
* clock,bit rate & SPI mode. It will create the dev entry
* (sr200) for user space.
* Parameters : spi : struct spi_device *
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static int sr200_probe(struct spi_device *spi) {
int ret;
struct sr200_spi_platform_data *platform_data = NULL;
struct sr200_spi_platform_data platform_data1;
struct sr200_dev *sr200_dev = NULL;
unsigned int irq_flags;
SR200_DBG_MSG("%s chip select : %d , bus number = %d \n", __FUNCTION__,
spi->chip_select, spi->master->bus_num);
ret = sr200_parse_dt(&spi->dev, &platform_data1);
if (ret) {
SR200_ERR_MSG("%s - failed to parse DT\n", __func__);
goto err_exit;
}
platform_data = &platform_data1;
sr200_dev = kzalloc(sizeof(*sr200_dev), GFP_KERNEL);
if (sr200_dev == NULL) {
SR200_ERR_MSG("failed to allocate memory for module data\n");
ret = -ENOMEM;
goto err_exit;
}
ret = sr200_hw_setup(platform_data);
if (ret < 0) {
SR200_ERR_MSG("failed to sr200_enable_SR200_IRQ_ENABLE\n");
goto err_exit0;
}
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
spi->max_speed_hz = SR200_SPI_CLOCK;
ret = spi_setup(spi);
if (ret < 0) {
SR200_ERR_MSG("failed to do spi_setup()\n");
goto err_exit0;
}
sr200_dev->spi = spi;
sr200_dev->sr200_device.minor = MISC_DYNAMIC_MINOR;
sr200_dev->sr200_device.name = "sr200";
sr200_dev->sr200_device.fops = &sr200_dev_fops;
sr200_dev->sr200_device.parent = &spi->dev;
sr200_dev->irq_gpio = platform_data->irq_gpio;
sr200_dev->reset_gpio = platform_data->reset_gpio;
sr200_dev->ant_connection_status_gpio = platform_data->ant_connection_status_gpio;
sr200_dev->uwb_vdd_io = platform_data->uwb_vdd_io;
sr200_dev->uwb_vdd_sw = platform_data->uwb_vdd_sw;
sr200_dev->tx_buffer = kzalloc(SR200_TXBUF_SIZE, GFP_KERNEL);
sr200_dev->rx_buffer = kzalloc(SR200_RXBUF_SIZE, GFP_KERNEL);
sr200_dev->rx_dir_byte_buff = kzalloc(SR200_RXBUF_SIZE, GFP_KERNEL);
if (sr200_dev->tx_buffer == NULL) {
ret = -ENOMEM;
goto exit_free_dev;
}
if (sr200_dev->rx_buffer == NULL) {
ret = -ENOMEM;
goto exit_free_dev;
}
if (sr200_dev->rx_dir_byte_buff == NULL) {
ret = -ENOMEM;
goto exit_free_dev;
}
dev_set_drvdata(&spi->dev, sr200_dev);
/* init mutex and queues */
init_waitqueue_head(&sr200_dev->read_wq);
mutex_init(&sr200_dev->sr200_access_lock);
mutex_init(&sr200_dev->sr200_read_count_lock);
spin_lock_init(&sr200_dev->irq_enabled_lock);
ret = misc_register(&sr200_dev->sr200_device);
if (ret < 0) {
SR200_ERR_MSG("misc_register failed! %d\n", ret);
goto err_exit0;
}
ret = sr200_regulator_onoff(&spi->dev, sr200_dev, true);
if (ret < 0) {
SR200_ERR_MSG("regulator_on fail err:%d\n", ret);
}
usleep_range(1000, 1100);
sr200_dev->spi->irq = gpio_to_irq(platform_data->irq_gpio);
if (sr200_dev->spi->irq < 0) {
SR200_ERR_MSG("gpio_to_irq request failed gpio = 0x%x\n",
platform_data->irq_gpio);
goto err_exit1;
}
/* request irq. the irq is set whenever the chip has data available
* for reading. it is cleared when all data has been read.
*/
// irq_flags = IRQF_TRIGGER_RISING;
irq_flags = IRQ_TYPE_LEVEL_HIGH;
sr200_dev->irq_enabled = true;
sr200_dev->irq_received = false;
ret = request_irq(sr200_dev->spi->irq, sr200_dev_irq_handler, irq_flags,
sr200_dev->sr200_device.name, sr200_dev);
if (ret) {
SR200_ERR_MSG("request_irq failed\n");
goto err_exit1;
} else {
sr200_disable_irq(sr200_dev);
device_set_wakeup_capable(&sr200_dev->spi->dev, true);
device_wakeup_disable(&sr200_dev->spi->dev);
}
SR200_DBG_MSG("exit : %s\n", __FUNCTION__);
return ret;
err_exit1:
exit_free_dev:
if (sr200_dev != NULL) {
if (sr200_dev->tx_buffer) {
kfree(sr200_dev->tx_buffer);
}
if (sr200_dev->rx_buffer) {
kfree(sr200_dev->rx_buffer);
}
if (sr200_dev->rx_dir_byte_buff) {
kfree(sr200_dev->rx_dir_byte_buff);
}
misc_deregister(&sr200_dev->sr200_device);
}
err_exit0:
if (sr200_dev != NULL) {
mutex_destroy(&sr200_dev->sr200_access_lock);
mutex_destroy(&sr200_dev->sr200_read_count_lock);
}
err_exit:
if (sr200_dev != NULL)
kfree(sr200_dev);
SR200_DBG_MSG("ERROR: exit : %s ret %d\n", __FUNCTION__, ret);
return ret;
}
#if KERNEL_VERSION(5, 18, 0) <= LINUX_VERSION_CODE
static void sr200_remove(struct spi_device *spi) {
struct sr200_dev *sr200_dev = sr200_get_data(spi);
SR200_DBG_MSG("entry : %s\n", __FUNCTION__);
if (sr200_dev != NULL) {
device_wakeup_disable(&sr200_dev->spi->dev);
sr200_regulator_onoff(&spi->dev, sr200_dev, false);
gpio_free(sr200_dev->reset_gpio);
mutex_destroy(&sr200_dev->sr200_access_lock);
free_irq(sr200_dev->spi->irq, sr200_dev);
gpio_free(sr200_dev->irq_gpio);
if((int)sr200_dev->ant_connection_status_gpio > 0) {
gpio_free(sr200_dev->ant_connection_status_gpio);
}
misc_deregister(&sr200_dev->sr200_device);
if (sr200_dev->tx_buffer != NULL)
kfree(sr200_dev->tx_buffer);
if (sr200_dev->rx_buffer != NULL)
kfree(sr200_dev->rx_buffer);
if(sr200_dev->rx_dir_byte_buff != NULL)
kfree(sr200_dev->rx_dir_byte_buff);
kfree(sr200_dev);
}
SR200_DBG_MSG("exit : %s\n", __FUNCTION__);
return;
}
#else
/******************************************************************************
* Function : sr200_remove
*
* Description : Will get called when the device is removed to release the
* resources.
*
* Parameters : spi : struct spi_device *
*
* Returns : retval 0 if ok else -1 on error
****************************************************************************/
static int sr200_remove(struct spi_device *spi) {
struct sr200_dev *sr200_dev = sr200_get_data(spi);
SR200_DBG_MSG("entry : %s\n", __FUNCTION__);
if (sr200_dev != NULL) {
device_wakeup_disable(&sr200_dev->spi->dev);
sr200_regulator_onoff(&spi->dev, sr200_dev, false);
gpio_free(sr200_dev->reset_gpio);
mutex_destroy(&sr200_dev->sr200_access_lock);
mutex_destroy(&sr200_dev->sr200_read_count_lock);
free_irq(sr200_dev->spi->irq, sr200_dev);
gpio_free(sr200_dev->irq_gpio);
if((int)sr200_dev->ant_connection_status_gpio > 0) {
gpio_free(sr200_dev->ant_connection_status_gpio);
}
misc_deregister(&sr200_dev->sr200_device);
if (sr200_dev->tx_buffer != NULL)
kfree(sr200_dev->tx_buffer);
if (sr200_dev->rx_buffer != NULL)
kfree(sr200_dev->rx_buffer);
if(sr200_dev->rx_dir_byte_buff != NULL)
kfree(sr200_dev->rx_dir_byte_buff);
kfree(sr200_dev);
}
SR200_DBG_MSG("exit : %s\n", __FUNCTION__);
return 0;
}
#endif
/**
* sr200_dev_resume
*
* Executed after waking the system up from a sleep state
*
*/
int sr200_dev_resume(struct device *dev)
{
struct sr200_dev *sr200_dev = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(sr200_dev->spi->irq);
return 0;
}
/**
* sr200_dev_suspend
*
* Executed before putting the system into a sleep state
*
*/
int sr200_dev_suspend(struct device *dev)
{
struct sr200_dev *sr200_dev = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(sr200_dev->spi->irq);
return 0;
}
static struct of_device_id sr200_dt_match[] = {{
.compatible = "nxp,sr200",
},
{}};
static const struct dev_pm_ops sr200_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(
sr200_dev_suspend, sr200_dev_resume) };
static struct spi_driver sr200_driver = {
.driver =
{
.name = "sr200",
.pm = &sr200_dev_pm_ops,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = sr200_dt_match,
},
.probe = sr200_probe,
.remove = (sr200_remove),
};
/******************************************************************************
* Function : sr200_dev_init
*
* Description : Module init interface
*
* Parameters :void
*
* Returns : returns handle
****************************************************************************/
static int __init sr200_dev_init(void) {
debug_level = SR200_FULL_DEBUG;
uwb_logger_init();
SR200_DBG_MSG("entry : %s\n", __FUNCTION__);
return spi_register_driver(&sr200_driver);
}
module_init(sr200_dev_init);
/******************************************************************************
* Function : sr200_dev_exit
*
* Description : Module Exit interface
*
* Parameters :void
*
* Returns : returns void
****************************************************************************/
static void __exit sr200_dev_exit(void) {
SR200_DBG_MSG("entry : %s\n", __FUNCTION__);
spi_unregister_driver(&sr200_driver);
SR200_DBG_MSG("exit : %s\n", __FUNCTION__);
}
module_exit(sr200_dev_exit);
MODULE_AUTHOR("Manjunatha Venkatesh");
MODULE_DESCRIPTION("NXP SR200 SPI driver");
MODULE_LICENSE("GPL");
/** @} */