lineage_kernel_xcoverpro/drivers/misc/gnss_if/gnss_link_device_shmem.h

445 lines
11 KiB
C
Raw Normal View History

2023-06-18 22:53:49 +00:00
/*
* Copyright (C) 2010 Samsung Electronics.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#ifndef __GNSS_LINK_DEVICE_SHMEM_H__
#define __GNSS_LINK_DEVICE_SHMEM_H__
#include <linux/mcu_ipc.h>
#include "gnss_link_device_memory.h"
#define IPC_WAKELOCK_TIMEOUT (HZ)
#define BCMD_WAKELOCK_TIMEOUT (HZ / 10) /* 100 msec */
struct shmem_circ {
u32 __iomem *head;
u32 __iomem *tail;
u8 __iomem *buff;
u32 size;
};
struct shmem_ipc_device {
struct shmem_circ txq;
struct shmem_circ rxq;
};
struct shmem_ipc_map {
struct shmem_ipc_device dev;
};
struct shmem_region {
u8 __iomem *vaddr; /* ioremap base address */
u32 paddr; /* physical base address */
u32 size; /* region size */
};
struct shmem_link_device {
struct link_device ld;
struct gnss_mbox *mbx;
struct gnss_shared_reg **reg;
/* Reserved memory information */
struct shmem_region res_mem;
/* Fault area information */
struct shmem_region fault_mem;
/* SHMEM (SHARED MEMORY) address, size, IRQ# */
struct shmem_region ipc_mem;
u32 ipc_reg_cnt;
/* IPC device map */
struct shmem_ipc_map ipc_map;
/* Pointers (aliases) to IPC device map */
struct shmem_ipc_device *dev;
/* MBOX number & IRQ */
int int_ap2gnss_ipc_msg;
int irq_gnss2ap_ipc_msg;
/* Wakelock for SHMEM device */
struct wake_lock wlock;
char wlock_name[GNSS_MAX_NAME_LEN];
/* for locking TX process */
spinlock_t tx_lock;
/* for retransmission under SHMEM flow control after TXQ full state */
atomic_t res_required;
//struct completion req_ack_cmpl;
/* for efficient RX process */
struct tasklet_struct rx_tsk;
struct delayed_work msg_rx_dwork;
struct io_device *iod;
/* for logging SHMEM status */
struct mem_status_queue tx_msq;
struct mem_status_queue rx_msq;
/* for logging SHMEM dump */
//struct trace_data_queue trace_list;
/* to hold/release "cp_wakeup" for PM (power-management) */
//struct delayed_work cp_sleep_dwork;
atomic_t ref_cnt;
//spinlock_t pm_lock;
};
/* converts from struct link_device* to struct xxx_link_device* */
#define to_shmem_link_device(linkdev) \
container_of(linkdev, struct shmem_link_device, ld)
void gnss_write_reg(struct shmem_link_device *, enum gnss_reg_type, u32);
u32 gnss_read_reg(struct shmem_link_device *, enum gnss_reg_type);
/**
* get_txq_head
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the value of a head (in) pointer in a TX queue.
*/
static inline u32 get_txq_head(struct shmem_link_device *shmd)
{
return gnss_read_reg(shmd, GNSS_REG_TX_HEAD);
}
/**
* get_txq_tail
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the value of a tail (out) pointer in a TX queue.
*
* It is useless for an AP to read a tail pointer in a TX queue twice to verify
* whether or not the value in the pointer is valid, because it can already have
* been updated by a GNSS after the first access from the AP.
*/
static inline u32 get_txq_tail(struct shmem_link_device *shmd)
{
return gnss_read_reg(shmd, GNSS_REG_TX_TAIL);
}
/**
* get_txq_buff
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the start address of the buffer in a TXQ.
*/
static inline u8 *get_txq_buff(struct shmem_link_device *shmd)
{
return shmd->dev->txq.buff;
}
/**
* get_txq_buff_size
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the size of the buffer in a TXQ.
*/
static inline u32 get_txq_buff_size(struct shmem_link_device *shmd)
{
return shmd->dev->txq.size;
}
/**
* get_rxq_head
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the value of a head (in) pointer in an RX queue.
*
* It is useless for an AP to read a head pointer in an RX queue twice to verify
* whether or not the value in the pointer is valid, because it can already have
* been updated by a GNSS after the first access from the AP.
*/
static inline u32 get_rxq_head(struct shmem_link_device *shmd)
{
return gnss_read_reg(shmd, GNSS_REG_RX_HEAD);
}
/**
* get_rxq_tail
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the value of a tail (in) pointer in an RX queue.
*/
static inline u32 get_rxq_tail(struct shmem_link_device *shmd)
{
return gnss_read_reg(shmd, GNSS_REG_RX_TAIL);
}
/**
* get_rxq_buff
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the start address of the buffer in an RXQ.
*/
static inline u8 *get_rxq_buff(struct shmem_link_device *shmd)
{
return shmd->dev->rxq.buff;
}
/**
* get_rxq_buff_size
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the size of the buffer in an RXQ.
*/
static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd)
{
return shmd->dev->rxq.size;
}
/**
* set_txq_head
* @shmd: pointer to an instance of shmem_link_device structure
* @in: value to be written to the head pointer in a TXQ
*/
static inline void set_txq_head(struct shmem_link_device *shmd, u32 in)
{
gnss_write_reg(shmd, GNSS_REG_TX_HEAD, in);
}
/**
* set_txq_tail
* @shmd: pointer to an instance of shmem_link_device structure
* @out: value to be written to the tail pointer in a TXQ
*/
static inline void set_txq_tail(struct shmem_link_device *shmd, u32 out)
{
gnss_write_reg(shmd, GNSS_REG_TX_TAIL, out);
}
/**
* set_rxq_head
* @shmd: pointer to an instance of shmem_link_device structure
* @in: value to be written to the head pointer in an RXQ
*/
static inline void set_rxq_head(struct shmem_link_device *shmd, u32 in)
{
gnss_write_reg(shmd, GNSS_REG_RX_HEAD, in);
}
/**
* set_rxq_tail
* @shmd: pointer to an instance of shmem_link_device structure
* @out: value to be written to the tail pointer in an RXQ
*/
static inline void set_rxq_tail(struct shmem_link_device *shmd, u32 out)
{
gnss_write_reg(shmd, GNSS_REG_RX_TAIL, out);
}
/**
* read_int2gnss
* @shmd: pointer to an instance of shmem_link_device structure
*
* Returns the value of the AP-to-GNSS interrupt register.
*/
static inline u16 read_int2gnss(struct shmem_link_device *shmd)
{
return mbox_get_value(MCU_GNSS, shmd->int_ap2gnss_ipc_msg);
}
/**
* reset_txq_circ
* @shmd: pointer to an instance of shmem_link_device structure
* @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
*
* Empties a TXQ by resetting the head (in) pointer with the value in the tail
* (out) pointer.
*/
static inline void reset_txq_circ(struct shmem_link_device *shmd)
{
struct link_device *ld = &shmd->ld;
u32 head = get_txq_head(shmd);
u32 tail = get_txq_tail(shmd);
gif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n",
ld->name, "FMT", head, tail);
set_txq_head(shmd, tail);
}
/**
* reset_rxq_circ
* @shmd: pointer to an instance of shmem_link_device structure
* @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
*
* Empties an RXQ by resetting the tail (out) pointer with the value in the head
* (in) pointer.
*/
static inline void reset_rxq_circ(struct shmem_link_device *shmd)
{
struct link_device *ld = &shmd->ld;
u32 head = get_rxq_head(shmd);
u32 tail = get_rxq_tail(shmd);
gif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n",
ld->name, "FMT", tail, head);
set_rxq_tail(shmd, head);
}
/**
* get_rxq_rcvd
* @shmd: pointer to an instance of shmem_link_device structure
* @mst: pointer to an instance of mem_status structure
* OUT @circ: pointer to an instance of circ_status structure
*
* Stores {start address of the buffer in a RXQ, size of the buffer, in & out
* pointer values, size of received data} into the 'circ' instance.
*
* Returns an error code.
*/
static inline int get_rxq_rcvd(struct shmem_link_device *shmd,
struct mem_status *mst, struct circ_status *circ)
{
struct link_device *ld = &shmd->ld;
circ->buff = get_rxq_buff(shmd);
circ->qsize = get_rxq_buff_size(shmd);
circ->in = mst->head[RX];
circ->out = mst->tail[RX];
circ->size = circ_get_usage(circ->qsize, circ->in, circ->out);
if (circ_valid(circ->qsize, circ->in, circ->out)) {
gif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
ld->name, "FMT", circ->qsize, circ->in,
circ->out, circ->size);
return 0;
} else {
gif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n",
ld->name, "FMT", circ->qsize, circ->in,
circ->out);
return -EIO;
}
}
/*
* shmem_purge_rxq
* @ld: pointer to an instance of the link_device structure
*
* Purges pending transfers from the RXQ.
*/
static inline void purge_rxq(struct link_device *ld)
{
skb_queue_purge(ld->skb_rxq);
}
/**
* get_txq_space
* @shmd: pointer to an instance of shmem_link_device structure
* OUT @circ: pointer to an instance of circ_status structure
*
* Stores {start address of the buffer in a TXQ, size of the buffer, in & out
* pointer values, size of free space} into the 'circ' instance.
*
* Returns the size of free space in the buffer or an error code.
*/
static inline int get_txq_space(struct shmem_link_device *shmd,
struct circ_status *circ)
{
struct link_device *ld = &shmd->ld;
int cnt = 0;
u32 qsize;
u32 head;
u32 tail;
int space;
while (1) {
qsize = get_txq_buff_size(shmd);
head = get_txq_head(shmd);
tail = get_txq_tail(shmd);
space = circ_get_space(qsize, head, tail);
gif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n",
ld->name, "FMT", qsize, head, tail, space);
if (circ_valid(qsize, head, tail))
break;
cnt++;
gif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d space:%d}, count %d\n",
ld->name, "FMT", qsize, head, tail,
space, cnt);
if (cnt >= MAX_RETRY_CNT) {
space = -EIO;
break;
}
udelay(100);
}
circ->buff = get_txq_buff(shmd);
circ->qsize = qsize;
circ->in = head;
circ->out = tail;
circ->size = space;
return space;
}
/**
* shmem_purge_txq
* @ld: pointer to an instance of the link_device structure
*
* Purges pending transfers from the TXQ.
*/
static inline void purge_txq(struct link_device *ld)
{
struct shmem_link_device *shmd = to_shmem_link_device(ld);
unsigned long flags;
spin_lock_irqsave(&shmd->tx_lock, flags);
skb_queue_purge(ld->skb_txq);
spin_unlock_irqrestore(&shmd->tx_lock, flags);
}
/**
* clear_shmem_map
* @shmd: pointer to an instance of shmem_link_device structure
*
* Clears all pointers in every circular queue.
*/
static inline void clear_shmem_map(struct shmem_link_device *shmd)
{
set_txq_head(shmd, 0);
set_txq_tail(shmd, 0);
set_rxq_head(shmd, 0);
set_rxq_tail(shmd, 0);
atomic_set(&shmd->res_required, 0);
memset(shmd->ipc_mem.vaddr, 0x0, shmd->ipc_mem.size);
}
/**
* reset_shmem_ipc
* @shmd: pointer to an instance of shmem_link_device structure
*
* Reset SHMEM with IPC map.
*/
static inline void reset_shmem_ipc(struct shmem_link_device *shmd)
{
clear_shmem_map(shmd);
atomic_set(&shmd->res_required, 0);
atomic_set(&shmd->ref_cnt, 0);
}
#endif