aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if/modem_link_device_memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if/modem_link_device_memory.c')
-rw-r--r--drivers/misc/modem_if/modem_link_device_memory.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/drivers/misc/modem_if/modem_link_device_memory.c b/drivers/misc/modem_if/modem_link_device_memory.c
new file mode 100644
index 0000000..9e49e50
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_memory.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2011 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.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/kallsyms.h>
+#include <linux/suspend.h>
+#include <plat/gpio-cfg.h>
+#include <mach/gpio.h>
+
+#include "modem.h"
+#include "modem_prj.h"
+#include "modem_utils.h"
+#include "modem_link_device_memory.h"
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+#include "modem_link_device_dpram.h"
+#endif
+
+/**
+ * msq_get_free_slot
+ * @trq : pointer to an instance of mem_status_queue structure
+ *
+ * Succeeds always by dropping the oldest slot if a "msq" is full.
+ */
+struct mem_status *msq_get_free_slot(struct mem_status_queue *msq)
+{
+ int qsize = MAX_MEM_LOG_CNT;
+ int in;
+ int out;
+ unsigned long flags;
+ struct mem_status *stat;
+
+ spin_lock_irqsave(&msq->lock, flags);
+
+ in = msq->in;
+ out = msq->out;
+
+ if (circ_get_space(qsize, in, out) < 1) {
+ /* Make the oldest slot empty */
+ out++;
+ msq->out = (out == qsize) ? 0 : out;
+ }
+
+ /* Get a free slot */
+ stat = &msq->stat[in];
+
+ /* Make it as "data" slot */
+ in++;
+ msq->in = (in == qsize) ? 0 : in;
+
+ spin_unlock_irqrestore(&msq->lock, flags);
+
+ memset(stat, 0, sizeof(struct mem_status));
+
+ return stat;
+}
+
+struct mem_status *msq_get_data_slot(struct mem_status_queue *msq)
+{
+ int qsize = MAX_MEM_LOG_CNT;
+ int in;
+ int out;
+ unsigned long flags;
+ struct mem_status *stat;
+
+ spin_lock_irqsave(&msq->lock, flags);
+
+ in = msq->in;
+ out = msq->out;
+
+ if (in == out) {
+ stat = NULL;
+ goto exit;
+ }
+
+ /* Get a data slot */
+ stat = &msq->stat[out];
+
+ /* Make it "free" slot */
+ out++;
+ msq->out = (out == qsize) ? 0 : out;
+
+exit:
+ spin_unlock_irqrestore(&msq->lock, flags);
+ return stat;
+}
+
+/**
+ * memcpy16_from_io
+ * @to: pointer to "real" memory
+ * @from: pointer to IO memory
+ * @count: data length in bytes to be copied
+ *
+ * Copies data from IO memory space to "real" memory space.
+ */
+void memcpy16_from_io(const void *to, const void __iomem *from, u32 count)
+{
+ u16 *d = (u16 *)to;
+ u16 *s = (u16 *)from;
+ u32 words = count >> 1;
+ while (words--)
+ *d++ = ioread16(s++);
+}
+
+/**
+ * memcpy16_to_io
+ * @to: pointer to IO memory
+ * @from: pointer to "real" memory
+ * @count: data length in bytes to be copied
+ *
+ * Copies data from "real" memory space to IO memory space.
+ */
+void memcpy16_to_io(const void __iomem *to, const void *from, u32 count)
+{
+ u16 *d = (u16 *)to;
+ u16 *s = (u16 *)from;
+ u32 words = count >> 1;
+ while (words--)
+ iowrite16(*s++, d++);
+}
+
+/**
+ * memcmp16_to_io
+ * @to: pointer to IO memory
+ * @from: pointer to "real" memory
+ * @count: data length in bytes to be compared
+ *
+ * Compares data from "real" memory space to IO memory space.
+ */
+int memcmp16_to_io(const void __iomem *to, const void *from, u32 count)
+{
+ u16 *d = (u16 *)to;
+ u16 *s = (u16 *)from;
+ int words = count >> 1;
+ int diff = 0;
+ int i;
+ u16 d1;
+ u16 s1;
+
+ for (i = 0; i < words; i++) {
+ d1 = ioread16(d);
+ s1 = *s;
+ if (d1 != s1) {
+ diff++;
+ mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1);
+ }
+ d++;
+ s++;
+ }
+
+ return diff;
+}
+
+/**
+ * circ_read16_from_io
+ * @dst: start address of the destination buffer
+ * @src: start address of the buffer in a circular queue
+ * @qsize: size of the circular queue
+ * @out: offset to read
+ * @len: length of data to be read
+ *
+ * Should be invoked after checking data length
+ */
+void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len)
+{
+ if ((out + len) <= qsize) {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+ memcpy16_from_io(dst, (src + out), len);
+ } else {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ unsigned len1 = qsize - out;
+
+ /* 1) data start (out) ~ buffer end */
+ memcpy16_from_io(dst, (src + out), len1);
+
+ /* 2) buffer start ~ data end (in - 1) */
+ memcpy16_from_io((dst + len1), src, (len - len1));
+ }
+}
+
+/**
+ * circ_write16_to_io
+ * @dst: pointer to the start of the circular queue
+ * @src: pointer to the source
+ * @qsize: size of the circular queue
+ * @in: offset to write
+ * @len: length of data to be written
+ *
+ * Should be invoked after checking free space
+ */
+void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len)
+{
+ u32 space;
+
+ if ((in + len) < qsize) {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ memcpy16_to_io((dst + in), src, len);
+ } else {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+
+ /* 1) space start (in) ~ buffer end */
+ space = qsize - in;
+ memcpy16_to_io((dst + in), src, ((len > space) ? space : len));
+
+ /* 2) buffer start ~ data end */
+ if (len > space)
+ memcpy16_to_io(dst, (src + space), (len - space));
+ }
+}
+
+/**
+ * copy_circ_to_user
+ * @dst: start address of the destination buffer
+ * @src: start address of the buffer in a circular queue
+ * @qsize: size of the circular queue
+ * @out: offset to read
+ * @len: length of data to be read
+ *
+ * Should be invoked after checking data length
+ */
+int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len)
+{
+ if ((out + len) <= qsize) {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+ if (copy_to_user(dst, (src + out), len)) {
+ mif_err("ERR! <called by %pf> copy_to_user fail\n",
+ CALLER);
+ return -EFAULT;
+ }
+ } else {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ unsigned len1 = qsize - out;
+
+ /* 1) data start (out) ~ buffer end */
+ if (copy_to_user(dst, (src + out), len1)) {
+ mif_err("ERR! <called by %pf> copy_to_user fail\n",
+ CALLER);
+ return -EFAULT;
+ }
+
+ /* 2) buffer start ~ data end (in?) */
+ if (copy_to_user((dst + len1), src, (len - len1))) {
+ mif_err("ERR! <called by %pf> copy_to_user fail\n",
+ CALLER);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * copy_user_to_circ
+ * @dst: pointer to the start of the circular queue
+ * @src: pointer to the source
+ * @qsize: size of the circular queue
+ * @in: offset to write
+ * @len: length of data to be written
+ *
+ * Should be invoked after checking free space
+ */
+int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len)
+{
+ u32 space;
+ u32 len1;
+
+ if ((in + len) < qsize) {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ if (copy_from_user((dst + in), src, len)) {
+ mif_err("ERR! <called by %pf> copy_from_user fail\n",
+ CALLER);
+ return -EFAULT;
+ }
+ } else {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+
+ /* 1) space start (in) ~ buffer end */
+ space = qsize - in;
+ len1 = (len > space) ? space : len;
+ if (copy_from_user((dst + in), src, len1)) {
+ mif_err("ERR! <called by %pf> copy_from_user fail\n",
+ CALLER);
+ return -EFAULT;
+ }
+
+ /* 2) buffer start ~ data end */
+ if (len > len1) {
+ if (copy_from_user(dst, (src + space), (len - len1))) {
+ mif_err("ERR! <called by %pf> copy_from_user "
+ "fail\n", CALLER);
+ return -EFAULT;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * print_mem_status
+ * @ld: pointer to an instance of link_device structure
+ * @mst: pointer to an instance of mem_status structure
+ *
+ * Prints a snapshot of the status of a SHM.
+ */
+void print_mem_status(struct link_device *ld, struct mem_status *mst)
+{
+ struct utc_time utc;
+ int us = ns2us(mst->ts.tv_nsec);
+
+ ts2utc(&mst->ts, &utc);
+ pr_info("%s: %s: [%02d:%02d:%02d.%06d] "
+ "[%s] ACC{%X %d} "
+ "FMT{TI:%u TO:%u RI:%u RO:%u} "
+ "RAW{TI:%u TO:%u RI:%u RO:%u} "
+ "INTR{RX:0x%X TX:0x%X}\n",
+ MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us,
+ get_dir_str(mst->dir), mst->magic, mst->access,
+ mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX],
+ mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX],
+ mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX],
+ mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX],
+ mst->int2ap, mst->int2cp);
+}
+
+/**
+ * print_circ_status
+ * @ld: pointer to an instance of link_device structure
+ * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
+ * @mst: pointer to an instance of mem_status structure
+ *
+ * Prints a snapshot of the status of a memory
+ */
+void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst)
+{
+ struct utc_time utc;
+ int us = ns2us(mst->ts.tv_nsec);
+
+ if (dev > IPC_RAW)
+ return;
+
+ ts2utc(&mst->ts, &utc);
+ pr_info("%s: %s: [%02d:%02d:%02d.%06d] "
+ "[%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u}\n",
+ MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us,
+ get_dir_str(mst->dir), get_dev_name(dev),
+ mst->head[dev][TX], mst->tail[dev][TX],
+ mst->head[dev][RX], mst->tail[dev][RX]);
+}
+
+/**
+ * print_ipc_trace
+ * @ld: pointer to an instance of link_device structure
+ * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
+ * @stat: pointer to an instance of circ_status structure
+ * @ts: pointer to an instance of timespec structure
+ * @buff: start address of a buffer into which RX IPC messages were copied
+ * @rcvd: size of data in the buffer
+ *
+ * Prints IPC messages in a local memory buffer to a kernel log.
+ */
+void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat,
+ struct timespec *ts, u8 *buff, u32 rcvd)
+{
+ struct utc_time utc;
+
+ ts2utc(ts, &utc);
+
+ pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] "
+ "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n",
+ MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec,
+ utc.msec, ld->name, get_dev_name(dev), stat->in, stat->out,
+ stat->size);
+
+ mif_print_dump(buff, rcvd, 4);
+}
+
+/**
+ * capture_mem_dump
+ * @ld: pointer to an instance of link_device structure
+ * @base: base virtual address to a memory interface medium
+ * @size: size of the memory interface medium
+ *
+ * Captures a dump for a memory interface medium.
+ *
+ * Returns the pointer to a memory dump buffer.
+ */
+u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size)
+{
+ u8 *buff = kzalloc(size, GFP_ATOMIC);
+ if (!buff) {
+ mif_err("%s: ERR! kzalloc(%d) fail\n", ld->name, size);
+ return NULL;
+ } else {
+ memcpy16_from_io(buff, base, size);
+ return buff;
+ }
+}
+
+/**
+ * trq_get_free_slot
+ * @trq : pointer to an instance of trace_data_queue structure
+ *
+ * Succeeds always by dropping the oldest slot if a "trq" is full.
+ */
+struct trace_data *trq_get_free_slot(struct trace_data_queue *trq)
+{
+ int qsize = MAX_TRACE_SIZE;
+ int in;
+ int out;
+ unsigned long flags;
+ struct trace_data *trd;
+
+ spin_lock_irqsave(&trq->lock, flags);
+
+ in = trq->in;
+ out = trq->out;
+
+ /* The oldest slot can be dropped. */
+ if (circ_get_space(qsize, in, out) < 1) {
+ /* Free the data buffer in the oldest slot */
+ trd = &trq->trd[out];
+ kfree(trd->data);
+
+ /* Make the oldest slot empty */
+ out++;
+ trq->out = (out == qsize) ? 0 : out;
+ }
+
+ /* Get a free slot and make it occupied */
+ trd = &trq->trd[in++];
+ trq->in = (in == qsize) ? 0 : in;
+
+ spin_unlock_irqrestore(&trq->lock, flags);
+
+ memset(trd, 0, sizeof(struct trace_data));
+
+ return trd;
+}
+
+struct trace_data *trq_get_data_slot(struct trace_data_queue *trq)
+{
+ int qsize = MAX_TRACE_SIZE;
+ int in;
+ int out;
+ unsigned long flags;
+ struct trace_data *trd;
+
+ spin_lock_irqsave(&trq->lock, flags);
+
+ in = trq->in;
+ out = trq->out;
+
+ if (circ_get_usage(qsize, in, out) < 1) {
+ spin_unlock_irqrestore(&trq->lock, flags);
+ return NULL;
+ }
+
+ /* Get a data slot and make it empty */
+ trd = &trq->trd[out++];
+ trq->out = (out == qsize) ? 0 : out;
+
+ spin_unlock_irqrestore(&trq->lock, flags);
+
+ return trd;
+}
+