/* ret < 0 : error ret == 0 : no data ret > 0 : valid data */ static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; struct dpram_rxb *rxb; u8 __iomem *src = get_rx_buff(dpld, dev); u32 qsize = get_rx_buff_size(dpld, dev); u32 in = get_rx_head(dpld, dev); u32 out = get_rx_tail(dpld, dev); u32 rcvd = 0; struct mif_irq_map map; if (in == out) return 0; if (dev == IPC_FMT) { set_dpram_map(dpld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Get data length in DPRAM*/ rcvd = (in > out) ? (in - out) : (qsize - out + in); mif_debug("%s: %s qsize[%u] in[%u] out[%u] rcvd[%u]\n", ld->name, get_dev_name(dev), qsize, in, out, rcvd); /* Check each queue */ if (!dpram_circ_valid(qsize, in, out)) { mif_err("%s: ERR! %s_RXQ invalid (size:%d in:%d out:%d)\n", ld->name, get_dev_name(dev), qsize, in, out); #if 0 set_rx_head(dpld, dev, 0); set_rx_tail(dpld, dev, 0); #else dpram_trigger_force_cp_crash(dpld); #endif return -EINVAL; } /* 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)); return -ENOMEM; } /* Read data from each DPRAM buffer */ dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); /* Calculate and set new out */ out += rcvd; if (out >= qsize) out -= qsize; set_rx_tail(dpld, dev, out); return rcvd; }
/* ret < 0 : error ret == 0 : no data ret > 0 : valid data */ static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; struct dpram_rxb *rxb; u8 __iomem *src = get_rx_buff(dpld, dev); u32 qsize = get_rx_buff_size(dpld, dev); u32 in; u32 out; u32 rcvd; struct mif_irq_map map; rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); if (rcvd <= 0) return rcvd; if (dev == IPC_FMT) { 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)); return -ENOMEM; } /* Read data from each DPRAM buffer */ dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); /* Calculate and set new out */ out += rcvd; if (out >= qsize) out -= qsize; set_rx_tail(dpld, dev, out); return rcvd; }
struct link_device *dpram_create_link_device(struct platform_device *pdev) { struct modem_data *mdm_data = NULL; struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; struct resource *res = NULL; resource_size_t res_size; struct modemlink_dpram_control *dpctl = NULL; unsigned long task_data; int ret = 0; int i = 0; int bsize; int qsize; /* Get the platform data */ mdm_data = (struct modem_data *)pdev->dev.platform_data; if (!mdm_data) { mif_info("ERR! mdm_data == NULL\n"); goto err; } mif_info("modem = %s\n", mdm_data->name); mif_info("link device = %s\n", mdm_data->link_name); if (!mdm_data->dpram_ctl) { mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); goto err; } dpctl = mdm_data->dpram_ctl; /* Alloc DPRAM link device structure */ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); if (!dpld) { mif_info("ERR! kzalloc dpld fail\n"); goto err; } ld = &dpld->ld; task_data = (unsigned long)dpld; /* Retrieve modem data and DPRAM control data from the modem data */ ld->mdm_data = mdm_data; ld->name = mdm_data->link_name; ld->ipc_version = mdm_data->ipc_version; /* Retrieve the most basic data for IPC from the modem data */ dpld->dpctl = dpctl; dpld->dp_type = dpctl->dp_type; if (mdm_data->ipc_version < SIPC_VER_50) { if (!dpctl->max_ipc_dev) { mif_info("ERR! no max_ipc_dev\n"); goto err; } ld->aligned = dpctl->aligned; dpld->max_ipc_dev = dpctl->max_ipc_dev; } else { ld->aligned = 1; dpld->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(mdm_data->modem_type); /* Retrieve DPRAM resource */ if (!dpctl->dp_base) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { mif_info("%s: ERR! platform_get_resource fail\n", ld->name); goto err; } res_size = resource_size(res); dpctl->dp_base = ioremap_nocache(res->start, res_size); dpctl->dp_size = res_size; } dpld->dp_base = dpctl->dp_base; dpld->dp_size = dpctl->dp_size; mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, dpld->dp_size); /* Initialize DPRAM map (physical map -> logical map) */ ret = dpram_table_init(dpld); if (ret < 0) { mif_info("%s: ERR! dpram_table_init fail (err %d)\n", ld->name, ret); goto err; } /* Disable IPC */ set_magic(dpld, 0); set_access(dpld, 0); dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; /* Initialize locks, completions, and bottom halves */ snprintf(dpld->wlock_name, DP_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->dump_start_complete); init_completion(&dpld->dump_recv_done); tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); if (dpld->ext_op && dpld->ext_op->dl_task) tasklet_init(&dpld->dl_tsk, dpld->ext_op->dl_task, task_data); /* Prepare RXB queue */ qsize = DPRAM_MAX_RXBQ_SIZE; for (i = 0; i < dpld->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_info("%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->dp_size, GFP_KERNEL); if (!dpld->buff) { mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } /* Retrieve DPRAM IRQ GPIO# */ dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; /* Retrieve DPRAM IRQ# */ if (!dpctl->dpram_irq) { dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); if (dpctl->dpram_irq < 0) { mif_info("%s: ERR! platform_get_irq_byname fail\n", ld->name); goto err; } } dpld->irq = dpctl->dpram_irq; /* Retrieve DPRAM IRQ flags */ if (!dpctl->dpram_irq_flags) dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); dpld->irq_flags = dpctl->dpram_irq_flags; /* Register DPRAM interrupt handler */ snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); ret = dpram_register_isr(dpld->irq, dpram_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; }
/* ret < 0 : error ret == 0 : no data ret > 0 : valid data */ static int dpram_ipc_recv_data_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; u8 __iomem *src = get_rx_buff(dpld, dev); u32 qsize = get_rx_buff_size(dpld, dev); u32 in; u32 out; u32 rcvd; int rest; u8 *frm; u8 *dst; unsigned int len; unsigned int pad; unsigned int tot; struct mif_irq_map map; rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); if (rcvd <= 0) return rcvd; if (dev == IPC_FMT) { set_dpram_map(dpld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } rest = rcvd; while (rest > 0) { frm = src + out; if (unlikely(!sipc5_start_valid(frm[0]))) { mif_err("%s: ERR! %s invalid start 0x%02X\n", ld->name, get_dev_name(dev), frm[0]); skb_queue_purge(&dpld->skb_rxq[dev]); return -EBADMSG; } len = sipc5_get_frame_sz16(frm); if (unlikely(len > rest)) { mif_err("%s: ERR! %s len %d > rest %d\n", ld->name, get_dev_name(dev), len, rest); skb_queue_purge(&dpld->skb_rxq[dev]); return -EBADMSG; } pad = sipc5_calc_padding_size(len); tot = len + pad; /* 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)); return -ENOMEM; } /* Read data from each DPRAM buffer */ dst = skb_put(skb, tot); dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); skb_trim(skb, len); iod->recv_skb(iod, ld, skb); /* Calculate and set new out */ rest -= tot; out += tot; if (out >= qsize) out -= qsize; } set_rx_tail(dpld, dev, out); return rcvd; }