diff options
author | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-10-25 21:44:51 +0100 |
---|---|---|
committer | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-10-25 21:44:51 +0100 |
commit | 5e64624059d6f984f4304abf336cce05cdb0212f (patch) | |
tree | 95fff68ddba9921771d458d755c87489a5fccf08 /drivers/misc/modem_if_u1/modem_link_device_dpram.c | |
parent | b673969b102cece38c5de54d35617425459174f5 (diff) | |
download | kernel_samsung_smdk4412-5e64624059d6f984f4304abf336cce05cdb0212f.zip kernel_samsung_smdk4412-5e64624059d6f984f4304abf336cce05cdb0212f.tar.gz kernel_samsung_smdk4412-5e64624059d6f984f4304abf336cce05cdb0212f.tar.bz2 |
remove more unused drivers, readd accidentally removed iommu, reenable graphics settings
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.c | 2657 |
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; -} - |