1935 lines
48 KiB
C
Executable File
1935 lines
48 KiB
C
Executable File
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#define pr_fmt(msg) "slatecom: %s: " msg, __func__
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/ratelimit.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/device.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/ipc_logging.h>
|
|
#include "slatecom.h"
|
|
#include <linux/soc/qcom/slatecom_interface.h>
|
|
|
|
#define SLATE_SPI_WORD_SIZE (0x04)
|
|
#define SLATE_SPI_READ_LEN (0x04)
|
|
#define SLATE_SPI_WRITE_CMND_LEN (0x01)
|
|
#define SLATE_SPI_FIFO_READ_CMD (0x41)
|
|
#define SLATE_SPI_FIFO_WRITE_CMD (0x40)
|
|
#define SLATE_SPI_AHB_READ_CMD (0x43)
|
|
#define SLATE_SPI_AHB_WRITE_CMD (0x42)
|
|
#define SLATE_SPI_AHB_CMD_LEN (0x05)
|
|
#define SLATE_SPI_AHB_READ_CMD_LEN (0x08)
|
|
#define SLATE_STATUS_REG (0x05)
|
|
#define SLATE_CMND_REG (0x14)
|
|
|
|
#define SLATE_SPI_MAX_WORDS (0x3FFFFFFD)
|
|
#define SLATE_SPI_MAX_REGS (0x0A)
|
|
#define HED_EVENT_ID_LEN (0x02)
|
|
#define HED_EVENT_SIZE_LEN (0x02)
|
|
#define HED_EVENT_DATA_STRT_LEN (0x05)
|
|
#define CMA_BFFR_POOL_SIZE (128*1024)
|
|
#define TX_AHB_BUF_SIZE 1024
|
|
#define SLATE_HEALTH_CHECK BIT(23)
|
|
#define SLATE_OK_SLP_RBSC BIT(24)
|
|
#define SLATE_OK_SLP_S2R BIT(25)
|
|
#define SLATE_OK_SLP_S2D (BIT(25) | BIT(24))
|
|
#define SLATE_OK_SLP_SIF BIT(26)
|
|
|
|
#define OK_TO_SLEEP_CLEARED BIT(18)
|
|
#define SLAVE_STATUS_READY BIT(31)
|
|
|
|
#define WR_PROTOCOL_OVERHEAD (5)
|
|
#define WR_PROTOCOL_OVERHEAD_IN_WORDS (2)
|
|
|
|
#define WR_BUF_SIZE_IN_BYTES CMA_BFFR_POOL_SIZE
|
|
#define WR_BUF_SIZE_IN_WORDS (CMA_BFFR_POOL_SIZE / sizeof(uint32_t))
|
|
#define WR_BUF_SIZE_IN_WORDS_FOR_USE \
|
|
(WR_BUF_SIZE_IN_WORDS - WR_PROTOCOL_OVERHEAD_IN_WORDS)
|
|
|
|
#define WR_BUF_SIZE_IN_BYTES_FOR_USE (WR_BUF_SIZE_IN_WORDS_FOR_USE * sizeof(uint32_t))
|
|
#define SLATE_RESUME_IRQ_TIMEOUT 100
|
|
#define SLATE_SPI_AUTOSUSPEND_TIMEOUT 500
|
|
#define MIN_SLEEP_TIME 5
|
|
|
|
/* Master_Command[27] */
|
|
#define SLATE_PAUSE_OK BIT(27)
|
|
|
|
/* SLAVE_STATUS_AUTO_CLEAR[16:15] */
|
|
#define SLATE_PAUSE_REQ BIT(15)
|
|
#define SLATE_RESUME_IND BIT(16)
|
|
|
|
#define SPI_FREQ_1MHZ 1000000
|
|
#define SPI_FREQ_40MHZ 40000000
|
|
|
|
#define MAX_RETRY 3
|
|
|
|
/* Define IPC Logging Macros */
|
|
#define LOG_PAGES_CNT 2
|
|
static void *slatecom_ipc_log;
|
|
|
|
#define SLATECOM_INFO(x, ...) \
|
|
ipc_log_string(slatecom_ipc_log, "[%s]: "x, __func__, ##__VA_ARGS__)
|
|
|
|
#define SLATECOM_ERR(x, ...) \
|
|
do { \
|
|
printk_ratelimited("%s[%s]: " x, KERN_ERR, __func__, ##__VA_ARGS__); \
|
|
ipc_log_string(slatecom_ipc_log, "%s[%s]: " x, "", __func__, \
|
|
##__VA_ARGS__);\
|
|
} while (0)
|
|
|
|
enum slatecom_state {
|
|
/*SLATECOM Staus ready*/
|
|
SLATECOM_PROB_SUCCESS = 0,
|
|
SLATECOM_PROB_WAIT = 1,
|
|
SLATECOM_STATE_SUSPEND_PREPARE = 2,
|
|
SLATECOM_STATE_SUSPEND = 3,
|
|
SLATECOM_STATE_ACTIVE = 4,
|
|
SLATECOM_STATE_RUNTIME_SUSPEND = 5,
|
|
SLATECOM_STATE_HIBERNATE = 6,
|
|
};
|
|
|
|
enum slatecom_req_type {
|
|
/*SLATECOM local requests*/
|
|
SLATECOM_READ_REG = 0,
|
|
SLATECOM_READ_FIFO = 1,
|
|
SLATECOM_READ_AHB = 2,
|
|
SLATECOM_WRITE_REG = 3,
|
|
};
|
|
|
|
struct slate_spi_priv {
|
|
struct spi_device *spi;
|
|
/* Transaction related */
|
|
struct mutex xfer_mutex;
|
|
void *lhandle;
|
|
/* Message for single transfer */
|
|
struct spi_message msg1;
|
|
struct spi_transfer xfer1;
|
|
atomic_t irq_lock;
|
|
|
|
};
|
|
|
|
struct cb_data {
|
|
void *priv;
|
|
void *handle;
|
|
void (*slatecom_notification_cb)(void *handle, void *priv,
|
|
enum slatecom_event_type event,
|
|
union slatecom_event_data_type *event_data);
|
|
struct list_head list;
|
|
};
|
|
|
|
struct slate_context {
|
|
struct slate_spi_priv *slate_spi;
|
|
enum slatecom_state state;
|
|
struct cb_data *cb;
|
|
};
|
|
|
|
struct event_list {
|
|
struct event *evnt;
|
|
struct list_head list;
|
|
};
|
|
static void *slate_com_drv;
|
|
static uint32_t g_slav_status_reg;
|
|
static uint32_t g_slave_status_auto_clear_reg;
|
|
static bool is_hibernate;
|
|
static bool s2a_status;
|
|
|
|
/* SLATECOM client callbacks set-up */
|
|
static void send_input_events(struct work_struct *work);
|
|
static struct list_head cb_head = LIST_HEAD_INIT(cb_head);
|
|
static struct list_head pr_lst_hd = LIST_HEAD_INIT(pr_lst_hd);
|
|
static DEFINE_SPINLOCK(lst_setup_lock);
|
|
static enum slatecom_spi_state spi_state;
|
|
|
|
|
|
static struct workqueue_struct *wq;
|
|
static DECLARE_WORK(input_work, send_input_events);
|
|
|
|
static struct mutex slate_resume_mutex;
|
|
static struct mutex slate_task_mutex;
|
|
|
|
static atomic_t slate_is_runtime_suspend;
|
|
static atomic_t slate_is_spi_active;
|
|
static atomic_t ok_to_sleep;
|
|
static atomic_t state;
|
|
static int slate_irq;
|
|
|
|
static uint8_t *fxd_mem_buffer;
|
|
static struct mutex cma_buffer_lock;
|
|
static ktime_t sleep_time_start;
|
|
|
|
static DECLARE_COMPLETION(slate_resume_wait);
|
|
static int slatecom_reg_write_cmd(void *handle, uint8_t reg_start_addr,
|
|
uint8_t num_regs, void *write_buf, bool flag);
|
|
|
|
static int slatecom_reg_read_internal(void *handle, uint8_t reg_start_addr,
|
|
uint32_t num_regs, void *read_buf);
|
|
static int slatecom_force_resume(void *handle);
|
|
|
|
struct subsys_state_ops state_ops;
|
|
static irqreturn_t slate_irq_tasklet_hndlr(int irq, void *device);
|
|
static int req_irq_flag = 1;
|
|
|
|
static struct spi_device *get_spi_device(void)
|
|
{
|
|
struct slate_spi_priv *slate_spi = container_of(slate_com_drv,
|
|
struct slate_spi_priv, lhandle);
|
|
struct spi_device *spi = slate_spi->spi;
|
|
return spi;
|
|
}
|
|
|
|
static void augmnt_fifo(uint8_t *data, int pos)
|
|
{
|
|
data[pos] = '\0';
|
|
}
|
|
|
|
void slatecom_state_init(void (*fn1)(bool), void (*fn2)(bool))
|
|
{
|
|
state_ops.set_dsp_state = fn1;
|
|
state_ops.set_bt_state = fn2;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_state_init);
|
|
|
|
static void send_input_events(struct work_struct *work)
|
|
{
|
|
struct list_head *temp;
|
|
struct list_head *pos;
|
|
struct event_list *node;
|
|
struct event *evnt;
|
|
|
|
if (list_empty(&pr_lst_hd))
|
|
return;
|
|
|
|
list_for_each_safe(pos, temp, &pr_lst_hd) {
|
|
node = list_entry(pos, struct event_list, list);
|
|
evnt = node->evnt;
|
|
//bgrsb_send_input(evnt);
|
|
kfree(evnt);
|
|
spin_lock(&lst_setup_lock);
|
|
list_del(&node->list);
|
|
spin_unlock(&lst_setup_lock);
|
|
kfree(node);
|
|
}
|
|
}
|
|
|
|
int slatecom_set_spi_state(enum slatecom_spi_state state)
|
|
{
|
|
struct slate_spi_priv *slate_spi = container_of(slate_com_drv,
|
|
struct slate_spi_priv, lhandle);
|
|
ktime_t time_start, delta;
|
|
s64 time_elapsed;
|
|
struct slate_context clnt_handle;
|
|
int ret = 0;
|
|
struct device spi_dev;
|
|
|
|
if (!slate_com_drv)
|
|
return -ENODEV;
|
|
spi_dev = slate_spi->spi->master->dev;
|
|
|
|
if (req_irq_flag && state == SLATECOM_SPI_FREE) {
|
|
ret = request_threaded_irq(slate_irq, NULL, slate_irq_tasklet_hndlr,
|
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "qcom-slate_spi", slate_spi);
|
|
if (ret)
|
|
pr_err("qcom-slate_spi: failed to register IRQ:%d\n", ret);
|
|
ret = irq_set_irq_wake(slate_irq, true);
|
|
if (ret)
|
|
pr_err("irq set as wakeup return: %d\n", ret);
|
|
req_irq_flag = 0;
|
|
}
|
|
|
|
clnt_handle.slate_spi = slate_spi;
|
|
|
|
if (state < 0 || state > 1) {
|
|
SLATECOM_ERR("Invalid spi state. Returning %d\n", -EINVAL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (state == spi_state) {
|
|
SLATECOM_ERR("state same as spi_state. Returning 0\n");
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&slate_spi->xfer_mutex);
|
|
if (state == SLATECOM_SPI_BUSY) {
|
|
time_start = ktime_get();
|
|
while (!pm_runtime_status_suspended(spi_dev.parent)) {
|
|
delta = ktime_sub(ktime_get(), time_start);
|
|
time_elapsed = ktime_to_ms(delta);
|
|
WARN_ON(time_elapsed > 5 * MSEC_PER_SEC);
|
|
SLATECOM_INFO("Waiting to set state busy....\n");
|
|
msleep(100);
|
|
}
|
|
/* After PIL seq, clear preceding state. As rmproc
|
|
* reloaded, it is unaware of previous context.
|
|
*/
|
|
atomic_set(&ok_to_sleep, 0);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
}
|
|
spi_state = state;
|
|
SLATECOM_INFO("state = %d\n", state);
|
|
mutex_unlock(&slate_spi->xfer_mutex);
|
|
|
|
if (state == SLATECOM_SPI_FREE) {
|
|
SLATECOM_ERR("Need force resume\n");
|
|
slatecom_force_resume(&clnt_handle);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_set_spi_state);
|
|
|
|
static inline
|
|
void add_to_irq_list(struct cb_data *data)
|
|
{
|
|
list_add_tail(&data->list, &cb_head);
|
|
}
|
|
|
|
static uint8_t is_slatecom_ready(void)
|
|
{
|
|
if (slate_com_drv == NULL)
|
|
pr_err("driver not probed yet\n");
|
|
return (slate_com_drv != NULL ? 1 : 0);
|
|
}
|
|
|
|
static void slate_spi_reinit_xfer(struct spi_transfer *xfer)
|
|
{
|
|
xfer->tx_buf = NULL;
|
|
xfer->rx_buf = NULL;
|
|
xfer->delay.value = 0;
|
|
xfer->len = 0;
|
|
}
|
|
|
|
static int read_slate_locl(enum slatecom_req_type req_type,
|
|
uint32_t no_of_words, void *buf)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
struct slate_spi_priv *spi = NULL;
|
|
int ret = 0;
|
|
|
|
if (!slate_com_drv || !buf) {
|
|
pr_err("driver not probed yet or buf is empty\n");
|
|
return -ENODEV;
|
|
}
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
clnt_handle.slate_spi = spi;
|
|
|
|
switch (req_type) {
|
|
case SLATECOM_READ_REG:
|
|
ret = slatecom_reg_read(&clnt_handle,
|
|
SLATE_STATUS_REG, no_of_words, buf);
|
|
break;
|
|
case SLATECOM_READ_FIFO:
|
|
ret = slatecom_fifo_read(&clnt_handle, no_of_words, buf);
|
|
break;
|
|
case SLATECOM_WRITE_REG:
|
|
ret = slatecom_reg_write(&clnt_handle, SLATE_CMND_REG,
|
|
no_of_words, buf);
|
|
break;
|
|
case SLATECOM_READ_AHB:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int slatecom_transfer(void *handle, uint8_t *tx_buf,
|
|
uint8_t *rx_buf, uint32_t txn_len, uint32_t freq, bool flag)
|
|
{
|
|
struct spi_transfer *tx_xfer;
|
|
struct slate_spi_priv *slate_spi;
|
|
struct slate_context *cntx;
|
|
struct spi_device *spi = NULL;
|
|
int ret = 0;
|
|
|
|
if (!handle || !tx_buf)
|
|
return -EINVAL;
|
|
|
|
cntx = (struct slate_context *)handle;
|
|
|
|
if (cntx->state == SLATECOM_PROB_WAIT) {
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
cntx->slate_spi = container_of(slate_com_drv,
|
|
struct slate_spi_priv, lhandle);
|
|
cntx->state = SLATECOM_PROB_SUCCESS;
|
|
}
|
|
slate_spi = cntx->slate_spi;
|
|
|
|
if (!slate_spi)
|
|
return -ENODEV;
|
|
|
|
tx_xfer = &slate_spi->xfer1;
|
|
spi = slate_spi->spi;
|
|
|
|
mutex_lock(&slate_spi->xfer_mutex);
|
|
|
|
if (!atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_ERR("slatecom is inactive\n");
|
|
mutex_unlock(&slate_spi->xfer_mutex);
|
|
return -ECANCELED;
|
|
}
|
|
if (flag) {
|
|
SLATECOM_ERR("OK_TO_SLEEP Flag is set\n");
|
|
atomic_set(&slate_is_spi_active, 0);
|
|
}
|
|
|
|
slate_spi_reinit_xfer(tx_xfer);
|
|
tx_xfer->tx_buf = tx_buf;
|
|
if (rx_buf)
|
|
tx_xfer->rx_buf = rx_buf;
|
|
|
|
tx_xfer->len = txn_len;
|
|
SLATECOM_INFO("txn_len = %d\n", txn_len);
|
|
tx_xfer->speed_hz = freq;
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("SPI is held by TZ, skip spi_sync\n");
|
|
mutex_unlock(&slate_spi->xfer_mutex);
|
|
return -EBUSY;
|
|
}
|
|
ret = spi_sync(spi, &slate_spi->msg1);
|
|
mutex_unlock(&slate_spi->xfer_mutex);
|
|
|
|
if (ret)
|
|
SLATECOM_ERR("SPI transaction failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* SLATE-COM Interrupt handling */
|
|
static inline
|
|
void send_event(enum slatecom_event_type event,
|
|
void *data)
|
|
{
|
|
struct list_head *pos;
|
|
struct cb_data *cb;
|
|
|
|
/* send interrupt notification for each
|
|
* registered call-back
|
|
*/
|
|
list_for_each(pos, &cb_head) {
|
|
cb = list_entry(pos, struct cb_data, list);
|
|
cb->slatecom_notification_cb(cb->handle,
|
|
cb->priv, event, data);
|
|
}
|
|
}
|
|
|
|
void slatecom_slatedown_handler(void)
|
|
{
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!is_slatecom_ready())
|
|
return;
|
|
spi = get_spi_device();
|
|
g_slav_status_reg = 0;
|
|
atomic_set(&ok_to_sleep, 0);
|
|
pm_runtime_get_sync(&spi->dev);
|
|
send_event(SLATECOM_EVENT_RESET_OCCURRED, NULL);
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_slatedown_handler);
|
|
|
|
static void parse_fifo(uint8_t *data, uint16_t data_len, union slatecom_event_data_type *event_data)
|
|
{
|
|
uint16_t p_len;
|
|
uint16_t event_id;
|
|
void *evnt_data;
|
|
|
|
while (*data != '\0') {
|
|
if (data_len < HED_EVENT_ID_LEN)
|
|
break;
|
|
event_id = *((uint16_t *) data);
|
|
data = data + HED_EVENT_ID_LEN;
|
|
data_len = data_len - HED_EVENT_ID_LEN;
|
|
if (data_len < HED_EVENT_SIZE_LEN)
|
|
break;
|
|
p_len = *((uint16_t *) data);
|
|
data = data + HED_EVENT_SIZE_LEN;
|
|
data_len = data_len - HED_EVENT_SIZE_LEN;
|
|
|
|
if (event_id == 0x0001) {
|
|
evnt_data = kmalloc(p_len, GFP_KERNEL);
|
|
if (evnt_data != NULL) {
|
|
memcpy(evnt_data, data, p_len);
|
|
event_data->fifo_data.to_master_fifo_used =
|
|
p_len/SLATE_SPI_WORD_SIZE;
|
|
event_data->fifo_data.data = evnt_data;
|
|
send_event(SLATECOM_EVENT_TO_MASTER_FIFO_USED,
|
|
event_data);
|
|
}
|
|
} else if (event_id == 0xc8) {
|
|
data = data + 12;
|
|
data_len = data_len - 12;
|
|
SLATECOM_INFO("Packet Received = 0x%X, len = %u\n", event_id, p_len);
|
|
}
|
|
|
|
data = data + p_len;
|
|
data_len = data_len - p_len;
|
|
}
|
|
if (!list_empty(&pr_lst_hd))
|
|
queue_work(wq, &input_work);
|
|
}
|
|
|
|
static void send_back_notification(uint32_t slav_status_reg,
|
|
uint32_t slav_status_auto_clear_reg,
|
|
uint32_t fifo_fill_reg, uint32_t fifo_size_reg)
|
|
{
|
|
uint16_t master_fifo_used;
|
|
uint16_t slave_fifo_free;
|
|
uint32_t *ptr;
|
|
int ret = 0;
|
|
uint32_t oem_provisioning_status;
|
|
union slatecom_event_data_type event_data = { .fifo_data = {0} };
|
|
|
|
master_fifo_used = (uint16_t)fifo_fill_reg;
|
|
slave_fifo_free = (uint16_t)(fifo_fill_reg >> 16);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(31))
|
|
send_event(SLATECOM_EVENT_RESET_OCCURRED, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(30))
|
|
send_event(SLATECOM_EVENT_ERROR_WRITE_FIFO_OVERRUN, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(29))
|
|
send_event(SLATECOM_EVENT_ERROR_WRITE_FIFO_BUS_ERR, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(28))
|
|
send_event(SLATECOM_EVENT_ERROR_WRITE_FIFO_ACCESS, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(27))
|
|
send_event(SLATECOM_EVENT_ERROR_READ_FIFO_UNDERRUN, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(26))
|
|
send_event(SLATECOM_EVENT_ERROR_READ_FIFO_BUS_ERR, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(25))
|
|
send_event(SLATECOM_EVENT_ERROR_READ_FIFO_ACCESS, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(24))
|
|
send_event(SLATECOM_EVENT_ERROR_TRUNCATED_READ, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(23))
|
|
send_event(SLATECOM_EVENT_ERROR_TRUNCATED_WRITE, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(22))
|
|
send_event(SLATECOM_EVENT_ERROR_AHB_ILLEGAL_ADDRESS, NULL);
|
|
|
|
if (slav_status_auto_clear_reg & BIT(21))
|
|
send_event(SLATECOM_EVENT_ERROR_AHB_BUS_ERR, NULL);
|
|
|
|
/* check if SLATE status is changed */
|
|
if (g_slav_status_reg ^ slav_status_reg) {
|
|
SLATECOM_ERR("Slate status 0x%x\n", slav_status_reg);
|
|
if (slav_status_reg & BIT(30)) {
|
|
event_data.application_running = true;
|
|
send_event(SLATECOM_EVENT_APPLICATION_RUNNING,
|
|
&event_data);
|
|
}
|
|
|
|
if (slav_status_reg & BIT(29)) {
|
|
event_data.to_slave_fifo_ready = true;
|
|
send_event(SLATECOM_EVENT_TO_SLAVE_FIFO_READY,
|
|
&event_data);
|
|
}
|
|
|
|
if (slav_status_reg & BIT(28)) {
|
|
event_data.to_master_fifo_ready = true;
|
|
send_event(SLATECOM_EVENT_TO_MASTER_FIFO_READY,
|
|
&event_data);
|
|
}
|
|
|
|
if (slav_status_reg & BIT(27)) {
|
|
event_data.ahb_ready = true;
|
|
send_event(SLATECOM_EVENT_AHB_READY,
|
|
&event_data);
|
|
}
|
|
|
|
if (slav_status_reg & BIT(26)) {
|
|
pr_err("Slate DSP DOWN\n");
|
|
state_ops.set_dsp_state(false);
|
|
} else if (slav_status_reg & BIT(30)) {
|
|
if (!(slav_status_reg & BIT(26))) {
|
|
pr_err("Slate DSP UP\n");
|
|
state_ops.set_dsp_state(true);
|
|
}
|
|
}
|
|
|
|
if (slav_status_reg & BIT(25)) {
|
|
pr_err("Slate BT DOWN\n");
|
|
state_ops.set_bt_state(false);
|
|
} else if (slav_status_reg & BIT(30)) {
|
|
if (!(slav_status_reg & BIT(25))) {
|
|
pr_err("Slate BT UP\n");
|
|
state_ops.set_bt_state(true);
|
|
}
|
|
}
|
|
|
|
oem_provisioning_status = slav_status_reg & (BIT(23) | BIT(24));
|
|
oem_provisioning_status = ((oem_provisioning_status<<7)>>30);
|
|
SLATECOM_ERR("Slate OEM prov. status 0x%x\n", oem_provisioning_status);
|
|
}
|
|
|
|
if (master_fifo_used > 0) {
|
|
ptr = kzalloc(master_fifo_used*SLATE_SPI_WORD_SIZE + 1,
|
|
GFP_KERNEL | GFP_ATOMIC);
|
|
if (ptr != NULL) {
|
|
ret = read_slate_locl(SLATECOM_READ_FIFO,
|
|
master_fifo_used, ptr);
|
|
if (!ret) {
|
|
augmnt_fifo((uint8_t *)ptr,
|
|
master_fifo_used*SLATE_SPI_WORD_SIZE);
|
|
parse_fifo((uint8_t *)ptr,
|
|
master_fifo_used*SLATE_SPI_WORD_SIZE, &event_data);
|
|
}
|
|
kfree(ptr);
|
|
}
|
|
}
|
|
|
|
event_data.to_slave_fifo_free = slave_fifo_free;
|
|
send_event(SLATECOM_EVENT_TO_SLAVE_FIFO_FREE, &event_data);
|
|
}
|
|
|
|
static void slate_irq_tasklet_hndlr_l(void)
|
|
{
|
|
uint32_t slave_status_reg;
|
|
uint32_t glink_isr_reg;
|
|
uint32_t slav_status_auto_clear_reg;
|
|
uint32_t fifo_fill_reg;
|
|
uint32_t fifo_size_reg;
|
|
int ret = 0;
|
|
uint32_t irq_buf[5] = {0};
|
|
uint32_t cmnd_reg = 0;
|
|
struct slate_context clnt_handle;
|
|
struct slate_spi_priv *spi = NULL;
|
|
|
|
if (!slate_com_drv)
|
|
return;
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
clnt_handle.slate_spi = spi;
|
|
|
|
ret = slatecom_reg_read_internal(&clnt_handle, SLATE_STATUS_REG, 5, &irq_buf[0]);
|
|
if (ret) {
|
|
SLATECOM_ERR("Returning from tasklet handler with value %d\n", ret);
|
|
return;
|
|
}
|
|
/* save current state */
|
|
slave_status_reg = irq_buf[0];
|
|
glink_isr_reg = irq_buf[1];
|
|
slav_status_auto_clear_reg = irq_buf[2];
|
|
fifo_fill_reg = irq_buf[3];
|
|
fifo_size_reg = irq_buf[4];
|
|
|
|
if (slav_status_auto_clear_reg & SLATE_PAUSE_REQ) {
|
|
cmnd_reg |= SLATE_PAUSE_OK;
|
|
ret = slatecom_reg_write_cmd(&clnt_handle,
|
|
SLATE_CMND_REG, 1, &cmnd_reg, false);
|
|
if (ret == 0) {
|
|
spi_state = SLATECOM_SPI_PAUSE;
|
|
SLATECOM_INFO("SPI is in Pause State\n");
|
|
}
|
|
}
|
|
|
|
if (slav_status_auto_clear_reg & SLATE_RESUME_IND) {
|
|
spi_state = SLATECOM_SPI_FREE;
|
|
SLATECOM_INFO("Apps to resume operation\n");
|
|
}
|
|
|
|
/* Check if there are any status updates */
|
|
if (slav_status_auto_clear_reg & OK_TO_SLEEP_CLEARED) {
|
|
SLATECOM_INFO("SLAVE_STATUS_READY = 0x%08lX, OK_TO_SLEEP_CLEARED = 0x%08lX\n",
|
|
(slave_status_reg & SLAVE_STATUS_READY),
|
|
(OK_TO_SLEEP_CLEARED & slav_status_auto_clear_reg));
|
|
g_slave_status_auto_clear_reg = slav_status_auto_clear_reg;
|
|
atomic_set(&ok_to_sleep, 0);
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
complete(&slate_resume_wait);
|
|
}
|
|
|
|
send_back_notification(slave_status_reg,
|
|
slav_status_auto_clear_reg, fifo_fill_reg, fifo_size_reg);
|
|
|
|
g_slav_status_reg = slave_status_reg;
|
|
}
|
|
|
|
static int wakeup_ahb_read(void *handle)
|
|
{
|
|
uint8_t *tx_ahb_buf = NULL;
|
|
uint8_t *rx_ahb_buf = fxd_mem_buffer;
|
|
uint32_t ahb_addr = 0x200E1800;
|
|
uint32_t txn_len;
|
|
uint8_t cmnd = 0;
|
|
int ret = 0;
|
|
|
|
SLATECOM_INFO("slatecom AHB read to resume\n");
|
|
tx_ahb_buf = kmalloc(TX_AHB_BUF_SIZE, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_ahb_buf)
|
|
return -ENOMEM;
|
|
txn_len = 8;
|
|
cmnd |= SLATE_SPI_AHB_READ_CMD;
|
|
memcpy(tx_ahb_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_ahb_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
|
|
|
|
ret = slatecom_transfer(handle, tx_ahb_buf, rx_ahb_buf, txn_len, SPI_FREQ_1MHZ, false);
|
|
if (ret)
|
|
pr_err("slatecom_transfer fail with error %d\n", ret);
|
|
kfree(tx_ahb_buf);
|
|
return ret;
|
|
}
|
|
|
|
/* Returns 1, if the slate spi is active */
|
|
static int is_slate_resume(void *handle, uint32_t *slav_status_reg,
|
|
uint32_t *slav_status_auto_clear_reg)
|
|
{
|
|
uint32_t txn_len;
|
|
int ret = 0;
|
|
uint8_t *tx_buf = NULL;
|
|
uint8_t *rx_buf = NULL;
|
|
uint32_t read_buf[5] = {0};
|
|
uint32_t size;
|
|
uint32_t num_regs = 5;
|
|
uint8_t reg_start_addr = SLATE_STATUS_REG;
|
|
uint8_t cmnd = 0;
|
|
ktime_t delta;
|
|
s64 time_elapsed;
|
|
|
|
SLATECOM_INFO("slatecom entry\n");
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
printk_ratelimited("SPI is held by TZ\n");
|
|
ret = -EBUSY;
|
|
goto ret_err;
|
|
}
|
|
|
|
/* require a min time gap between OK_TO_SLEEP message and resume. */
|
|
delta = ktime_sub(ktime_get(), sleep_time_start);
|
|
time_elapsed = ktime_to_ms(delta);
|
|
if (time_elapsed < MIN_SLEEP_TIME) {
|
|
pr_err("avoid aggresive wakeup, sleep for %lld ms\n",
|
|
MIN_SLEEP_TIME - time_elapsed);
|
|
msleep(MIN_SLEEP_TIME - time_elapsed);
|
|
}
|
|
|
|
size = num_regs * SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_READ_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto ret_err;
|
|
}
|
|
|
|
rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!rx_buf) {
|
|
kfree(tx_buf);
|
|
ret = -ENOMEM;
|
|
goto ret_err;
|
|
}
|
|
|
|
cmnd |= reg_start_addr;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
ret = slatecom_transfer(handle, tx_buf, rx_buf, txn_len, SPI_FREQ_40MHZ, false);
|
|
|
|
if (!ret) {
|
|
memcpy(read_buf, rx_buf+SLATE_SPI_READ_LEN, size);
|
|
*slav_status_reg = read_buf[0];
|
|
*slav_status_auto_clear_reg = read_buf[2];
|
|
SLATECOM_INFO("slav_status_auto_clear_reg = 0x%X - OK_TO_SLEEP_CLEARED = 0x%08lX\n",
|
|
*slav_status_auto_clear_reg,
|
|
(*slav_status_auto_clear_reg & OK_TO_SLEEP_CLEARED));
|
|
}
|
|
kfree(rx_buf);
|
|
kfree(tx_buf);
|
|
|
|
ret_err:
|
|
return ((*slav_status_reg) & BIT(31));
|
|
}
|
|
|
|
void update_s2a_status(bool s2a_status_value)
|
|
{
|
|
s2a_status = s2a_status_value;
|
|
}
|
|
EXPORT_SYMBOL_GPL(update_s2a_status);
|
|
|
|
static int slatecom_resume_l(void *handle)
|
|
{
|
|
struct slate_context *cntx;
|
|
uint32_t slav_status_reg = 0;
|
|
uint32_t slav_status_auto_clear_reg = 0;
|
|
int retry = 0;
|
|
int ret = 0;
|
|
uint32_t cmnd_reg = 0;
|
|
|
|
if (handle == NULL) {
|
|
SLATECOM_ERR("slatecom handle null\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_ERR("slatecom is inactive. Returning %d\n", -ECANCELED);
|
|
return -ECANCELED;
|
|
}
|
|
cntx = (struct slate_context *)handle;
|
|
|
|
/* if client is outside slatecom scope and
|
|
* handle is provided before SLATECOM probed
|
|
*/
|
|
if (cntx->state == SLATECOM_PROB_WAIT) {
|
|
SLATECOM_INFO("handle is provided before SLATECOM probed\n");
|
|
if (!is_slatecom_ready())
|
|
return -EAGAIN;
|
|
cntx->slate_spi = container_of(slate_com_drv,
|
|
struct slate_spi_priv, lhandle);
|
|
cntx->state = SLATECOM_PROB_SUCCESS;
|
|
}
|
|
|
|
mutex_lock(&slate_resume_mutex);
|
|
if (atomic_read(&state) == SLATECOM_STATE_ACTIVE) {
|
|
SLATECOM_ERR("Slatecom in active state\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (!(g_slav_status_reg & BIT(31)) || !s2a_status) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI resume\n");
|
|
goto unlock;
|
|
}
|
|
|
|
do {
|
|
reinit_completion(&slate_resume_wait);
|
|
|
|
is_slate_resume(handle, &slav_status_reg, &slav_status_auto_clear_reg);
|
|
SLATECOM_INFO("SLAVE_STATUS_READY = 0x%08lX, OK_TO_SLEEP_CLEARED = 0x%08lX\n",
|
|
(slav_status_reg & SLAVE_STATUS_READY),
|
|
(slav_status_auto_clear_reg & OK_TO_SLEEP_CLEARED));
|
|
|
|
if (slav_status_reg & SLAVE_STATUS_READY) {
|
|
if (slav_status_auto_clear_reg & OK_TO_SLEEP_CLEARED) {
|
|
atomic_set(&ok_to_sleep, 0);
|
|
goto complete;
|
|
}
|
|
}
|
|
wakeup_ahb_read(handle);
|
|
ret = wait_for_completion_timeout(&slate_resume_wait,
|
|
msecs_to_jiffies(SLATE_RESUME_IRQ_TIMEOUT));
|
|
|
|
SLATECOM_INFO("resume local ret = %d\n", ret);
|
|
if (atomic_read(&ok_to_sleep) == 0)
|
|
goto complete;
|
|
|
|
retry++;
|
|
} while (retry < MAX_RETRY);
|
|
|
|
reinit_completion(&slate_resume_wait);
|
|
cmnd_reg |= SLATE_HEALTH_CHECK;
|
|
ret = slatecom_reg_write_cmd(cntx,
|
|
SLATE_CMND_REG, 1, &cmnd_reg, false);
|
|
if (ret < 0)
|
|
SLATECOM_ERR("SLATE_HEALTH_CHECK write cmd failed\n");
|
|
ret = wait_for_completion_timeout(&slate_resume_wait,
|
|
msecs_to_jiffies(SLATE_RESUME_IRQ_TIMEOUT));
|
|
SLATECOM_INFO("slate health check ret:%d\n", ret);
|
|
|
|
if (atomic_read(&ok_to_sleep) == 0)
|
|
goto complete;
|
|
if (retry == MAX_RETRY) {
|
|
mutex_unlock(&slate_resume_mutex);
|
|
/* SLATE failed to resume. Trigger watchdog. */
|
|
SLATECOM_ERR("SLATE failed to resume, gpio#95 value is: %d\n", gpio_get_value(95));
|
|
BUG();
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
complete:
|
|
SLATECOM_INFO("slatecom resume completed\n");
|
|
atomic_set(&state, SLATECOM_STATE_ACTIVE);
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
|
|
unlock:
|
|
g_slave_status_auto_clear_reg = 0;
|
|
mutex_unlock(&slate_resume_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static int slatecom_force_resume(void *handle)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
SLATECOM_INFO("Doing force resume\n");
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
|
|
ret = slatecom_resume_l(handle);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return 0;
|
|
}
|
|
|
|
int slatecom_ahb_read(void *handle, uint32_t ahb_start_addr,
|
|
uint32_t num_words, void *read_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint8_t *rx_buf;
|
|
uint32_t size;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
uint32_t ahb_addr = 0;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!handle || !read_buf || num_words == 0
|
|
|| num_words > SLATE_SPI_MAX_WORDS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
size = num_words*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_AHB_READ_CMD_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!rx_buf) {
|
|
kfree(tx_buf);
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
cmnd |= SLATE_SPI_AHB_READ_CMD;
|
|
ahb_addr |= ahb_start_addr;
|
|
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, rx_buf, txn_len, SPI_FREQ_40MHZ, false);
|
|
|
|
if (!ret)
|
|
memcpy(read_buf, rx_buf+SLATE_SPI_AHB_READ_CMD_LEN, size);
|
|
|
|
kfree(tx_buf);
|
|
kfree(rx_buf);
|
|
|
|
error_ret:
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_ahb_read);
|
|
|
|
int slatecom_ahb_write_bytes(void *handle, uint32_t ahb_start_addr,
|
|
uint32_t num_bytes, void *write_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
uint32_t ahb_addr = 0;
|
|
uint32_t curr_num_bytes;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!handle || !write_buf || num_bytes == 0
|
|
|| num_bytes > (SLATE_SPI_MAX_WORDS * sizeof(int))) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
ahb_addr = ahb_start_addr;
|
|
|
|
mutex_lock(&cma_buffer_lock);
|
|
while (num_bytes) {
|
|
curr_num_bytes = (num_bytes < WR_BUF_SIZE_IN_BYTES_FOR_USE) ?
|
|
num_bytes : WR_BUF_SIZE_IN_BYTES_FOR_USE;
|
|
|
|
txn_len = SLATE_SPI_AHB_CMD_LEN + curr_num_bytes;
|
|
|
|
if ((txn_len % sizeof(uint32_t)) != 0) {
|
|
txn_len +=
|
|
(sizeof(uint32_t) - (txn_len % sizeof(uint32_t)));
|
|
}
|
|
memset(fxd_mem_buffer, 0, txn_len);
|
|
tx_buf = fxd_mem_buffer;
|
|
|
|
cmnd |= SLATE_SPI_AHB_WRITE_CMD;
|
|
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
|
|
memcpy(tx_buf+SLATE_SPI_AHB_CMD_LEN, write_buf, curr_num_bytes);
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, NULL, txn_len, SPI_FREQ_40MHZ, false);
|
|
if (ret) {
|
|
SLATECOM_ERR("slatecom_transfer fail with error %d\n", ret);
|
|
goto error;
|
|
}
|
|
write_buf += curr_num_bytes;
|
|
ahb_addr += curr_num_bytes;
|
|
num_bytes -= curr_num_bytes;
|
|
}
|
|
|
|
error:
|
|
mutex_unlock(&cma_buffer_lock);
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_ahb_write_bytes);
|
|
|
|
int slatecom_ahb_write(void *handle, uint32_t ahb_start_addr,
|
|
uint32_t num_words, void *write_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
uint32_t ahb_addr = 0;
|
|
uint32_t curr_num_words;
|
|
uint32_t curr_num_bytes;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!handle || !write_buf || num_words == 0
|
|
|| num_words > SLATE_SPI_MAX_WORDS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
ahb_addr = ahb_start_addr;
|
|
|
|
mutex_lock(&cma_buffer_lock);
|
|
while (num_words) {
|
|
curr_num_words = (num_words < WR_BUF_SIZE_IN_WORDS_FOR_USE) ?
|
|
num_words : WR_BUF_SIZE_IN_WORDS_FOR_USE;
|
|
curr_num_bytes = curr_num_words * SLATE_SPI_WORD_SIZE;
|
|
|
|
txn_len = SLATE_SPI_AHB_CMD_LEN + curr_num_bytes;
|
|
memset(fxd_mem_buffer, 0, txn_len);
|
|
tx_buf = fxd_mem_buffer;
|
|
|
|
cmnd |= SLATE_SPI_AHB_WRITE_CMD;
|
|
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
|
|
memcpy(tx_buf+SLATE_SPI_AHB_CMD_LEN, write_buf, curr_num_bytes);
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, NULL, txn_len, SPI_FREQ_40MHZ, false);
|
|
if (ret) {
|
|
SLATECOM_ERR("slatecom_transfer fail with error %d\n", ret);
|
|
goto error;
|
|
}
|
|
write_buf += curr_num_bytes;
|
|
ahb_addr += curr_num_bytes;
|
|
num_words -= curr_num_words;
|
|
}
|
|
|
|
error:
|
|
mutex_unlock(&cma_buffer_lock);
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_ahb_write);
|
|
|
|
int slatecom_fifo_write(void *handle, uint32_t num_words,
|
|
void *write_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint32_t size;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (!handle || !write_buf || num_words == 0
|
|
|| num_words > SLATE_SPI_MAX_WORDS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
size = num_words*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_WRITE_CMND_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
cmnd |= SLATE_SPI_FIFO_WRITE_CMD;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_buf+sizeof(cmnd), write_buf, size);
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, NULL, txn_len, SPI_FREQ_40MHZ, false);
|
|
kfree(tx_buf);
|
|
|
|
error_ret:
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_fifo_write);
|
|
|
|
int slatecom_fifo_read(void *handle, uint32_t num_words,
|
|
void *read_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint8_t *rx_buf;
|
|
uint32_t size;
|
|
uint8_t cmnd = 0;
|
|
int ret = 0;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!handle || !read_buf || num_words == 0
|
|
|| num_words > SLATE_SPI_MAX_WORDS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
size = num_words*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_READ_LEN + size;
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!rx_buf) {
|
|
kfree(tx_buf);
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
cmnd |= SLATE_SPI_FIFO_READ_CMD;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, rx_buf, txn_len, SPI_FREQ_40MHZ, false);
|
|
|
|
if (!ret)
|
|
memcpy(read_buf, rx_buf+SLATE_SPI_READ_LEN, size);
|
|
kfree(tx_buf);
|
|
kfree(rx_buf);
|
|
|
|
error_ret:
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_fifo_read);
|
|
|
|
static int slatecom_reg_write_cmd(void *handle, uint8_t reg_start_addr,
|
|
uint8_t num_regs, void *write_buf, bool flag)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint32_t size;
|
|
uint8_t cmnd = 0;
|
|
int ret = 0;
|
|
|
|
if (!handle || !write_buf || num_regs == 0
|
|
|| num_regs > SLATE_SPI_MAX_REGS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
if (spi_state == SLATECOM_SPI_PAUSE) {
|
|
SLATECOM_ERR("Device in Pause State\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
size = num_regs*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_WRITE_CMND_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL);
|
|
|
|
if (!tx_buf)
|
|
return -ENOMEM;
|
|
|
|
cmnd |= reg_start_addr;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
memcpy(tx_buf+sizeof(cmnd), write_buf, size);
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, NULL, txn_len, SPI_FREQ_40MHZ, flag);
|
|
kfree(tx_buf);
|
|
return ret;
|
|
}
|
|
|
|
int slatecom_reg_write(void *handle, uint8_t reg_start_addr,
|
|
uint8_t num_regs, void *write_buf)
|
|
{
|
|
int ret = 0;
|
|
struct spi_device *spi = NULL;
|
|
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
ret = slatecom_reg_write_cmd(handle, reg_start_addr,
|
|
num_regs, write_buf, false);
|
|
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_reg_write);
|
|
|
|
static int slatecom_reg_read_internal(void *handle, uint8_t reg_start_addr,
|
|
uint32_t num_regs, void *read_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint8_t *rx_buf;
|
|
uint32_t size;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
|
|
if (!handle || !read_buf || num_regs == 0
|
|
|| num_regs > SLATE_SPI_MAX_REGS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
size = num_regs*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_READ_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!rx_buf) {
|
|
kfree(tx_buf);
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
cmnd |= reg_start_addr;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, rx_buf, txn_len, SPI_FREQ_40MHZ, false);
|
|
|
|
if (!ret)
|
|
memcpy(read_buf, rx_buf+SLATE_SPI_READ_LEN, size);
|
|
kfree(tx_buf);
|
|
kfree(rx_buf);
|
|
|
|
error_ret:
|
|
return ret;
|
|
}
|
|
|
|
int slatecom_reg_read(void *handle, uint8_t reg_start_addr,
|
|
uint32_t num_regs, void *read_buf)
|
|
{
|
|
uint32_t txn_len;
|
|
uint8_t *tx_buf;
|
|
uint8_t *rx_buf;
|
|
uint32_t size;
|
|
int ret = 0;
|
|
uint8_t cmnd = 0;
|
|
struct spi_device *spi = NULL;
|
|
|
|
if (!handle || !read_buf || num_regs == 0
|
|
|| num_regs > SLATE_SPI_MAX_REGS) {
|
|
SLATECOM_ERR("Invalid param\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!is_slatecom_ready())
|
|
return -ENODEV;
|
|
|
|
if (spi_state == SLATECOM_SPI_BUSY) {
|
|
SLATECOM_ERR("Device busy\n");
|
|
return -EBUSY;
|
|
}
|
|
spi = get_spi_device();
|
|
pm_runtime_get_sync(&spi->dev);
|
|
mutex_lock(&slate_task_mutex);
|
|
|
|
size = num_regs*SLATE_SPI_WORD_SIZE;
|
|
txn_len = SLATE_SPI_READ_LEN + size;
|
|
|
|
tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!tx_buf) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
|
|
if (!rx_buf) {
|
|
kfree(tx_buf);
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
cmnd |= reg_start_addr;
|
|
memcpy(tx_buf, &cmnd, sizeof(cmnd));
|
|
|
|
ret = slatecom_transfer(handle, tx_buf, rx_buf, txn_len, SPI_FREQ_40MHZ, false);
|
|
|
|
if (!ret)
|
|
memcpy(read_buf, rx_buf+SLATE_SPI_READ_LEN, size);
|
|
kfree(tx_buf);
|
|
kfree(rx_buf);
|
|
|
|
error_ret:
|
|
pm_runtime_mark_last_busy(&spi->dev);
|
|
pm_runtime_put_sync_autosuspend(&spi->dev);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_reg_read);
|
|
|
|
int slatecom_resume(void *handle)
|
|
{
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_resume);
|
|
|
|
int slatecom_suspend(void *handle)
|
|
{
|
|
if (!handle)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_suspend);
|
|
|
|
void *slatecom_open(struct slatecom_open_config_type *open_config)
|
|
{
|
|
struct slate_spi_priv *spi;
|
|
struct cb_data *irq_notification;
|
|
struct slate_context *clnt_handle =
|
|
kzalloc(sizeof(*clnt_handle), GFP_KERNEL);
|
|
|
|
if (!clnt_handle)
|
|
return NULL;
|
|
|
|
/* Client handle Set-up */
|
|
if (!is_slatecom_ready()) {
|
|
clnt_handle->slate_spi = NULL;
|
|
clnt_handle->state = SLATECOM_PROB_WAIT;
|
|
} else {
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
clnt_handle->slate_spi = spi;
|
|
clnt_handle->state = SLATECOM_PROB_SUCCESS;
|
|
}
|
|
clnt_handle->cb = NULL;
|
|
/* Interrupt callback Set-up */
|
|
if (open_config && open_config->slatecom_notification_cb) {
|
|
irq_notification = kzalloc(sizeof(*irq_notification),
|
|
GFP_KERNEL);
|
|
if (!irq_notification)
|
|
goto error_ret;
|
|
|
|
/* set irq node */
|
|
irq_notification->handle = clnt_handle;
|
|
irq_notification->priv = open_config->priv;
|
|
irq_notification->slatecom_notification_cb =
|
|
open_config->slatecom_notification_cb;
|
|
add_to_irq_list(irq_notification);
|
|
clnt_handle->cb = irq_notification;
|
|
}
|
|
return clnt_handle;
|
|
|
|
error_ret:
|
|
kfree(clnt_handle);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_open);
|
|
|
|
int slatecom_close(void **handle)
|
|
{
|
|
struct slate_context *lhandle;
|
|
struct cb_data *cb = NULL;
|
|
|
|
if (*handle == NULL)
|
|
return -EINVAL;
|
|
lhandle = *handle;
|
|
cb = lhandle->cb;
|
|
if (cb)
|
|
list_del(&cb->list);
|
|
|
|
kfree(*handle);
|
|
*handle = NULL;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(slatecom_close);
|
|
|
|
static irqreturn_t slate_irq_tasklet_hndlr(int irq, void *device)
|
|
{
|
|
struct slate_spi_priv *slate_spi = device;
|
|
uint32_t slav_status_reg = 0;
|
|
uint32_t slav_status_auto_clear_reg = 0;
|
|
struct slate_context clnt_handle;
|
|
uint32_t cmnd_reg = 0;
|
|
int ret = 0;
|
|
|
|
clnt_handle.slate_spi = slate_spi;
|
|
|
|
/* set active to allow spi transfer */
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
|
|
if (atomic_read(&ok_to_sleep) == 1)
|
|
SLATECOM_INFO("Interrupt received in XO/Deepsleep\n");
|
|
|
|
/* check if call-back exists */
|
|
if (atomic_read(&slate_is_runtime_suspend)) {
|
|
SLATECOM_INFO("Interrupt received in suspend state\n");
|
|
atomic_set(&slate_spi->irq_lock, 1);
|
|
|
|
is_slate_resume(&clnt_handle, &slav_status_reg, &slav_status_auto_clear_reg);
|
|
|
|
if (slav_status_reg & SLAVE_STATUS_READY) {
|
|
if (slav_status_auto_clear_reg & OK_TO_SLEEP_CLEARED) {
|
|
SLATECOM_INFO("Marked Complete\n");
|
|
atomic_set(&ok_to_sleep, 0);
|
|
complete(&slate_resume_wait);
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
}
|
|
|
|
}
|
|
if (slav_status_auto_clear_reg & SLATE_PAUSE_REQ) {
|
|
cmnd_reg |= SLATE_PAUSE_OK;
|
|
ret = slatecom_reg_write_cmd(&clnt_handle,
|
|
SLATE_CMND_REG, 1, &cmnd_reg, false);
|
|
if (ret == 0) {
|
|
spi_state = SLATECOM_SPI_PAUSE;
|
|
SLATECOM_INFO("SPI is in Pause State\n");
|
|
}
|
|
}
|
|
if (slav_status_auto_clear_reg & SLATE_RESUME_IND) {
|
|
spi_state = SLATECOM_SPI_FREE;
|
|
SLATECOM_INFO("Apps to resume operation\n");
|
|
}
|
|
atomic_set(&slate_spi->irq_lock, 0);
|
|
} else if (list_empty(&cb_head)) {
|
|
SLATECOM_INFO("No callback registered\n");
|
|
} else if (spi_state == SLATECOM_SPI_BUSY) {
|
|
/* delay for SPI to be freed */
|
|
msleep(50);
|
|
} else if (atomic_read(&slate_spi->irq_lock) == 0) {
|
|
atomic_set(&slate_spi->irq_lock, 1);
|
|
slate_irq_tasklet_hndlr_l();
|
|
atomic_set(&slate_spi->irq_lock, 0);
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void slate_spi_init(struct slate_spi_priv *slate_spi)
|
|
{
|
|
if (!slate_spi) {
|
|
pr_err("device not found\n");
|
|
return;
|
|
}
|
|
|
|
/* SLATECOM SPI set-up */
|
|
mutex_init(&slate_spi->xfer_mutex);
|
|
spi_message_init(&slate_spi->msg1);
|
|
spi_message_add_tail(&slate_spi->xfer1, &slate_spi->msg1);
|
|
|
|
/* SLATECOM IRQ set-up */
|
|
atomic_set(&slate_spi->irq_lock, 0);
|
|
|
|
spi_state = SLATECOM_SPI_FREE;
|
|
|
|
wq = create_singlethread_workqueue("input_wq");
|
|
|
|
atomic_set(&state, SLATECOM_STATE_ACTIVE);
|
|
|
|
slate_com_drv = &slate_spi->lhandle;
|
|
|
|
mutex_init(&slate_resume_mutex);
|
|
mutex_init(&slate_task_mutex);
|
|
|
|
fxd_mem_buffer = kmalloc(CMA_BFFR_POOL_SIZE, GFP_KERNEL | GFP_ATOMIC);
|
|
|
|
mutex_init(&cma_buffer_lock);
|
|
|
|
slatecom_ipc_log = ipc_log_context_create(LOG_PAGES_CNT, "slatecom_spi", 0);
|
|
|
|
}
|
|
|
|
static int slatecom_pm_notifier(struct notifier_block *nb, unsigned long event, void *unused)
|
|
{
|
|
switch (event) {
|
|
#ifdef CONFIG_HIBERNATION
|
|
|
|
case PM_HIBERNATION_PREPARE:
|
|
pr_err("Hibernate entry\n");
|
|
is_hibernate = true;
|
|
break;
|
|
|
|
case PM_POST_HIBERNATION:
|
|
pr_err("Hibernate exit\n");
|
|
is_hibernate = false;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
WARN_ONCE(1, "Default case: PM Notifier\n");
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static struct notifier_block slatecom_pm_nb = {
|
|
.notifier_call = slatecom_pm_notifier,
|
|
};
|
|
|
|
static int slate_spi_probe(struct spi_device *spi)
|
|
{
|
|
struct slate_spi_priv *slate_spi;
|
|
int ret = 0;
|
|
struct device_node *node;
|
|
int irq_gpio = 0;
|
|
|
|
slate_spi = devm_kzalloc(&spi->dev, sizeof(*slate_spi),
|
|
GFP_KERNEL | GFP_ATOMIC);
|
|
|
|
pr_info("%s started\n", __func__);
|
|
|
|
if (!slate_spi)
|
|
return -ENOMEM;
|
|
slate_spi->spi = spi;
|
|
spi_set_drvdata(spi, slate_spi);
|
|
slate_spi_init(slate_spi);
|
|
|
|
/* SLATECOM Interrupt probe */
|
|
node = slate_spi->spi->dev.of_node;
|
|
irq_gpio = of_get_named_gpio(node, "qcom,irq-gpio", 0);
|
|
if (!gpio_is_valid(irq_gpio)) {
|
|
pr_err("gpio %d found is not valid\n", irq_gpio);
|
|
goto err_ret;
|
|
}
|
|
ret = gpio_request(irq_gpio, "slatecom_gpio");
|
|
if (ret) {
|
|
pr_err("gpio %d request failed\n", irq_gpio);
|
|
goto err_ret;
|
|
}
|
|
ret = gpio_direction_input(irq_gpio);
|
|
if (ret) {
|
|
pr_err("gpio_direction_input not set: %d\n", ret);
|
|
goto err_ret;
|
|
}
|
|
slate_irq = gpio_to_irq(irq_gpio);
|
|
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
dma_set_coherent_mask(&spi->dev, 0);
|
|
|
|
/* Enable Runtime PM for this device */
|
|
pm_runtime_enable(&spi->dev);
|
|
pm_runtime_set_autosuspend_delay(&spi->dev, SLATE_SPI_AUTOSUSPEND_TIMEOUT);
|
|
pm_runtime_use_autosuspend(&spi->dev);
|
|
|
|
ret = register_pm_notifier(&slatecom_pm_nb);
|
|
if (ret) {
|
|
pr_err("slatecom notif error %d\n", ret);
|
|
goto err_ret;
|
|
}
|
|
|
|
pr_info("%s success\n", __func__);
|
|
pr_info("Slatecom Probed successfully\n");
|
|
return ret;
|
|
|
|
err_ret:
|
|
slate_com_drv = NULL;
|
|
mutex_destroy(&slate_spi->xfer_mutex);
|
|
spi_set_drvdata(spi, NULL);
|
|
if (gpio_is_valid(irq_gpio))
|
|
gpio_free(irq_gpio);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void slate_spi_remove(struct spi_device *spi)
|
|
{
|
|
struct slate_spi_priv *slate_spi = spi_get_drvdata(spi);
|
|
|
|
slate_com_drv = NULL;
|
|
pm_runtime_disable(&spi->dev);
|
|
mutex_destroy(&slate_spi->xfer_mutex);
|
|
spi_set_drvdata(spi, NULL);
|
|
kfree(fxd_mem_buffer);
|
|
mutex_destroy(&cma_buffer_lock);
|
|
mutex_destroy(&slate_task_mutex);
|
|
unregister_pm_notifier(&slatecom_pm_nb);
|
|
}
|
|
|
|
static void slate_spi_shutdown(struct spi_device *spi)
|
|
{
|
|
disable_irq(slate_irq);
|
|
slate_spi_remove(spi);
|
|
}
|
|
|
|
static int slatecom_pm_prepare(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
uint32_t cmnd_reg = 0;
|
|
struct spi_device *s_dev = to_spi_device(dev);
|
|
struct slate_spi_priv *slate_spi = spi_get_drvdata(s_dev);
|
|
int ret = 0;
|
|
|
|
g_slave_status_auto_clear_reg = 0;
|
|
clnt_handle.slate_spi = slate_spi;
|
|
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
if (is_hibernate)
|
|
cmnd_reg |= SLATE_OK_SLP_S2D;
|
|
else
|
|
cmnd_reg |= SLATE_OK_SLP_RBSC;
|
|
|
|
if (!atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_INFO("spi is already inactive, get_sync.\n");
|
|
pm_runtime_get_sync(&s_dev->dev);
|
|
usleep_range(5000, 10000);
|
|
} else {
|
|
SLATECOM_INFO("spi is already active, skip get_sync.\n");
|
|
}
|
|
|
|
atomic_set(&ok_to_sleep, 1);
|
|
ret = slatecom_reg_write_cmd(&clnt_handle, SLATE_CMND_REG, 1, &cmnd_reg, true);
|
|
if (ret < 0)
|
|
atomic_set(&ok_to_sleep, 0);
|
|
|
|
(!atomic_read(&slate_is_spi_active)) ? pm_runtime_put_sync(&s_dev->dev)
|
|
: SLATECOM_INFO("spi is already active, skip put_sync...\n");
|
|
|
|
sleep_time_start = ktime_get();
|
|
atomic_set(&slate_is_spi_active, 0);
|
|
atomic_set(&state, SLATECOM_STATE_SUSPEND_PREPARE);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
|
|
SLATECOM_INFO("reg write status: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t slate_irq_tasklet_hndlr_during_suspend(int irq, void *device)
|
|
{
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int slatecom_pm_suspend(struct device *dev)
|
|
{
|
|
struct spi_device *s_dev = to_spi_device(dev);
|
|
struct slate_spi_priv *slate_spi = spi_get_drvdata(s_dev);
|
|
int ret = 0;
|
|
|
|
SLATECOM_ERR("entry\n");
|
|
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
if ((g_slave_status_auto_clear_reg & OK_TO_SLEEP_CLEARED) &&
|
|
(g_slav_status_reg & SLAVE_STATUS_READY)) {
|
|
SLATECOM_ERR("Slate is in active, Abort Suspend\n");
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
atomic_set(&state, SLATECOM_STATE_ACTIVE);
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
g_slave_status_auto_clear_reg = 0;
|
|
return -ECANCELED;
|
|
}
|
|
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
|
|
free_irq(slate_irq, slate_spi);
|
|
ret = request_threaded_irq(slate_irq, NULL, slate_irq_tasklet_hndlr_during_suspend,
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "qcom-slate_spi", slate_spi);
|
|
|
|
if (atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_ERR("Slate interrupted, abort suspend\n");
|
|
return -ECANCELED;
|
|
}
|
|
|
|
SLATECOM_INFO("suspended\n");
|
|
|
|
atomic_set(&state, SLATECOM_STATE_SUSPEND);
|
|
return 0;
|
|
}
|
|
|
|
static int slatecom_pm_resume(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
int ret = 0;
|
|
struct slate_spi_priv *spi = NULL;
|
|
|
|
if (!slate_com_drv)
|
|
return -ENODEV;
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
SLATECOM_ERR("entry\n");
|
|
free_irq(slate_irq, spi);
|
|
ret = request_threaded_irq(slate_irq, NULL, slate_irq_tasklet_hndlr,
|
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "qcom-slate_spi", spi);
|
|
|
|
if (atomic_read(&spi->irq_lock) == 1) {
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
atomic_set(&state, SLATECOM_STATE_ACTIVE);
|
|
pr_debug("Shouldn't Execute\n");
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_INFO("Slatecom in resume state\n");
|
|
return 0;
|
|
}
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI resume\n");
|
|
return 0;
|
|
}
|
|
mutex_lock(&slate_task_mutex);
|
|
clnt_handle.slate_spi = spi;
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
ret = slatecom_resume_l(&clnt_handle);
|
|
SLATECOM_ERR("Slatecom resumed with : %d\n", ret);
|
|
mutex_unlock(&slate_task_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void slatecom_pm_complete(struct device *dev)
|
|
{
|
|
/* resume if state is still SLATECOM_STATE_SUSPEND_PREPARE*/
|
|
SLATECOM_ERR("entry\n");
|
|
if (atomic_read(&state) == SLATECOM_STATE_SUSPEND_PREPARE)
|
|
slatecom_pm_resume(dev);
|
|
}
|
|
|
|
static int slatecom_pm_runtime_suspend(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
uint32_t cmnd_reg = 0;
|
|
struct spi_device *s_dev = to_spi_device(dev);
|
|
struct slate_spi_priv *slate_spi = spi_get_drvdata(s_dev);
|
|
int ret = 0;
|
|
|
|
SLATECOM_INFO("entry\n");
|
|
clnt_handle.slate_spi = slate_spi;
|
|
|
|
if (atomic_read(&state) == SLATECOM_STATE_RUNTIME_SUSPEND)
|
|
return 0;
|
|
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
cmnd_reg |= SLATE_OK_SLP_SIF;
|
|
atomic_set(&ok_to_sleep, 1);
|
|
ret = slatecom_reg_write_cmd(&clnt_handle,
|
|
SLATE_CMND_REG, 1, &cmnd_reg, false);
|
|
sleep_time_start = ktime_get();
|
|
if (ret == 0) {
|
|
atomic_set(&state, SLATECOM_STATE_RUNTIME_SUSPEND);
|
|
atomic_set(&slate_is_spi_active, 0);
|
|
atomic_set(&slate_is_runtime_suspend, 1);
|
|
} else
|
|
atomic_set(&ok_to_sleep, 0);
|
|
|
|
SLATECOM_INFO("Runtime suspended with : %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int slatecom_pm_runtime_resume(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
int ret = 0;
|
|
struct slate_spi_priv *spi = NULL;
|
|
|
|
if (!slate_com_drv)
|
|
return -ENODEV;
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
SLATECOM_INFO("entry\n");
|
|
clnt_handle.slate_spi = spi;
|
|
|
|
if (atomic_read(&spi->irq_lock) == 1) {
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
atomic_set(&state, SLATECOM_STATE_ACTIVE);
|
|
pr_debug("Slate Already Woken up! Skip.....\n");
|
|
return 0;
|
|
}
|
|
mutex_lock(&slate_task_mutex);
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
ret = slatecom_resume_l(&clnt_handle);
|
|
SLATECOM_INFO("Slatecom Runtime resumed with : %d\n", ret);
|
|
mutex_unlock(&slate_task_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int slatecom_pm_freeze(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
struct spi_device *s_dev = to_spi_device(dev);
|
|
struct slate_spi_priv *slate_spi = spi_get_drvdata(s_dev);
|
|
|
|
clnt_handle.slate_spi = slate_spi;
|
|
if (atomic_read(&state) == SLATECOM_STATE_HIBERNATE)
|
|
return 0;
|
|
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI suspend\n");
|
|
return 0;
|
|
}
|
|
|
|
atomic_set(&slate_is_spi_active, 0);
|
|
atomic_set(&state, SLATECOM_STATE_HIBERNATE);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
|
|
SLATECOM_INFO("Slatecom freezed\n");
|
|
return 0;
|
|
}
|
|
|
|
static int slatecom_pm_restore(struct device *dev)
|
|
{
|
|
struct slate_context clnt_handle;
|
|
int ret = 0;
|
|
struct slate_spi_priv *spi = NULL;
|
|
|
|
if (!slate_com_drv)
|
|
return -ENODEV;
|
|
spi = container_of(slate_com_drv, struct slate_spi_priv, lhandle);
|
|
|
|
if (atomic_read(&slate_is_spi_active)) {
|
|
SLATECOM_INFO("Slatecom in restore state\n");
|
|
} else {
|
|
if (!(g_slav_status_reg & BIT(31))) {
|
|
SLATECOM_ERR("Slate boot is not complete, skip SPI resume\n");
|
|
return 0;
|
|
}
|
|
clnt_handle.slate_spi = spi;
|
|
atomic_set(&slate_is_spi_active, 1);
|
|
atomic_set(&slate_is_runtime_suspend, 0);
|
|
is_hibernate = false;
|
|
ret = slatecom_resume_l(&clnt_handle);
|
|
SLATECOM_INFO("Slatecom restore with : %d\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const struct dev_pm_ops slatecom_pm = {
|
|
.prepare = slatecom_pm_prepare,
|
|
.complete = slatecom_pm_complete,
|
|
.runtime_suspend = slatecom_pm_runtime_suspend,
|
|
.runtime_resume = slatecom_pm_runtime_resume,
|
|
.suspend = slatecom_pm_suspend,
|
|
.resume = slatecom_pm_resume,
|
|
.freeze = slatecom_pm_freeze,
|
|
.restore = slatecom_pm_restore,
|
|
};
|
|
|
|
static const struct of_device_id slate_spi_of_match[] = {
|
|
{ .compatible = "qcom,slate-spi", },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, slate_spi_of_match);
|
|
|
|
static struct spi_driver slate_spi_driver = {
|
|
.driver = {
|
|
.name = "slate-spi",
|
|
.of_match_table = slate_spi_of_match,
|
|
.pm = &slatecom_pm,
|
|
},
|
|
.probe = slate_spi_probe,
|
|
.remove = slate_spi_remove,
|
|
.shutdown = slate_spi_shutdown,
|
|
};
|
|
|
|
module_spi_driver(slate_spi_driver);
|
|
MODULE_DESCRIPTION("slate SPI driver");
|
|
MODULE_LICENSE("GPL");
|