diff options
Diffstat (limited to 'drivers/misc/modem_if/modem_link_device_shmem.h')
-rw-r--r-- | drivers/misc/modem_if/modem_link_device_shmem.h | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/drivers/misc/modem_if/modem_link_device_shmem.h b/drivers/misc/modem_if/modem_link_device_shmem.h new file mode 100644 index 0000000..1f33c2a --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_shmem.h @@ -0,0 +1,700 @@ +/* + * 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 __MODEM_LINK_DEVICE_SHMEM_H__ +#define __MODEM_LINK_DEVICE_SHMEM_H__ + +#include "modem_utils.h" +#include "modem_link_device_memory.h" + +#define SHM_BOOT_MAGIC 0x424F4F54 +#define SHM_DUMP_MAGIC 0x44554D50 +#define SHM_IPC_MAGIC 0xAA +#define SHM_PM_MAGIC 0x5F + +#define SHM_4M_RESERVED_SZ 4056 +#define SHM_4M_FMT_TX_BUFF_SZ 4096 +#define SHM_4M_FMT_RX_BUFF_SZ 4096 +#define SHM_4M_RAW_TX_BUFF_SZ 2084864 +#define SHM_4M_RAW_RX_BUFF_SZ 2097152 + +struct shmem_4mb_phys_map { + u32 magic; + u32 access; + + u32 fmt_tx_head; + u32 fmt_tx_tail; + + u32 fmt_rx_head; + u32 fmt_rx_tail; + + u32 raw_tx_head; + u32 raw_tx_tail; + + u32 raw_rx_head; + u32 raw_rx_tail; + + u8 reserved[SHM_4M_RESERVED_SZ]; + + u8 fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ]; + u8 fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ]; + + u8 raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ]; +} __packed; + +struct shmem_circ { + u32 __iomem *head; + u32 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct shmem_ipc_device { + char name[16]; + int id; + + struct shmem_circ txq; + struct shmem_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; + + int req_ack_rcvd; +}; + +struct shmem_ipc_map { + u32 __iomem *magic; + u32 __iomem *access; + + struct shmem_ipc_device dev[MAX_SIPC5_DEV]; + + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; +}; + +struct shmem_link_device { + struct link_device ld; + + enum shmem_type type; + + /* SHMEM (SHARED MEMORY) address and size */ + u32 start; /* physical "start" address of SHMEM */ + u32 size; /* size of SHMEM */ + u8 __iomem *base; /* virtual address of the "start" */ + + /* SHMEM GPIO & IRQ */ + unsigned gpio_pda_active; + + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* IPC device map */ + struct shmem_ipc_map ipc_map; + + /* Pointers (aliases) to IPC device map */ + u32 __iomem *magic; + u32 __iomem *access; + struct shmem_ipc_device *dev[MAX_SIPC5_DEV]; + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; + + /* Wakelock for SHMEM device */ + struct wake_lock wlock; + char wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock ap_wlock; + char ap_wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock cp_wlock; + char cp_wlock_name[MIF_MAX_NAME_LEN]; + + /* for UDL */ + struct completion udl_cmpl; + struct std_dload_info dl_info; + + /* for CP crash dump */ + bool forced_cp_crash; + struct timer_list crash_ack_timer; + + /* for locking TX process */ + spinlock_t tx_lock[MAX_SIPC5_DEV]; + + /* for retransmission under SHMEM flow control after TXQ full state */ + atomic_t res_required[MAX_SIPC5_DEV]; + struct completion req_ack_cmpl[MAX_SIPC5_DEV]; + + /* for efficient RX process */ + struct tasklet_struct rx_tsk; + struct delayed_work ipc_rx_dwork; + struct delayed_work udl_rx_dwork; + struct io_device *iod[MAX_SIPC5_DEV]; + + /* for logging SHMEM status */ + struct mem_status_queue stat_list; + + /* for logging SHMEM dump */ + struct trace_data_queue trace_list; +#ifdef DEBUG_MODEM_IF + struct delayed_work dump_dwork; + char dump_path[MIF_MAX_PATH_LEN]; +#endif + + /* to hold/release "cp_wakeup" for PM (power-management) */ + struct delayed_work cp_sleep_dwork; + struct delayed_work link_off_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) + +#if 1 +#endif + +/** + * get_magic + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "magic code" field. + */ +static inline u32 get_magic(struct shmem_link_device *shmd) +{ + return ioread32(shmd->magic); +} + +/** + * get_access + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "access enable" field. + */ +static inline u32 get_access(struct shmem_link_device *shmd) +{ + return ioread32(shmd->access); +} + +/** + * set_magic + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "magic code" field + */ +static inline void set_magic(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->magic); +} + +/** + * set_access + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "access enable" field + */ +static inline void set_access(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->access); +} + +/** + * get_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.head); +} + +/** + * get_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 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 CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.tail); +} + +/** + * get_txq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.buff; +} + +/** + * get_txq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.size; +} + +/** + * get_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 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 CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.head); +} + +/** + * get_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.tail); +} + +/** + * get_rxq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.buff; +} + +/** + * get_rxq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.size; +} + +/** + * set_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->txq.head); +} + +/** + * set_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->txq.tail); +} + +/** + * set_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->rxq.head); +} + +/** + * set_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->rxq.tail); +} + +/** + * get_mask_req_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_req_ack; +} + +/** + * get_mask_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_res_ack; +} + +/** + * get_mask_send + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_send; +} + +#ifndef CONFIG_LINK_DEVICE_C2C +/** + * read_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the AP-to-CP interrupt register. + */ +static inline u16 read_int2cp(struct shmem_link_device *shmd) +{ + if (shmd->mbx2cp) + return ioread16(shmd->mbx2cp); + else + return 0; +} +#endif + +/** + * 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, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_txq_head(shmd, dev); + u32 tail = get_txq_tail(shmd, dev); + + mif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); + + set_txq_head(shmd, dev, 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, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_rxq_head(shmd, dev); + u32 tail = get_rxq_tail(shmd, dev); + + mif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); + + set_rxq_tail(shmd, dev, head); +} + +/** + * ipc_active + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns whether or not IPC via the shmem_link_device instance is possible. + */ +static bool ipc_active(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + u32 magic = get_magic(shmd); + u32 access = get_access(shmd); + + /* Check link mode */ + if (unlikely(ld->mode != LINK_MODE_IPC)) { + mif_err("%s: <by %pf> ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); + return false; + } + + /* Check "magic code" and "access enable" values */ + if (unlikely(magic != SHM_IPC_MAGIC || access != 1)) { + mif_err("%s: <by %pf> ERR! magic:0x%X access:%d\n", + ld->name, CALLER, magic, access); + return false; + } + + return true; +} + +/** + * get_rxq_rcvd + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @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 int get_rxq_rcvd(struct shmem_link_device *shmd, int dev, + struct mem_status *mst, struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + + circ->buff = get_rxq_buff(shmd, dev); + circ->qsize = get_rxq_buff_size(shmd, dev); + circ->in = mst->head[dev][RX]; + circ->out = mst->tail[dev][RX]; + circ->size = circ_get_usage(circ->qsize, circ->in, circ->out); + + if (circ_valid(circ->qsize, circ->in, circ->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out, circ->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out); + return -EIO; + } +} + +/** + * get_txq_space + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * 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 int get_txq_space(struct shmem_link_device *shmd, int dev, + 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, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + space = circ_get_space(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u space:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = space; + + return space; +} + +/** + * get_txq_saved + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @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 TXQ, size of the buffer, in & out + * pointer values, size of stored data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_txq_saved(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int saved; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + saved = circ_get_usage(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u saved:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, saved); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "saved:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + saved, cnt); + if (cnt >= MAX_RETRY_CNT) { + saved = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = saved; + + return saved; +} + +/** + * clear_shmem_map + * @shmd: pointer to an instance of shmem_link_device structure + * + * Clears all pointers in every circular queue. + */ +static void clear_shmem_map(struct shmem_link_device *shmd) +{ + set_txq_head(shmd, IPC_FMT, 0); + set_txq_tail(shmd, IPC_FMT, 0); + set_rxq_head(shmd, IPC_FMT, 0); + set_rxq_tail(shmd, IPC_FMT, 0); + + set_txq_head(shmd, IPC_RAW, 0); + set_txq_tail(shmd, IPC_RAW, 0); + set_rxq_head(shmd, IPC_RAW, 0); + set_rxq_tail(shmd, IPC_RAW, 0); +} + +/** + * reset_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Reset SHMEM with IPC map. + */ +static void reset_shmem_ipc(struct shmem_link_device *shmd) +{ + set_access(shmd, 0); + + clear_shmem_map(shmd); + + atomic_set(&shmd->res_required[IPC_FMT], 0); + atomic_set(&shmd->res_required[IPC_RAW], 0); + + atomic_set(&shmd->ref_cnt, 0); + + set_magic(shmd, SHM_IPC_MAGIC); + set_access(shmd, 1); +} + +/** + * init_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Initializes IPC via SHMEM. + */ +static int init_shmem_ipc(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode == LINK_MODE_IPC && + get_magic(shmd) == SHM_IPC_MAGIC && + get_access(shmd) == 1) { + mif_err("%s: IPC already initialized\n", ld->name); + return 0; + } + + /* Initialize variables for efficient TX/RX processing */ + shmd->iod[IPC_FMT] = link_get_iod_with_format(ld, IPC_FMT); + shmd->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + reset_shmem_ipc(shmd); + + if (get_magic(shmd) != SHM_IPC_MAGIC || get_access(shmd) != 1) + return -EACCES; + + return 0; +} + +#endif + |