aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if_u1/modem_link_device_dpram.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if_u1/modem_link_device_dpram.c')
-rw-r--r--drivers/misc/modem_if_u1/modem_link_device_dpram.c2657
1 files changed, 0 insertions, 2657 deletions
diff --git a/drivers/misc/modem_if_u1/modem_link_device_dpram.c b/drivers/misc/modem_if_u1/modem_link_device_dpram.c
deleted file mode 100644
index 8b99669..0000000
--- a/drivers/misc/modem_if_u1/modem_link_device_dpram.c
+++ /dev/null
@@ -1,2657 +0,0 @@
-/*
- * 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.
- *
- */
-
-#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/platform_data/modem.h>
-
-#include "modem_prj.h"
-#include "modem_link_device_dpram.h"
-#include "modem_utils.h"
-
-static void handle_cp_crash(struct dpram_link_device *dpld);
-static void trigger_force_cp_crash(struct dpram_link_device *dpld);
-
-/**
- * set_circ_pointer
- * @dpld: pointer to an instance of dpram_link_device structure
- * @id: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @dir: direction of communication (TX or RX)
- * @ptr: type of the queue pointer (HEAD or TAIL)
- * @addr: address of the queue pointer
- * @val: value to be written to the queue pointer
- *
- * Writes a value to a pointer in a circular queue with verification
- */
-static inline void set_circ_pointer(struct dpram_link_device *dpld, int id,
- int dir, int ptr, void __iomem *addr, u16 val)
-{
- struct link_device *ld = &dpld->ld;
- int cnt = 0;
- u16 saved = 0;
-
- iowrite16(val, addr);
-
- while (1) {
- /* Check the value written to the address */
- saved = ioread16(addr);
- if (likely(saved == val))
- break;
-
- cnt++;
- mif_err("%s: ERR! %s_%s.%s saved(%d) != val(%d), count %d\n",
- ld->name, get_dev_name(id), circ_dir(dir),
- circ_ptr(ptr), saved, val, cnt);
- if (cnt >= MAX_RETRY_CNT) {
- trigger_force_cp_crash(dpld);
- break;
- }
-
- udelay(100);
-
- /* Write the value again */
- iowrite16(val, addr);
- }
-}
-
-/**
- * register_isr
- * @irq: IRQ number for a DPRAM interrupt
- * @isr: function pointer to an interrupt service routine
- * @flags: set of interrupt flags
- * @name: name of the interrupt
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Registers the ISR for the IRQ number
- */
-static int register_isr(unsigned int irq, irqreturn_t (*isr)(int, void*),
- unsigned long flags, const char *name,
- struct dpram_link_device *dpld)
-{
- int ret;
-
- ret = request_irq(irq, isr, flags, name, dpld);
- if (ret) {
- mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret);
- return ret;
- }
-
- ret = enable_irq_wake(irq);
- if (ret)
- mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret);
-
- mif_info("%s (#%d) handler registered\n", name, irq);
-
- return 0;
-}
-
-/**
- * clear_intr
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Clears the CP-to-AP interrupt register in a DPRAM
- */
-static inline void clear_intr(struct dpram_link_device *dpld)
-{
- if (likely(dpld->need_intr_clear))
- dpld->ext_op->clear_intr(dpld);
-}
-
-/**
- * recv_intr
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns the value of the CP-to-AP interrupt register in a DPRAM
- */
-static inline u16 recv_intr(struct dpram_link_device *dpld)
-{
- return ioread16(dpld->mbx2ap);
-}
-
-/**
- * send_intr
- * @dpld: pointer to an instance of dpram_link_device structure
- * @mask: value to be written to the AP-to-CP interrupt register in a DPRAM
- */
-static inline void send_intr(struct dpram_link_device *dpld, u16 mask)
-{
- iowrite16(mask, dpld->mbx2cp);
-}
-
-/**
- * get_magic
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns the value of the "magic code" field in a DPRAM
- */
-static inline u16 get_magic(struct dpram_link_device *dpld)
-{
- return ioread16(dpld->magic);
-}
-
-/**
- * set_magic
- * @dpld: pointer to an instance of dpram_link_device structure
- * @val: value to be written to the "magic code" field in a DPRAM
- */
-static inline void set_magic(struct dpram_link_device *dpld, u16 val)
-{
- iowrite16(val, dpld->magic);
-}
-
-/**
- * get_access
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns the value of the "access enable" field in a DPRAM
- */
-static inline u16 get_access(struct dpram_link_device *dpld)
-{
- return ioread16(dpld->access);
-}
-
-/**
- * set_access
- * @dpld: pointer to an instance of dpram_link_device structure
- * @val: value to be written to the "access enable" field in a DPRAM
- */
-static inline void set_access(struct dpram_link_device *dpld, u16 val)
-{
- iowrite16(val, dpld->access);
-}
-
-/**
- * get_tx_head
- * @dpld: pointer to an instance of dpram_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_tx_head(struct dpram_link_device *dpld, int id)
-{
- return ioread16(dpld->dev[id]->txq.head);
-}
-
-/**
- * get_tx_tail
- * @dpld: pointer to an instance of dpram_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_tx_tail(struct dpram_link_device *dpld, int id)
-{
- return ioread16(dpld->dev[id]->txq.tail);
-}
-
-/**
- * set_tx_head
- * @dpld: pointer to an instance of dpram_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_tx_head(struct dpram_link_device *dpld, int id, u32 in)
-{
- set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->txq.head, in);
-}
-
-/**
- * set_tx_tail
- * @dpld: pointer to an instance of dpram_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_tx_tail(struct dpram_link_device *dpld, int id, u32 out)
-{
- set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->txq.tail, out);
-}
-
-/**
- * get_tx_buff
- * @dpld: pointer to an instance of dpram_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_tx_buff(struct dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->txq.buff;
-}
-
-/**
- * get_tx_buff_size
- * @dpld: pointer to an instance of dpram_link_device structure
- * @id: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * Returns the size of the buffer in a TXQ
- */
-static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->txq.size;
-}
-
-/**
- * get_rx_head
- * @dpld: pointer to an instance of dpram_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_rx_head(struct dpram_link_device *dpld, int id)
-{
- return ioread16(dpld->dev[id]->rxq.head);
-}
-
-/**
- * get_rx_tail
- * @dpld: pointer to an instance of dpram_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_rx_tail(struct dpram_link_device *dpld, int id)
-{
- return ioread16(dpld->dev[id]->rxq.tail);
-}
-
-/**
- * set_rx_head
- * @dpld: pointer to an instance of dpram_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_rx_head(struct dpram_link_device *dpld, int id, u32 in)
-{
- set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->rxq.head, in);
-}
-
-/**
- * set_rx_tail
- * @dpld: pointer to an instance of dpram_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_rx_tail(struct dpram_link_device *dpld, int id, u32 out)
-{
- set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->rxq.tail, out);
-}
-
-/**
- * get_rx_buff
- * @dpld: pointer to an instance of dpram_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_rx_buff(struct dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->rxq.buff;
-}
-
-/**
- * get_rx_buff_size
- * @dpld: pointer to an instance of dpram_link_device structure
- * @id: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * Returns the size of the buffer in an RXQ
- */
-static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->rxq.size;
-}
-
-/**
- * get_mask_req_ack
- * @dpld: pointer to an instance of dpram_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 dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->mask_req_ack;
-}
-
-/**
- * get_mask_res_ack
- * @dpld: pointer to an instance of dpram_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 dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->mask_res_ack;
-}
-
-/**
- * get_mask_send
- * @dpld: pointer to an instance of dpram_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 dpram_link_device *dpld, int id)
-{
- return dpld->dev[id]->mask_send;
-}
-
-/**
- * set_dpram_map
- * @dpld: pointer to an instance of dpram_link_device structure
- * @map: pointer to an instance of mif_irq_map structure
- *
- * Sets variables in an mif_irq_map instance as current DPRAM status for IPC
- * logging.
- */
-static void set_dpram_map(struct dpram_link_device *dpld,
- struct mif_irq_map *map)
-{
- map->magic = get_magic(dpld);
- map->access = get_access(dpld);
-
- map->fmt_tx_in = get_tx_head(dpld, IPC_FMT);
- map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT);
- map->fmt_rx_in = get_rx_head(dpld, IPC_FMT);
- map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT);
- map->raw_tx_in = get_tx_head(dpld, IPC_RAW);
- map->raw_tx_out = get_tx_tail(dpld, IPC_RAW);
- map->raw_rx_in = get_rx_head(dpld, IPC_RAW);
- map->raw_rx_out = get_rx_tail(dpld, IPC_RAW);
-
- map->cp2ap = recv_intr(dpld);
-}
-
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
-/**
- * log_dpram_status
- * @dpld: pointer to an instance of dpram_link_device structure
- * @str: pointer to a string that will be printed with a DPRAM status log
- *
- * Prints current DPRAM status with a string to a kernel log.
- */
-static inline void log_dpram_status(struct dpram_link_device *dpld, char *str)
-{
- struct utc_time utc;
-
- get_utc_time(&utc);
-
- pr_info("%s: %s: %s: [%02d:%02d:%02d.%03d] "
- "ACC{%X %d} FMT{TI:%u TO:%u RI:%u RO:%u} "
- "RAW{TI:%u TO:%u RI:%u RO:%u} INTR{0x%X}\n",
- MIF_TAG, dpld->ld.mc->name, str,
- utc.hour, utc.min, utc.sec, utc.msec,
- get_magic(dpld), get_access(dpld),
- get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT),
- get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT),
- get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW),
- get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW),
- recv_intr(dpld));
-}
-
-/**
- * save_dpram_dump_work
- * @work: pointer to an instance of work_struct structure
- *
- * Performs actual file operation for saving a DPRAM dump.
- */
-static void save_dpram_dump_work(struct work_struct *work)
-{
- struct dpram_link_device *dpld;
- struct link_device *ld;
- struct trace_queue *trq;
- struct trace_data *trd;
- struct file *fp;
- struct timespec *ts;
- u8 *dump;
- int rcvd;
- char *path;
- struct utc_time utc;
-
- dpld = container_of(work, struct dpram_link_device, dump_dwork.work);
- ld = &dpld->ld;
- trq = &dpld->dump_list;
- path = dpld->dump_path;
-
- while (1) {
- trd = trq_get_data_slot(trq);
- if (!trd)
- break;
-
- ts = &trd->ts;
- dump = trd->data;
- rcvd = trd->size;
-
- ts2utc(ts, &utc);
- snprintf(path, MIF_MAX_PATH_LEN,
- "%s/%s_dump_%d%02d%02d-%02d%02d%02d",
- MIF_LOG_DIR, ld->name, utc.year, utc.mon, utc.day,
- utc.hour, utc.min, utc.sec);
-
- fp = mif_open_file(path);
- if (fp) {
- mif_save_file(fp, dump, rcvd);
- mif_close_file(fp);
- } else {
- mif_err("%s: ERR! %s open fail\n", ld->name, path);
- mif_print_dump(dump, rcvd, 16);
- }
-
- kfree(dump);
- }
-}
-
-/**
- * save_dpram_dump
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Saves a current DPRAM dump.
- *
- * Actual file operation (save) will be performed by save_dpram_dump_work() that
- * is invoked by a delayed work.
- */
-static void save_dpram_dump(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct trace_data *trd;
- u8 *buff;
- struct timespec ts;
-
- buff = kzalloc(dpld->size, GFP_ATOMIC);
- if (!buff) {
- mif_err("%s: ERR! kzalloc fail\n", ld->name);
- return;
- }
-
- getnstimeofday(&ts);
-
- memcpy(buff, dpld->base, dpld->size);
-
- trd = trq_get_free_slot(&dpld->dump_list);
- if (!trd) {
- mif_err("%s: ERR! trq_get_free_slot fail\n", ld->name);
- mif_print_dump(buff, dpld->size, 16);
- kfree(buff);
- return;
- }
-
- memcpy(&trd->ts, &ts, sizeof(struct timespec));
- trd->data = buff;
- trd->size = dpld->size;
-
- queue_delayed_work(system_nrt_wq, &dpld->dump_dwork, 0);
-}
-
-/**
- * pr_trace
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @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.
- */
-static void pr_trace(struct dpram_link_device *dpld, int dev,
- struct timespec *ts, u8 *buff, u32 rcvd)
-{
- struct link_device *ld = &dpld->ld;
- struct utc_time utc;
-
- ts2utc(ts, &utc);
-
- pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] %s trace (%s)\n",
- MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec,
- utc.msec, get_dev_name(dev), ld->name);
-
- mif_print_dump(buff, rcvd, 4);
-
- return;
-}
-
-/**
- * print_ipc_trace
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @stat: pointer to an instance of dpram_circ_status structure
- *
- * Prints IPC messages currently in an RX circular queue to a kernel log.
- */
-static void print_ipc_trace(struct dpram_link_device *dpld, int dev,
- struct dpram_circ_status *stat)
-{
- u8 *buff = dpld->buff;
- struct timespec ts;
-
- getnstimeofday(&ts);
-
- /* Copy IPC messages from a DPRAM RXQ to a local buffer */
- memset(buff, 0, dpld->size);
- circ_read(buff, stat->buff, stat->qsize, stat->out, stat->size);
-
- /* Print IPC messages in the local buffer to a kernel log */
- pr_trace(dpld, dev, &ts, buff, stat->size);
-}
-
-/**
- * save_ipc_trace_work
- * @work: pointer to an instance of work_struct structure
- *
- * Performs actual file operation for saving RX IPC trace.
- */
-static void save_ipc_trace_work(struct work_struct *work)
-{
- struct dpram_link_device *dpld;
- struct link_device *ld;
- struct trace_queue *trq;
- struct trace_data *trd;
- struct file *fp;
- struct timespec *ts;
- int dev;
- u8 *dump;
- int rcvd;
- u8 *buff;
- char *path;
- struct utc_time utc;
-
- dpld = container_of(work, struct dpram_link_device, trace_dwork.work);
- ld = &dpld->ld;
- trq = &dpld->trace_list;
- path = dpld->trace_path;
-
- buff = kzalloc(dpld->size << 3, GFP_KERNEL);
- if (!buff) {
- while (1) {
- trd = trq_get_data_slot(trq);
- if (!trd)
- break;
-
- ts = &trd->ts;
- dev = trd->dev;
- dump = trd->data;
- rcvd = trd->size;
- pr_trace(dpld, dev, ts, dump, rcvd);
-
- kfree(dump);
- }
- return;
- }
-
- while (1) {
- trd = trq_get_data_slot(trq);
- if (!trd)
- break;
-
- ts = &trd->ts;
- dev = trd->dev;
- dump = trd->data;
- rcvd = trd->size;
-
- ts2utc(ts, &utc);
- snprintf(path, MIF_MAX_PATH_LEN,
- "%s/%s_%s_%d%02d%02d-%02d%02d%02d",
- MIF_LOG_DIR, ld->name, get_dev_name(dev),
- utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec);
-
- fp = mif_open_file(path);
- if (fp) {
- int len;
-
- snprintf(buff, MIF_MAX_PATH_LEN,
- "[%d-%02d-%02d %02d:%02d:%02d.%03d]\n",
- utc.year, utc.mon, utc.day, utc.hour, utc.min,
- utc.sec, utc.msec);
- len = strlen(buff);
- mif_dump2format4(dump, rcvd, (buff + len), NULL);
- strcat(buff, "\n");
- len = strlen(buff);
-
- mif_save_file(fp, buff, len);
-
- memset(buff, 0, len);
- mif_close_file(fp);
- } else {
- mif_err("%s: %s open fail\n", ld->name, path);
- pr_trace(dpld, dev, ts, dump, rcvd);
- }
-
- kfree(dump);
- }
-
- kfree(buff);
-}
-
-/**
- * save_ipc_trace
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @stat: pointer to an instance of dpram_circ_status structure
- *
- * Saves IPC messages currently in an RX circular queue.
- *
- * Actual file operation (save) will be performed by save_ipc_trace_work() that
- * is invoked by a delayed work.
- */
-static void save_ipc_trace(struct dpram_link_device *dpld, int dev,
- struct dpram_circ_status *stat)
-{
- struct link_device *ld = &dpld->ld;
- struct trace_data *trd;
- u8 *buff;
- struct timespec ts;
-
- buff = kzalloc(stat->size, GFP_ATOMIC);
- if (!buff) {
- mif_err("%s: %s: ERR! kzalloc fail\n",
- ld->name, get_dev_name(dev));
- print_ipc_trace(dpld, dev, stat);
- return;
- }
-
- getnstimeofday(&ts);
-
- circ_read(buff, stat->buff, stat->qsize, stat->out, stat->size);
-
- trd = trq_get_free_slot(&dpld->trace_list);
- if (!trd) {
- mif_err("%s: %s: ERR! trq_get_free_slot fail\n",
- ld->name, get_dev_name(dev));
- pr_trace(dpld, dev, &ts, buff, stat->size);
- kfree(buff);
- return;
- }
-
- memcpy(&trd->ts, &ts, sizeof(struct timespec));
- trd->dev = dev;
- trd->data = buff;
- trd->size = stat->size;
-
- queue_delayed_work(system_nrt_wq, &dpld->trace_dwork, 0);
-}
-#endif
-
-/**
- * check_magic_access
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns 0 if the "magic code" and "access enable" values are valid, otherwise
- * returns -EACCES.
- */
-static int check_magic_access(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- int i;
- u16 magic = get_magic(dpld);
- u16 access = get_access(dpld);
-
- /* Returns 0 if the "magic code" and "access enable" are valid */
- if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
- return 0;
-
- /* Retry up to 100 times with 100 us delay per each retry */
- for (i = 1; i <= 100; i++) {
- mif_info("%s: magic:%X access:%X -> retry:%d\n",
- ld->name, magic, access, i);
- udelay(100);
-
- magic = get_magic(dpld);
- access = get_access(dpld);
- if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
- return 0;
- }
-
- mif_info("%s: !CRISIS! magic:%X access:%X\n", ld->name, magic, access);
- return -EACCES;
-}
-
-/**
- * ipc_active
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns whether or not IPC via the dpram_link_device instance is possible.
- */
-static bool ipc_active(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
-
- /* Check DPRAM mode */
- if (ld->mode != LINK_MODE_IPC) {
- mif_err("%s: ERR! <%pf> ld->mode != LINK_MODE_IPC\n",
- ld->name, __builtin_return_address(0));
- return false;
- }
-
- /* Check "magic code" and "access enable" values */
- if (check_magic_access(dpld) < 0) {
- mif_err("%s: ERR! <%pf> check_magic_access fail\n",
- ld->name, __builtin_return_address(0));
- return false;
- }
-
- return true;
-}
-
-/**
- * dpram_can_sleep
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Returns tha value of the "need_wake_up" variable in a dpram_link_device
- * instance that is set in dpram_create_link_device().
- */
-static inline bool dpram_can_sleep(struct dpram_link_device *dpld)
-{
- return dpld->need_wake_up;
-}
-
-/**
- * dpram_wake_up
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Wakes up a DPRAM if it can sleep and increases the "accessing" counter in the
- * dpram_link_device instance.
- *
- * CAUTION!!! dpram_allow_sleep() MUST be invoked after dpram_wake_up() success
- * to decrease the "accessing" counter.
- */
-static int dpram_wake_up(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
-
- if (unlikely(!dpram_can_sleep(dpld)))
- return 0;
-
- if (dpld->ext_op->wakeup(dpld) < 0) {
- mif_err("%s: ERR! <%pf> wakeup fail, once\n",
- ld->name, __builtin_return_address(0));
-
- dpld->ext_op->sleep(dpld);
-
- udelay(10);
-
- if (dpld->ext_op->wakeup(dpld) < 0) {
- mif_err("%s: ERR! <%pf> wakeup fail, twice\n",
- ld->name, __builtin_return_address(0));
- return -EACCES;
- }
- }
-
- atomic_inc(&dpld->accessing);
- return 0;
-}
-
-/**
- * dpram_allow_sleep
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Decreases the "accessing" counter in the dpram_link_device instance if it can
- * sleep and allows the DPRAM to sleep only if the value of "accessing" counter
- * is less than or equal to 0.
- *
- * MUST be invoked after dpram_wake_up() success to decrease the "accessing"
- * counter.
- */
-static void dpram_allow_sleep(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
-
- if (unlikely(!dpram_can_sleep(dpld)))
- return;
-
- if (atomic_dec_return(&dpld->accessing) <= 0) {
- dpld->ext_op->sleep(dpld);
- atomic_set(&dpld->accessing, 0);
- mif_debug("%s: DPRAM sleep possible\n", ld->name);
- }
-}
-
-/**
- * handle_no_cp_crash_ack
- * @arg: pointer to an instance of dpram_link_device structure
- *
- * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no
- * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT.
- */
-static void handle_no_cp_crash_ack(unsigned long arg)
-{
- struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
- struct link_device *ld = &dpld->ld;
-
- mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name);
-
- if (!wake_lock_active(&dpld->wlock))
- wake_lock(&dpld->wlock);
-
- handle_cp_crash(dpld);
-}
-
-/**
- * trigger_force_cp_crash
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Triggers an enforced CP crash.
- */
-static void trigger_force_cp_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
-
- if (ld->mode == LINK_MODE_ULOAD) {
- mif_err("%s: CP crash is already in progress\n", ld->mc->name);
- return;
- }
-
- disable_irq_nosync(dpld->irq);
-
- ld->mode = LINK_MODE_ULOAD;
- mif_info("%s: called by %pf\n", ld->name, __builtin_return_address(0));
-
- dpram_wake_up(dpld);
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- /* Take a DPRAM dump */
- save_dpram_dump(dpld);
-#endif
-
- enable_irq(dpld->irq);
-
- /* Send CRASH_EXIT command to a CP */
- send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT));
-
- /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT,
- handle_no_cp_crash_ack() will be executed. */
- mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
- handle_no_cp_crash_ack, (unsigned long)dpld);
-
- return;
-}
-
-/**
- * ext_command_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- * @cmd: extended DPRAM command from a CP
- *
- * Processes an extended command from a CP
- */
-static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
-{
- struct link_device *ld = &dpld->ld;
- u16 resp;
-
- switch (EXT_CMD_MASK(cmd)) {
- case EXT_CMD_SET_SPEED_LOW:
- if (dpld->dpctl->setup_speed) {
- dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
- resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW);
- send_intr(dpld, resp);
- }
- break;
-
- case EXT_CMD_SET_SPEED_MID:
- if (dpld->dpctl->setup_speed) {
- dpld->dpctl->setup_speed(DPRAM_SPEED_MID);
- resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID);
- send_intr(dpld, resp);
- }
- break;
-
- case EXT_CMD_SET_SPEED_HIGH:
- if (dpld->dpctl->setup_speed) {
- dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
- resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH);
- send_intr(dpld, resp);
- }
- break;
-
- default:
- mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
- break;
- }
-}
-
-/**
- * udl_command_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- * @cmd: DPRAM upload/download command from a CP
- *
- * Processes a command for upload/download from a CP
- */
-static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd)
-{
- struct link_device *ld = &dpld->ld;
-
- if (cmd & UDL_RESULT_FAIL) {
- mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
- return;
- }
-
- switch (UDL_CMD_MASK(cmd)) {
- case UDL_CMD_RECV_READY:
- mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
- send_intr(dpld, CMD_IMG_START_REQ);
- break;
- default:
- complete_all(&dpld->udl_cmd_complete);
- }
-}
-
-/**
- * cmd_req_active_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Handles the REQ_ACTIVE command from a CP.
- */
-static void cmd_req_active_handler(struct dpram_link_device *dpld)
-{
- send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
-}
-
-/**
- * cmd_crash_reset_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Handles the CRASH_RESET command from a CP.
- */
-static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod = NULL;
- int i;
-
- ld->mode = LINK_MODE_ULOAD;
-
- if (!wake_lock_active(&dpld->wlock))
- wake_lock(&dpld->wlock);
-
- /* Stop network interfaces */
- mif_netif_stop(ld);
-
- /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */
- for (i = 0; i < ld->max_ipc_dev; i++)
- skb_queue_purge(ld->skb_txq[i]);
-
- mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
-
- /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */
- iod = link_get_iod_with_format(ld, IPC_FMT);
- iod->modem_state_changed(iod, STATE_CRASH_RESET);
-
- /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */
- iod = link_get_iod_with_format(ld, IPC_BOOT);
- iod->modem_state_changed(iod, STATE_CRASH_RESET);
-}
-
-/**
- * handle_cp_crash
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Actual handler for the CRASH_EXIT command from a CP.
- */
-static void handle_cp_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod;
- int i;
-
- /* Stop network interfaces */
- mif_netif_stop(ld);
-
- /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */
- for (i = 0; i < ld->max_ipc_dev; i++)
- skb_queue_purge(ld->skb_txq[i]);
-
- /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */
- iod = link_get_iod_with_format(ld, IPC_FMT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-
- /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */
- iod = link_get_iod_with_format(ld, IPC_BOOT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-}
-
-/**
- * cmd_crash_exit_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Handles the CRASH_EXIT command from a CP.
- */
-static void cmd_crash_exit_handler(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
-
- ld->mode = LINK_MODE_ULOAD;
-
- if (!wake_lock_active(&dpld->wlock))
- wake_lock(&dpld->wlock);
-
- del_timer(&dpld->crash_ack_timer);
-
- dpram_wake_up(dpld);
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- save_dpram_dump(dpld);
-#endif
-
- if (dpld->ext_op && dpld->ext_op->crash_log)
- dpld->ext_op->crash_log(dpld);
-
- mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
-
- handle_cp_crash(dpld);
-}
-
-/**
- * init_dpram_ipc
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Initializes IPC via DPRAM.
- */
-static int init_dpram_ipc(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- int i;
-
- if (ld->mode == LINK_MODE_IPC &&
- get_magic(dpld) == DPRAM_MAGIC_CODE &&
- get_access(dpld) == 1)
- mif_info("%s: IPC already initialized\n", ld->name);
-
- /* Clear pointers in every circular queue */
- for (i = 0; i < ld->max_ipc_dev; i++) {
- set_tx_head(dpld, i, 0);
- set_tx_tail(dpld, i, 0);
- set_rx_head(dpld, i, 0);
- set_rx_tail(dpld, i, 0);
- }
-
- /* Initialize variables for efficient TX/RX processing */
- for (i = 0; i < ld->max_ipc_dev; i++)
- dpld->iod[i] = link_get_iod_with_format(ld, i);
- dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW);
-
- if (dpld->iod[IPC_RAW]->recv_skb)
- dpld->rx_with_skb = true;
-
- for (i = 0; i < ld->max_ipc_dev; i++) {
- spin_lock_init(&dpld->tx_lock[i]);
- atomic_set(&dpld->res_required[i], 0);
- }
-
- /* Enable IPC */
- if (wake_lock_active(&dpld->wlock))
- wake_unlock(&dpld->wlock);
-
- atomic_set(&dpld->accessing, 0);
-
- set_magic(dpld, DPRAM_MAGIC_CODE);
- set_access(dpld, 1);
- if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1)
- return -EACCES;
-
- ld->mode = LINK_MODE_IPC;
-
- return 0;
-}
-
-/**
- * cmd_phone_start_handler
- * @dpld: pointer to an instance of dpram_link_device structure
- *
- * Handles the PHONE_START command from a CP.
- */
-static void cmd_phone_start_handler(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod = NULL;
-
- mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name);
-
- init_dpram_ipc(dpld);
-
- iod = link_get_iod_with_format(ld, IPC_FMT);
- if (!iod) {
- mif_info("%s: ERR! no iod\n", ld->name);
- return;
- }
-
- if (dpld->ext_op && dpld->ext_op->cp_start_handler)
- dpld->ext_op->cp_start_handler(dpld);
-
- if (ld->mc->phone_state != STATE_ONLINE) {
- mif_info("%s: phone_state: %d -> ONLINE\n",
- ld->name, ld->mc->phone_state);
- iod->modem_state_changed(iod, STATE_ONLINE);
- }
-
- mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name);
- send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
-}
-
-/**
- * command_handler: processes a DPRAM command from a CP
- * @dpld: pointer to an instance of dpram_link_device structure
- * @cmd: DPRAM command from a CP
- */
-static void command_handler(struct dpram_link_device *dpld, u16 cmd)
-{
- struct link_device *ld = &dpld->ld;
-
- switch (INT_CMD_MASK(cmd)) {
- case INT_CMD_REQ_ACTIVE:
- cmd_req_active_handler(dpld);
- break;
-
- case INT_CMD_CRASH_RESET:
- dpld->init_status = DPRAM_INIT_STATE_NONE;
- cmd_crash_reset_handler(dpld);
- break;
-
- case INT_CMD_CRASH_EXIT:
- dpld->init_status = DPRAM_INIT_STATE_NONE;
- cmd_crash_exit_handler(dpld);
- break;
-
- case INT_CMD_PHONE_START:
- dpld->init_status = DPRAM_INIT_STATE_READY;
- cmd_phone_start_handler(dpld);
- complete_all(&dpld->dpram_init_cmd);
- break;
-
- case INT_CMD_NV_REBUILDING:
- mif_info("%s: NV_REBUILDING\n", ld->name);
- break;
-
- case INT_CMD_PIF_INIT_DONE:
- complete_all(&dpld->modem_pif_init_done);
- break;
-
- case INT_CMD_SILENT_NV_REBUILDING:
- mif_info("%s: SILENT_NV_REBUILDING\n", ld->name);
- break;
-
- case INT_CMD_NORMAL_PWR_OFF:
- /*ToDo:*/
- /*kernel_sec_set_cp_ack()*/;
- break;
-
- case INT_CMD_REQ_TIME_SYNC:
- case INT_CMD_CP_DEEP_SLEEP:
- case INT_CMD_EMER_DOWN:
- break;
-
- default:
- mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
- }
-}
-
-/**
- * ipc_rx_task
- * @data: pointer to an instance of dpram_link_device structure
- *
- * Invokes the recv method in the io_device instance to perform receiving IPC
- * messages from each mif_rxb.
- */
-static void ipc_rx_task(unsigned long data)
-{
- struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- struct link_device *ld = &dpld->ld;
- struct io_device *iod;
- struct mif_rxb *rxb;
- int qlen;
- int i;
-
- for (i = 0; i < ld->max_ipc_dev; i++) {
- iod = dpld->iod[i];
- qlen = rxbq_size(&dpld->rxbq[i]);
- while (qlen > 0) {
- rxb = rxbq_get_data_rxb(&dpld->rxbq[i]);
- iod->recv(iod, ld, rxb->data, rxb->len);
- rxb_clear(rxb);
- qlen--;
- }
- }
-}
-
-/**
- * get_rxq_rcvd
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * OUT @stat: pointer to an instance of dpram_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 'stat' instance.
- *
- * Returns the size of received data in the buffer or an error code.
- */
-static int get_rxq_rcvd(struct dpram_link_device *dpld, int dev,
- struct dpram_circ_status *stat)
-{
- struct link_device *ld = &dpld->ld;
- int cnt = 0;
- u32 qsize;
- u32 head;
- u32 tail;
- int rcvd;
-
- while (1) {
- qsize = get_rx_buff_size(dpld, dev);
- head = get_rx_head(dpld, dev);
- tail = get_rx_tail(dpld, dev);
- rcvd = circ_get_usage(qsize, head, tail);
-
- mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
- ld->name, get_dev_name(dev), qsize, head, tail, rcvd);
-
- if (circ_valid(qsize, head, tail))
- break;
-
- cnt++;
- mif_err("%s: ERR! <%pf> "
- "%s_RXQ invalid (qsize:%d in:%d out:%d rcvd:%d), "
- "count %d\n",
- ld->name, __builtin_return_address(0),
- get_dev_name(dev), qsize, head, tail, rcvd, cnt);
- if (cnt >= MAX_RETRY_CNT) {
- rcvd = -EIO;
- break;
- }
-
- udelay(100);
- }
-
- stat->buff = get_rx_buff(dpld, dev);
- stat->qsize = qsize;
- stat->in = head;
- stat->out = tail;
- stat->size = rcvd;
-
- return rcvd;
-}
-
-/**
- * recv_ipc_with_rxb: receives IPC messages from an RXQ with mif_rxb
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * Returns
- * ret < 0 : error
- * ret == 0 : no data
- * ret > 0 : valid data
- *
- * Requires a bottom half (e.g. ipc_rx_task) that will invoke the recv method in
- * the io_device instance.
- */
-static int recv_ipc_with_rxb(struct dpram_link_device *dpld, int dev)
-{
- struct link_device *ld = &dpld->ld;
- struct mif_rxb *rxb;
- struct dpram_circ_status stat;
- int rcvd;
- u8 *dst;
- struct mif_irq_map map;
-
- rcvd = get_rxq_rcvd(dpld, dev, &stat);
- if (unlikely(rcvd <= 0)) {
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- if (rcvd < 0)
- trigger_force_cp_crash(dpld);
-#endif
- goto exit;
- }
-
- if (dev == IPC_FMT) {
-#if 0
- log_dpram_status(dpld, "CP2MIF");
-#endif
- set_dpram_map(dpld, &map);
- mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
- }
-
- /* Allocate an rxb */
- rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]);
- if (!rxb) {
- mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n",
- ld->name, get_dev_name(dev));
- rcvd = -ENOMEM;
- goto exit;
- }
-
- /* Read data from the RXQ */
- dst = rxb_put(rxb, stat.size);
- circ_read(dst, stat.buff, stat.qsize, stat.out, stat.size);
-
-exit:
- /* Update tail (out) pointer to empty out the RXQ */
- set_rx_tail(dpld, dev, stat.in);
-
- return rcvd;
-}
-
-/**
- * recv_ipc_with_skb: receives SIPC5 messages from an RXQ with skb
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * Returns
- * ret < 0 : error
- * ret == 0 : no data
- * ret > 0 : valid data
- *
- * Requires a recv_skb method in the io_device instance, so this function must
- * be used for only SIPC5.
- */
-static int recv_ipc_with_skb(struct dpram_link_device *dpld, int dev)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod = dpld->iod[dev];
- struct sk_buff *skb;
- /**
- * variables for the status of the circular queue
- */
- u8 __iomem *src;
- struct dpram_circ_status stat;
- /**
- * variables for RX processing
- */
- int qsize; /* size of the queue */
- int rcvd; /* size of data in the RXQ or error */
- int rest; /* size of the rest data */
- int idx; /* index to the start of current frame */
- u8 *frm; /* pointer to current frame */
- u8 *dst; /* pointer to the destination buffer */
- int len; /* length of current frame */
- int tot; /* total length including padding data */
- /**
- * variables for debug logging
- */
- struct mif_irq_map map;
-
- /* Get data size in the RXQ and in/out pointer values */
- rcvd = get_rxq_rcvd(dpld, dev, &stat);
- if (unlikely(rcvd <= 0)) {
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- if (rcvd < 0)
- trigger_force_cp_crash(dpld);
-#endif
- goto exit;
- }
-
- /* Take a log for debugging */
- if (dev == IPC_FMT) {
-#if 0
- log_dpram_status(dpld, "CP2MIF");
-#endif
- set_dpram_map(dpld, &map);
- mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
- }
-
- src = stat.buff;
- qsize = stat.qsize;
- rest = stat.size;
- idx = stat.out;
-
- while (rest > 0) {
- /* Calculate the start of an SIPC5 frame */
- frm = src + idx;
-
- /* Check the SIPC5 frame */
- len = sipc5_check_frame_in_dev(ld, dev, frm, rest);
- if (len <= 0) {
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- log_dpram_status(dpld, "CP2MIF");
- save_ipc_trace(dpld, dev, &stat);
- trigger_force_cp_crash(dpld);
-#endif
- rcvd = -EBADMSG;
- goto exit;
- }
-
- /* Calculate total length of the frame (data + padding) */
- tot = len + sipc5_calc_padding_size(len);
-
- /* Allocate an skb */
- skb = dev_alloc_skb(tot);
- if (!skb) {
- mif_err("%s: ERR! %s dev_alloc_skb fail\n",
- ld->name, get_dev_name(dev));
- rcvd = -ENOMEM;
- goto exit;
- }
-
- /* Read the frame from the RXQ */
- dst = skb_put(skb, tot);
- circ_read(dst, src, qsize, idx, tot);
-
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- /* Take a log for debugging */
- if (unlikely(dev == IPC_FMT)) {
- char str[MIF_MAX_STR_LEN];
- snprintf(str, MIF_MAX_STR_LEN, "%s: CP2MIF",
- ld->mc->name);
- pr_ipc(str, skb->data, (skb->len > 20 ? 20 : skb->len));
- }
-#endif
-
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- /* Verify data copied to the skb */
- if (ld->aligned && memcmp16_to_io(frm, dst, 4)) {
- mif_err("%s: memcmp16_to_io fail\n", ld->name);
- trigger_force_cp_crash(dpld);
- rcvd = -EIO;
- goto exit;
- }
-#endif
-
- /* Remove padding in the skb */
- skb_trim(skb, len);
-
- /* Pass the frame to the corresponding IO device */
- iod->recv_skb(iod, ld, skb);
-
- /* Calculate new idx value */
- rest -= tot;
- idx += tot;
- if (idx >= qsize)
- idx -= qsize;
- }
-
-exit:
- /* Update tail (out) pointer to empty out the RXQ */
- set_rx_tail(dpld, dev, stat.in);
-
- return rcvd;
-}
-
-/**
- * recv_ipc_msg: receives IPC messages from every RXQ
- * @dpld: pointer to an instance of dpram_link_device structure
- * @intr: interrupt value from a CP
- *
- * 1) Receives all IPC message frames currently in every DPRAM RXQ.
- * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP.
- * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if
- * there is any RES_ACK response.
- */
-static void recv_ipc_msg(struct dpram_link_device *dpld, u16 intr)
-{
- struct link_device *ld = &dpld->ld;
- int i = 0;
- int ret = 0;
- u16 mask = 0;
-
- if (!ipc_active(dpld))
- return;
-
- /* Read data from DPRAM */
- for (i = 0; i < ld->max_ipc_dev; i++) {
- if (dpld->rx_with_skb)
- ret = recv_ipc_with_skb(dpld, i);
- else
- ret = recv_ipc_with_rxb(dpld, i);
-
- /* Check and process REQ_ACK (at this time, in == out) */
- if (intr & get_mask_req_ack(dpld, i)) {
- mif_debug("%s: send %s_RES_ACK\n",
- ld->name, get_dev_name(i));
- mask |= get_mask_res_ack(dpld, i);
- }
- }
-
- if (!dpld->rx_with_skb) {
- /* Schedule soft IRQ for RX */
- tasklet_hi_schedule(&dpld->rx_tsk);
- }
-
- if (mask) {
- send_intr(dpld, INT_NON_CMD(mask));
- mif_debug("%s: send intr 0x%04X\n", ld->name, mask);
- }
-
- if (intr && INT_MASK_RES_ACK_SET) {
- if (intr && INT_MASK_RES_ACK_R)
- complete_all(&dpld->req_ack_cmpl[IPC_RAW]);
- else if (intr && INT_MASK_RES_ACK_F)
- complete_all(&dpld->req_ack_cmpl[IPC_FMT]);
- else
- complete_all(&dpld->req_ack_cmpl[IPC_RFS]);
- }
-}
-
-/**
- * cmd_msg_handler: processes a DPRAM command or receives IPC messages
- * @dpld: pointer to an instance of dpram_link_device structure
- * @intr: interrupt value from a CP
- *
- * Invokes command_handler for a DPRAM command or recv_ipc_msg for IPC messages.
- */
-static inline void cmd_msg_handler(struct dpram_link_device *dpld, u16 intr)
-{
- if (unlikely(INT_CMD_VALID(intr)))
- command_handler(dpld, intr);
- else
- recv_ipc_msg(dpld, intr);
-}
-
-/**
- * intr_handler: processes an interrupt from a CP
- * @dpld: pointer to an instance of dpram_link_device structure
- * @intr: interrupt value from a CP
- *
- * Call flow for normal interrupt handling:
- * cmd_msg_handler -> command_handler -> cmd_xxx_handler
- * cmd_msg_handler -> recv_ipc_msg -> recv_ipc_with_skb/recv_ipc_with_rxb ...
- */
-static inline void intr_handler(struct dpram_link_device *dpld, u16 intr)
-{
- char *name = dpld->ld.name;
-
- if (unlikely(intr == INT_POWERSAFE_FAIL)) {
- mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name);
- return;
- }
-
- if (unlikely(EXT_UDL_CMD(intr))) {
- if (likely(EXT_INT_VALID(intr))) {
- if (UDL_CMD_VALID(intr))
- udl_command_handler(dpld, intr);
- else if (EXT_CMD_VALID(intr))
- ext_command_handler(dpld, intr);
- else
- mif_info("%s: ERR! invalid intr 0x%04X\n",
- name, intr);
- } else {
- mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
- }
-
- return;
- }
-
- if (likely(INT_VALID(intr)))
- cmd_msg_handler(dpld, intr);
- else
- mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
-}
-
-/**
- * ap_idpram_irq_handler: interrupt handler for an internal DPRAM in an AP
- * @irq: IRQ number
- * @data: pointer to a data
- *
- * 1) Reads the interrupt value
- * 2) Performs interrupt handling
- */
-static irqreturn_t ap_idpram_irq_handler(int irq, void *data)
-{
- struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- struct link_device *ld = (struct link_device *)&dpld->ld;
- u16 int2ap = recv_intr(dpld);
-
- if (unlikely(ld->mode == LINK_MODE_OFFLINE))
- return IRQ_HANDLED;
-
- intr_handler(dpld, int2ap);
-
- return IRQ_HANDLED;
-}
-
-/**
- * cp_idpram_irq_handler: interrupt handler for an internal DPRAM in a CP
- * @irq: IRQ number
- * @data: pointer to a data
- *
- * 1) Wakes up the DPRAM
- * 2) Reads the interrupt value
- * 3) Performs interrupt handling
- * 4) Clears the interrupt port (port = memory or register)
- * 5) Allows the DPRAM to sleep
- */
-static irqreturn_t cp_idpram_irq_handler(int irq, void *data)
-{
- struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- struct link_device *ld = (struct link_device *)&dpld->ld;
- u16 int2ap;
-
- if (unlikely(ld->mode == LINK_MODE_OFFLINE))
- return IRQ_HANDLED;
-
- if (dpram_wake_up(dpld) < 0) {
- trigger_force_cp_crash(dpld);
- return IRQ_HANDLED;
- }
-
- int2ap = recv_intr(dpld);
-
- intr_handler(dpld, int2ap);
-
- clear_intr(dpld);
-
- dpram_allow_sleep(dpld);
-
- return IRQ_HANDLED;
-}
-
-/**
- * ext_dpram_irq_handler: interrupt handler for a normal external DPRAM
- * @irq: IRQ number
- * @data: pointer to a data
- *
- * 1) Reads the interrupt value
- * 2) Performs interrupt handling
- */
-static irqreturn_t ext_dpram_irq_handler(int irq, void *data)
-{
- struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- struct link_device *ld = (struct link_device *)&dpld->ld;
- u16 int2ap = recv_intr(dpld);
-
- if (unlikely(ld->mode == LINK_MODE_OFFLINE))
- return IRQ_HANDLED;
-
- intr_handler(dpld, int2ap);
-
- return IRQ_HANDLED;
-}
-
-/**
- * get_txq_space
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * OUT @stat: pointer to an instance of dpram_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 'stat' instance.
- *
- * Returns the size of free space in the buffer or an error code.
- */
-static int get_txq_space(struct dpram_link_device *dpld, int dev,
- struct dpram_circ_status *stat)
-{
- struct link_device *ld = &dpld->ld;
- int cnt = 0;
- u32 qsize;
- u32 head;
- u32 tail;
- int space;
-
- while (1) {
- qsize = get_tx_buff_size(dpld, dev);
- head = get_tx_head(dpld, dev);
- tail = get_tx_tail(dpld, 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! <%pf> "
- "%s_TXQ invalid (qsize:%d in:%d out:%d space:%d), "
- "count %d\n",
- ld->name, __builtin_return_address(0),
- get_dev_name(dev), qsize, head, tail, space, cnt);
- if (cnt >= MAX_RETRY_CNT) {
- space = -EIO;
- break;
- }
-
- udelay(100);
- }
-
- stat->buff = get_tx_buff(dpld, dev);
- stat->qsize = qsize;
- stat->in = head;
- stat->out = tail;
- stat->size = space;
-
- return space;
-}
-
-/**
- * write_ipc_to_txq
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @stat: pointer to an instance of dpram_circ_status structure
- * @skb: pointer to an instance of sk_buff structure
- *
- * Must be invoked only when there is enough space in the TXQ.
- */
-static void write_ipc_to_txq(struct dpram_link_device *dpld, int dev,
- struct dpram_circ_status *stat, struct sk_buff *skb)
-{
- struct link_device *ld = &dpld->ld;
- u8 __iomem *buff = stat->buff;
- u32 qsize = stat->qsize;
- u32 in = stat->in;
- u8 *src = skb->data;
- u32 len = skb->len;
- u32 inp;
- struct mif_irq_map map;
-
- /* Write data to the TXQ */
- circ_write(buff, src, qsize, in, len);
-
- /* Update new head (in) pointer */
- inp = in + len;
- if (inp >= qsize)
- inp -= qsize;
- set_tx_head(dpld, dev, inp);
-
- /* Take a log for debugging */
- if (dev == IPC_FMT) {
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- char tag[MIF_MAX_STR_LEN];
- snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2CP", ld->mc->name);
- pr_ipc(tag, src, (len > 20 ? 20 : len));
-#if 0
- log_dpram_status(dpld, "MIF2CP");
-#endif
-#endif
- set_dpram_map(dpld, &map);
- mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write"));
- mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len);
- }
-
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- /* Verify data written to the TXQ */
- if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) {
- mif_err("%s: memcmp16_to_io fail\n", ld->name);
- trigger_force_cp_crash(dpld);
- }
-#endif
-}
-
-/**
- * xmit_ipc_msg
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * Tries to transmit IPC messages in the skb_txq of @dev as many as possible.
- *
- * Returns total length of IPC messages transmitted or an error code.
- */
-static int xmit_ipc_msg(struct dpram_link_device *dpld, int dev)
-{
- struct link_device *ld = &dpld->ld;
- struct sk_buff_head *txq = ld->skb_txq[dev];
- struct sk_buff *skb;
- unsigned long flags;
- struct dpram_circ_status stat;
- int space;
- int copied = 0;
-
- /* Acquire the spin lock for a TXQ */
- spin_lock_irqsave(&dpld->tx_lock[dev], flags);
-
- while (1) {
- /* Get the size of free space in the TXQ */
- space = get_txq_space(dpld, dev, &stat);
- if (unlikely(space < 0)) {
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- /* Trigger a enforced CP crash */
- trigger_force_cp_crash(dpld);
-#endif
- /* Empty out the TXQ */
- set_tx_head(dpld, dev, stat.out);
- copied = -EIO;
- break;
- }
-
- skb = skb_dequeue(txq);
- if (unlikely(!skb))
- break;
-
- /* Check the free space size comparing with skb->len */
- if (unlikely(space < skb->len)) {
- atomic_set(&dpld->res_required[dev], 1);
- /* Take the skb back to the skb_txq */
- skb_queue_head(txq, skb);
- mif_info("%s: %s qsize[%u] "
- "in[%u] out[%u] free[%u] < len[%u]\n",
- ld->name, get_dev_name(dev), stat.qsize,
- stat.in, stat.out, space, skb->len);
- copied = -ENOSPC;
- break;
- }
-
- /* TX only when there is enough space in the TXQ */
- write_ipc_to_txq(dpld, dev, &stat, skb);
- copied += skb->len;
- dev_kfree_skb_any(skb);
- }
-
- /* Release the spin lock */
- spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
-
- return copied;
-}
-
-/**
- * wait_for_res_ack
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * 1) Sends an REQ_ACK interrupt for @dev to CP.
- * 2) Waits for the corresponding RES_ACK for @dev from CP.
- *
- * Returns the return value from wait_for_completion_interruptible_timeout().
- */
-static int wait_for_res_ack(struct dpram_link_device *dpld, int dev)
-{
- struct link_device *ld = &dpld->ld;
- struct completion *cmpl = &dpld->req_ack_cmpl[dev];
- unsigned long timeout = RES_ACK_WAIT_TIMEOUT;
- int ret;
- u16 mask;
-
- mask = get_mask_req_ack(dpld, dev);
- mif_info("%s: send %s_REQ_ACK\n", ld->name, get_dev_name(dev));
- send_intr(dpld, INT_NON_CMD(mask));
-
- ret = wait_for_completion_interruptible_timeout(cmpl, timeout);
- /* ret == 0 on timeout, ret < 0 if interrupted */
- if (ret == 0) {
- mif_info("%s: TIMEOUT! no %s_RES_ACK\n",
- ld->name, get_dev_name(dev));
- } else if (ret < 0) {
- mif_info("%s: %s: interrupted (ret %d)\n",
- ld->name, get_dev_name(dev), ret);
- }
-
- return ret;
-}
-
-/**
- * process_res_ack
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- *
- * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg()
- * function.
- * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg().
- * 3) Restarts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC.
- *
- * Returns the return value from xmit_ipc_msg().
- */
-static int process_res_ack(struct dpram_link_device *dpld, int dev)
-{
- struct link_device *ld = &dpld->ld;
- int ret;
- u16 mask;
-
- mif_info("%s: recv %s_RES_ACK\n", ld->name, get_dev_name(dev));
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- log_dpram_status(dpld, "LATEST");
-#endif
-
- ret = xmit_ipc_msg(dpld, dev);
- if (ret > 0) {
- mask = get_mask_send(dpld, dev);
- send_intr(dpld, INT_NON_CMD(mask));
- atomic_set(&dpld->res_required[dev], 0);
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- mif_info("%s: xmit_ipc_msg done (%d bytes)\n", ld->name, ret);
-#endif
- goto exit;
- }
-
- if (ret == 0) {
- mif_info("%s: %s skb_txq empty\n", ld->name, get_dev_name(dev));
- atomic_set(&dpld->res_required[dev], 0);
- goto exit;
- }
-
- /* At this point, ret < 0 */
- if (ret == -ENOSPC) {
- /*
- ** dpld->res_required[dev] is set in xmit_ipc_msg()
- */
- mif_info("%s: xmit_ipc_msg fail (err -ENOSPC)\n", ld->name);
- queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0);
- } else {
- mif_err("%s: ERR! xmit_ipc_msg fail (err %d)\n",
- ld->name, ret);
- }
-
-exit:
- return ret;
-}
-
-/**
- * fmt_tx_work: performs TX for FMT IPC device under DPRAM flow control
- * @work: pointer to an instance of the work_struct structure
- *
- * 1) Starts waiting for RES_ACK of FMT IPC device.
- * 2) Returns immediately if the wait is interrupted.
- * 3) Restarts DPRAM flow control if there is a timeout from the wait.
- * 4) Otherwise, it performs processing RES_ACK for FMT IPC device.
- */
-static void fmt_tx_work(struct work_struct *work)
-{
- struct link_device *ld;
- struct dpram_link_device *dpld;
- unsigned long delay = REQ_ACK_DELAY;
- int ret;
-
- ld = container_of(work, struct link_device, fmt_tx_dwork.work);
- dpld = to_dpram_link_device(ld);
-
- ret = wait_for_res_ack(dpld, IPC_FMT);
- /* ret == 0 on timeout, ret < 0 if interrupted */
- if (ret < 0)
- return;
- else if (ret == 0)
- queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], delay);
- else
- process_res_ack(dpld, IPC_FMT);
-}
-
-/**
- * raw_tx_work: performs TX for RAW IPC device under DPRAM flow control.
- * @work: pointer to an instance of the work_struct structure
- *
- * 1) Starts waiting for RES_ACK of RAW IPC device.
- * 2) Returns immediately if the wait is interrupted.
- * 3) Restarts DPRAM flow control if there is a timeout from the wait.
- * 4) Otherwise, it performs processing RES_ACK for RAW IPC device.
- */
-static void raw_tx_work(struct work_struct *work)
-{
- struct link_device *ld;
- struct dpram_link_device *dpld;
- unsigned long delay = REQ_ACK_DELAY;
- int ret;
-
- ld = container_of(work, struct link_device, raw_tx_dwork.work);
- dpld = to_dpram_link_device(ld);
-
- ret = wait_for_res_ack(dpld, IPC_RAW);
- /* ret == 0 on timeout, ret < 0 if interrupted */
- if (ret < 0)
- return;
-
- if (ret == 0) {
- queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], delay);
- return;
- }
-
- ret = process_res_ack(dpld, IPC_RAW);
- if (ret > 0)
- mif_netif_wake(ld);
-}
-
-/**
- * rfs_tx_work: performs TX for RFS IPC device under DPRAM flow control
- * @work: pointer to an instance of the work_struct structure
- *
- * 1) Starts waiting for RES_ACK of RFS IPC device.
- * 2) Returns immediately if the wait is interrupted.
- * 3) Restarts DPRAM flow control if there is a timeout from the wait.
- * 4) Otherwise, it performs processing RES_ACK for RFS IPC device.
- */
-static void rfs_tx_work(struct work_struct *work)
-{
- struct link_device *ld;
- struct dpram_link_device *dpld;
- unsigned long delay = REQ_ACK_DELAY;
- int ret;
-
- ld = container_of(work, struct link_device, rfs_tx_dwork.work);
- dpld = to_dpram_link_device(ld);
-
- ret = wait_for_res_ack(dpld, IPC_RFS);
- /* ret == 0 on timeout, ret < 0 if interrupted */
- if (ret < 0)
- return;
- else if (ret == 0)
- queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], delay);
- else
- process_res_ack(dpld, IPC_RFS);
-}
-
-/**
- * dpram_send_ipc
- * @dpld: pointer to an instance of dpram_link_device structure
- * @dev: IPC device (IPC_FMT, IPC_RAW, etc.)
- * @iod: pointer to an instance of the io_device structure
- * @skb: pointer to an skb that will be transmitted
- *
- * 1) Enqueues an skb to the skb_txq for @dev in the link device instance.
- * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg()
- * function.
- * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg().
- * 4) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC.
- */
-static void dpram_send_ipc(struct dpram_link_device *dpld, int dev,
- struct io_device *iod, struct sk_buff *skb)
-{
- struct link_device *ld = &dpld->ld;
- struct sk_buff_head *txq = ld->skb_txq[dev];
- int ret;
- u16 mask;
-
- if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) {
- mif_err("%s: %s txq->qlen %d >= %d\n", ld->name,
- get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH);
- if (iod->io_typ == IODEV_NET || iod->format == IPC_MULTI_RAW) {
- dev_kfree_skb_any(skb);
- return;
- }
- }
-
- skb_queue_tail(txq, skb);
-
- if (dpram_wake_up(dpld) < 0) {
- trigger_force_cp_crash(dpld);
- return;
- }
-
- if (!ipc_active(dpld)) {
- mif_info("%s: IPC is NOT active\n", ld->name);
- goto exit;
- }
-
- if (atomic_read(&dpld->res_required[dev]) > 0) {
- mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev));
- goto exit;
- }
-
- ret = xmit_ipc_msg(dpld, dev);
- if (likely(ret > 0)) {
- mask = get_mask_send(dpld, dev);
- send_intr(dpld, INT_NON_CMD(mask));
- goto exit;
- }
-
- if (ret == 0) {
- mif_info("%s: %s skb_txq empty\n", ld->name, get_dev_name(dev));
- goto exit;
- }
-
- /* At this point, ret < 0 */
- if (ret == -ENOSPC) {
- /*
- ** dpld->res_required[dev] is set in xmit_ipc_msg()
- */
- if (dev == IPC_RAW)
- mif_netif_stop(ld);
- mif_info("%s->%s: xmit_ipc_msg fail (err -ENOSPC)\n",
- iod->name, ld->name);
- queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0);
- } else {
- mif_err("%s->%s: ERR! xmit_ipc_msg fail (err %d)\n",
- iod->name, ld->name, ret);
- }
-
-exit:
- dpram_allow_sleep(dpld);
-}
-
-static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
-
- if (dpld->ext_op && dpld->ext_op->download_binary)
- return dpld->ext_op->download_binary(dpld, skb);
- else
- return -ENODEV;
-}
-
-/**
- * dpram_send
- * @ld: pointer to an instance of the link_device structure
- * @iod: pointer to an instance of the io_device structure
- * @skb: pointer to an skb that will be transmitted
- *
- * Returns the length of data transmitted or an error code.
- *
- * Normal call flow for an IPC message:
- * dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq
- *
- * Call flow on congestion in a DPRAM IPC TXQ:
- * dpram_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work
- * => xxx_tx_work -> wait_for_res_ack
- * => recv_ipc_msg
- * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...)
- */
-static int dpram_send(struct link_device *ld, struct io_device *iod,
- struct sk_buff *skb)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- int dev = iod->format;
- int len = skb->len;
-
- switch (dev) {
- case IPC_FMT:
- case IPC_RAW:
- case IPC_RFS:
- if (likely(ld->mode == LINK_MODE_IPC)) {
- dpram_send_ipc(dpld, dev, iod, skb);
- } else {
- mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name);
- dev_kfree_skb_any(skb);
- }
- return len;
-
- case IPC_BOOT:
- return dpram_send_cp_binary(ld, skb);
-
- default:
- mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name);
- dev_kfree_skb_any(skb);
- return -ENODEV;
- }
-}
-
-static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- trigger_force_cp_crash(dpld);
- return 0;
-}
-
-static int dpram_dump_start(struct link_device *ld, struct io_device *iod)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
-
- if (dpld->ext_op && dpld->ext_op->dump_start)
- return dpld->ext_op->dump_start(dpld);
- else
- return -ENODEV;
-}
-
-static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
- unsigned long arg)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
-
- if (dpld->ext_op && dpld->ext_op->dump_update)
- return dpld->ext_op->dump_update(dpld, (void *)arg);
- else
- return -ENODEV;
-}
-
-static int dpram_ioctl(struct link_device *ld, struct io_device *iod,
- unsigned int cmd, unsigned long arg)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- int err = 0;
-
- mif_info("%s: cmd 0x%08X\n", ld->name, cmd);
-
- switch (cmd) {
- case IOCTL_DPRAM_INIT_STATUS:
- mif_debug("%s: get dpram init status\n", ld->name);
- return dpld->init_status;
-
- default:
- if (dpld->ext_ioctl) {
- err = dpld->ext_ioctl(dpld, iod, cmd, arg);
- } else {
- mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
- err = -EINVAL;
- }
-
- break;
- }
-
- return err;
-}
-
-static void dpram_dump_memory(struct link_device *ld, char *buff)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- dpram_wake_up(dpld);
- memcpy(buff, dpld->base, dpld->size);
- dpram_allow_sleep(dpld);
-}
-
-static void dpram_remap_std_16k_region(struct dpram_link_device *dpld)
-{
- struct dpram_ipc_16k_map *dpram_map;
- struct dpram_ipc_device *dev;
-
- dpram_map = (struct dpram_ipc_16k_map *)dpld->base;
-
- /* "magic code" and "access enable" fields */
- dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic;
- dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access;
-
- /* FMT */
- dev = &dpld->ipc_map.dev[IPC_FMT];
-
- strcpy(dev->name, "FMT");
- dev->id = IPC_FMT;
-
- dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head;
- dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail;
- dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0];
- dev->txq.size = DP_16K_FMT_TX_BUFF_SZ;
-
- dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head;
- dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail;
- dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0];
- dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ;
-
- dev->mask_req_ack = INT_MASK_REQ_ACK_F;
- dev->mask_res_ack = INT_MASK_RES_ACK_F;
- dev->mask_send = INT_MASK_SEND_F;
-
- /* RAW */
- dev = &dpld->ipc_map.dev[IPC_RAW];
-
- strcpy(dev->name, "RAW");
- dev->id = IPC_RAW;
-
- dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head;
- dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail;
- dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0];
- dev->txq.size = DP_16K_RAW_TX_BUFF_SZ;
-
- dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head;
- dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail;
- dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0];
- dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ;
-
- dev->mask_req_ack = INT_MASK_REQ_ACK_R;
- dev->mask_res_ack = INT_MASK_RES_ACK_R;
- dev->mask_send = INT_MASK_SEND_R;
-
- /* interrupt ports */
- dpld->ipc_map.mbx_cp2ap = (u16 __iomem *)&dpram_map->mbx_cp2ap;
- dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp;
-}
-
-static int dpram_table_init(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- u8 __iomem *dp_base;
- int i;
-
- if (!dpld->base) {
- mif_err("%s: ERR! dpld->base == NULL\n", ld->name);
- return -EINVAL;
- }
- dp_base = dpld->base;
-
- /* Map for booting */
- if (dpld->ext_op && dpld->ext_op->init_boot_map) {
- dpld->ext_op->init_boot_map(dpld);
- } else {
- dpld->bt_map.magic = (u32 *)(dp_base);
- dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET);
- dpld->bt_map.size = dpld->size - 8;
- }
-
- /* Map for download (FOTA, UDL, etc.) */
- if (dpld->ext_op && dpld->ext_op->init_dl_map) {
- dpld->ext_op->init_dl_map(dpld);
- } else {
- dpld->dl_map.magic = (u32 *)(dp_base);
- dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
- }
-
- /* Map for upload mode */
- if (dpld->ext_op && dpld->ext_op->init_ul_map) {
- dpld->ext_op->init_ul_map(dpld);
- } else {
- dpld->ul_map.magic = (u32 *)(dp_base);
- dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET);
- }
-
- /* Map for IPC */
- if (dpld->ext_op && dpld->ext_op->init_ipc_map) {
- dpld->ext_op->init_ipc_map(dpld);
- } else if (dpld->dpctl->ipc_map) {
- memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map,
- sizeof(struct dpram_ipc_map));
- } else {
- if (dpld->size == DPRAM_SIZE_16KB)
- dpram_remap_std_16k_region(dpld);
- else
- return -EINVAL;
- }
-
- dpld->magic = dpld->ipc_map.magic;
- dpld->access = dpld->ipc_map.access;
- for (i = 0; i < ld->max_ipc_dev; i++)
- dpld->dev[i] = &dpld->ipc_map.dev[i];
- dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap;
- dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp;
-
- return 0;
-}
-
-static void dpram_setup_common_op(struct dpram_link_device *dpld)
-{
- dpld->recv_intr = recv_intr;
- dpld->send_intr = send_intr;
- dpld->get_magic = get_magic;
- dpld->set_magic = set_magic;
- dpld->get_access = get_access;
- dpld->set_access = set_access;
- dpld->get_tx_head = get_tx_head;
- dpld->get_tx_tail = get_tx_tail;
- dpld->set_tx_head = set_tx_head;
- dpld->set_tx_tail = set_tx_tail;
- dpld->get_tx_buff = get_tx_buff;
- dpld->get_tx_buff_size = get_tx_buff_size;
- dpld->get_rx_head = get_rx_head;
- dpld->get_rx_tail = get_rx_tail;
- dpld->set_rx_head = set_rx_head;
- dpld->set_rx_tail = set_rx_tail;
- dpld->get_rx_buff = get_rx_buff;
- dpld->get_rx_buff_size = get_rx_buff_size;
- dpld->get_mask_req_ack = get_mask_req_ack;
- dpld->get_mask_res_ack = get_mask_res_ack;
- dpld->get_mask_send = get_mask_send;
- dpld->ipc_rx_handler = cmd_msg_handler;
-}
-
-static int dpram_link_init(struct link_device *ld, struct io_device *iod)
-{
- return 0;
-}
-
-static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
-{
- if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) {
- if (!atomic_read(&iod->opened)) {
- ld->mode = LINK_MODE_OFFLINE;
- mif_err("%s: %s: link mode is changed: IPC->OFFLINE\n",
- iod->name, ld->name);
- }
- }
-
- return;
-}
-
-struct link_device *dpram_create_link_device(struct platform_device *pdev)
-{
- struct dpram_link_device *dpld = NULL;
- struct link_device *ld = NULL;
- struct modem_data *modem = NULL;
- struct modemlink_dpram_control *dpctl = NULL;
- struct resource *res = NULL;
- resource_size_t res_size;
- unsigned long task_data;
- int ret = 0;
- int i = 0;
- int bsize;
- int qsize;
-
- /*
- ** Alloc an instance of dpram_link_device structure
- */
- dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
- if (!dpld) {
- mif_err("ERR! kzalloc dpld fail\n");
- goto err;
- }
- ld = &dpld->ld;
-
- /*
- ** Get the modem (platform) data
- */
- modem = (struct modem_data *)pdev->dev.platform_data;
- if (!modem) {
- mif_err("ERR! modem == NULL\n");
- goto err;
- }
- mif_info("modem = %s\n", modem->name);
- mif_info("link device = %s\n", modem->link_name);
-
- /*
- ** Retrieve modem data and DPRAM control data from the modem data
- */
- ld->mdm_data = modem;
- ld->name = modem->link_name;
- ld->ipc_version = modem->ipc_version;
-
- if (!modem->dpram_ctl) {
- mif_err("ERR! modem->dpram_ctl == NULL\n");
- goto err;
- }
- dpctl = modem->dpram_ctl;
-
- dpld->dpctl = dpctl;
- dpld->type = dpctl->dp_type;
-
- if (ld->ipc_version < SIPC_VER_50) {
- if (!dpctl->max_ipc_dev) {
- mif_err("%s: ERR! no max_ipc_dev\n", ld->name);
- goto err;
- }
-
- ld->aligned = dpctl->aligned;
- ld->max_ipc_dev = dpctl->max_ipc_dev;
- } else {
- ld->aligned = 1;
- ld->max_ipc_dev = MAX_SIPC5_DEV;
- }
-
- /*
- ** Set attributes as a link device
- */
- ld->init_comm = dpram_link_init;
- ld->terminate_comm = dpram_link_terminate;
- ld->send = dpram_send;
- ld->force_dump = dpram_force_dump;
- ld->dump_start = dpram_dump_start;
- ld->dump_update = dpram_dump_update;
- ld->ioctl = dpram_ioctl;
-
- INIT_LIST_HEAD(&ld->list);
-
- skb_queue_head_init(&ld->sk_fmt_tx_q);
- skb_queue_head_init(&ld->sk_raw_tx_q);
- skb_queue_head_init(&ld->sk_rfs_tx_q);
- ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q;
- ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
- ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
-
- /*
- ** Set up function pointers
- */
- dpram_setup_common_op(dpld);
- dpld->dpram_dump = dpram_dump_memory;
- dpld->ext_op = dpram_get_ext_op(modem->modem_type);
- if (dpld->ext_op && dpld->ext_op->ioctl)
- dpld->ext_ioctl = dpld->ext_op->ioctl;
- if (dpld->ext_op && dpld->ext_op->wakeup && dpld->ext_op->sleep)
- dpld->need_wake_up = true;
- if (dpld->ext_op && dpld->ext_op->clear_intr)
- dpld->need_intr_clear = true;
-
- /*
- ** Retrieve DPRAM resource
- */
- if (!dpctl->dp_base) {
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- STR_DPRAM_BASE);
- if (!res) {
- mif_err("%s: ERR! no DPRAM resource\n", ld->name);
- goto err;
- }
- res_size = resource_size(res);
-
- dpctl->dp_base = ioremap_nocache(res->start, res_size);
- if (!dpctl->dp_base) {
- mif_err("%s: ERR! ioremap_nocache for BASE fail\n",
- ld->name);
- goto err;
- }
- dpctl->dp_size = res_size;
- }
- dpld->base = dpctl->dp_base;
- dpld->size = dpctl->dp_size;
-
- mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n",
- ld->name, dpld->type, ld->aligned, (int)dpld->base, dpld->size);
-
- /*
- ** Retrieve DPRAM SFR resource if exists
- */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- STR_DPRAM_SFR_BASE);
- if (res) {
- res_size = resource_size(res);
- dpld->sfr_base = ioremap_nocache(res->start, res_size);
- if (!dpld->sfr_base) {
- mif_err("%s: ERR! ioremap_nocache for SFR fail\n",
- ld->name);
- goto err;
- }
- }
-
- /* Initialize DPRAM map (physical map -> logical map) */
- ret = dpram_table_init(dpld);
- if (ret < 0) {
- mif_err("%s: ERR! dpram_table_init fail (err %d)\n",
- ld->name, ret);
- goto err;
- }
-
- /* Disable IPC */
- if (!dpctl->disabled) {
- set_magic(dpld, 0);
- set_access(dpld, 0);
- }
- dpld->init_status = DPRAM_INIT_STATE_NONE;
-
- /* Initialize locks, completions, and bottom halves */
- snprintf(dpld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name);
- wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name);
-
- init_completion(&dpld->dpram_init_cmd);
- init_completion(&dpld->modem_pif_init_done);
- init_completion(&dpld->udl_start_complete);
- init_completion(&dpld->udl_cmd_complete);
- init_completion(&dpld->crash_start_complete);
- init_completion(&dpld->crash_recv_done);
- for (i = 0; i < ld->max_ipc_dev; i++)
- init_completion(&dpld->req_ack_cmpl[i]);
-
- task_data = (unsigned long)dpld;
- tasklet_init(&dpld->rx_tsk, ipc_rx_task, task_data);
-
- ld->tx_wq = create_singlethread_workqueue("dpram_tx_wq");
- if (!ld->tx_wq) {
- mif_err("%s: ERR! fail to create tx_wq\n", ld->name);
- goto err;
- }
- INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work);
- INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work);
- INIT_DELAYED_WORK(&ld->rfs_tx_dwork, rfs_tx_work);
- ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork;
- ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork;
- ld->tx_dwork[IPC_RFS] = &ld->rfs_tx_dwork;
-
-#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
- INIT_DELAYED_WORK(&dpld->dump_dwork, save_dpram_dump_work);
- INIT_DELAYED_WORK(&dpld->trace_dwork, save_ipc_trace_work);
- spin_lock_init(&dpld->dump_list.lock);
- spin_lock_init(&dpld->trace_list.lock);
-#endif
-
- /* Prepare RXB queue */
- qsize = DPRAM_MAX_RXBQ_SIZE;
- for (i = 0; i < ld->max_ipc_dev; i++) {
- bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i));
- dpld->rxbq[i].size = qsize;
- dpld->rxbq[i].in = 0;
- dpld->rxbq[i].out = 0;
- dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize);
- if (!dpld->rxbq[i].rxb) {
- mif_err("%s: ERR! %s rxbq_create_pool fail\n",
- ld->name, get_dev_name(i));
- goto err;
- }
- mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n",
- ld->name, get_dev_name(i), bsize, qsize);
- }
-
- /* Prepare a multi-purpose miscellaneous buffer */
- dpld->buff = kzalloc(dpld->size, GFP_KERNEL);
- if (!dpld->buff) {
- mif_err("%s: ERR! kzalloc dpld->buff fail\n", ld->name);
- goto err;
- }
-
- /*
- ** Retrieve DPRAM IRQ GPIO#, IRQ#, and IRQ flags
- */
- dpld->gpio_dpram_int = modem->gpio_dpram_int;
-
- if (dpctl->dpram_irq) {
- dpld->irq = dpctl->dpram_irq;
- } else {
- dpld->irq = platform_get_irq_byname(pdev, STR_DPRAM_IRQ);
- if (dpld->irq < 0) {
- mif_err("%s: ERR! no DPRAM IRQ resource\n", ld->name);
- goto err;
- }
- }
-
- if (dpctl->dpram_irq_flags)
- dpld->irq_flags = dpctl->dpram_irq_flags;
- else
- dpld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW);
-
- /*
- ** Register DPRAM interrupt handler
- */
- snprintf(dpld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name);
- if (dpld->ext_op && dpld->ext_op->irq_handler)
- dpld->irq_handler = dpld->ext_op->irq_handler;
- else if (dpld->type == CP_IDPRAM)
- dpld->irq_handler = cp_idpram_irq_handler;
- else if (dpld->type == AP_IDPRAM)
- dpld->irq_handler = ap_idpram_irq_handler;
- else
- dpld->irq_handler = ext_dpram_irq_handler;
-
- ret = register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags,
- dpld->irq_name, dpld);
- if (ret)
- goto err;
-
- return ld;
-
-err:
- if (dpld) {
- if (dpld->buff)
- kfree(dpld->buff);
- kfree(dpld);
- }
-
- return NULL;
-}
-