1140 lines
27 KiB
C
1140 lines
27 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (C) 2021 Samsung Electronics.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_platform.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/usb/f_dm.h>
|
||
|
|
||
|
#include "direct_dm.h"
|
||
|
#include "modem_utils.h"
|
||
|
|
||
|
static struct direct_dm_ctrl *_dc;
|
||
|
|
||
|
static void direct_dm_rx_func(unsigned long arg);
|
||
|
|
||
|
/* RX timer */
|
||
|
static inline void direct_dm_start_rx_timer(struct direct_dm_ctrl *dc,
|
||
|
struct hrtimer *timer)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!dc->use_rx_timer) {
|
||
|
mif_err_limited("use_rx_timer is not set\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&dc->rx_timer_lock, flags);
|
||
|
if (!hrtimer_is_queued(timer)) {
|
||
|
ktime_t ktime = ktime_set(0, dc->rx_timer_period_msec * NSEC_PER_MSEC);
|
||
|
|
||
|
dc->stat.rx_timer_req++;
|
||
|
hrtimer_start(timer, ktime, HRTIMER_MODE_REL);
|
||
|
}
|
||
|
spin_unlock_irqrestore(&dc->rx_timer_lock, flags);
|
||
|
}
|
||
|
|
||
|
static enum hrtimer_restart direct_dm_rx_timer(struct hrtimer *timer)
|
||
|
{
|
||
|
mif_info("run rx func by timer\n");
|
||
|
|
||
|
_dc->stat.rx_timer_expire++;
|
||
|
|
||
|
if (_dc->use_rx_task)
|
||
|
tasklet_hi_schedule(&_dc->rx_task);
|
||
|
else
|
||
|
direct_dm_rx_func((unsigned long)_dc);
|
||
|
|
||
|
return HRTIMER_NORESTART;
|
||
|
}
|
||
|
|
||
|
/* RX func */
|
||
|
static int direct_dm_send_to_upper_layer(struct direct_dm_ctrl *dc,
|
||
|
struct direct_dm_desc *curr_desc, void *addr)
|
||
|
{
|
||
|
struct sk_buff *skb = NULL;
|
||
|
struct mem_link_device *mld;
|
||
|
int ch_id = 0;
|
||
|
|
||
|
if (!dc || !curr_desc || !addr) {
|
||
|
mif_err_limited("null addr\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
dc->stat.upper_layer_req_cnt++;
|
||
|
skb = dev_alloc_skb(curr_desc->length);
|
||
|
if (unlikely(!skb)) {
|
||
|
mif_err_limited("mem_alloc_skb() error pos:%d\n",
|
||
|
dc->curr_desc_pos);
|
||
|
dc->stat.err_upper_layer_req++;
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
skb_put(skb, curr_desc->length);
|
||
|
skb_copy_to_linear_data(skb, addr, curr_desc->length);
|
||
|
|
||
|
switch (dc->ld->protocol) {
|
||
|
case PROTOCOL_SIPC:
|
||
|
ch_id = SIPC_CH_ID_CPLOG1;
|
||
|
break;
|
||
|
case PROTOCOL_SIT:
|
||
|
ch_id = EXYNOS_CH_ID_CPLOG;
|
||
|
break;
|
||
|
default:
|
||
|
mif_err_limited("protocol error:%d\n", dc->ld->protocol);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
skbpriv(skb)->lnk_hdr = 0;
|
||
|
skbpriv(skb)->sipc_ch = ch_id;
|
||
|
skbpriv(skb)->iod = link_get_iod_with_channel(dc->ld, ch_id);
|
||
|
skbpriv(skb)->ld = dc->ld;
|
||
|
skbpriv(skb)->napi = NULL;
|
||
|
|
||
|
mld = to_mem_link_device(dc->ld);
|
||
|
mld->pass_skb_to_demux(mld, skb);
|
||
|
|
||
|
dc->desc_rgn[dc->curr_desc_pos].status &= ~BIT(DDM_DESC_S_DONE);
|
||
|
|
||
|
dc->curr_desc_pos = circ_new_ptr(dc->num_desc,
|
||
|
dc->curr_desc_pos, 1);
|
||
|
dc->curr_done_pos = circ_new_ptr(dc->num_desc,
|
||
|
dc->curr_done_pos, 1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int direct_dm_send_to_usb(struct direct_dm_ctrl *dc,
|
||
|
struct direct_dm_desc *curr_desc, void *addr)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!dc || !curr_desc || !addr) {
|
||
|
mif_err_limited("null addr\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
dc->stat.usb_req_cnt++;
|
||
|
ret = usb_dm_request(addr, curr_desc->length);
|
||
|
if (ret) {
|
||
|
mif_info_limited("usb_dm_request() ret:%d pos:%d\n",
|
||
|
ret, dc->curr_desc_pos);
|
||
|
|
||
|
dc->usb_req_failed = true;
|
||
|
dc->stat.err_usb_req++;
|
||
|
if (dc->use_rx_timer) {
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("start timer\n");
|
||
|
|
||
|
direct_dm_start_rx_timer(dc, &dc->rx_timer);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
dc->usb_req_failed = false;
|
||
|
|
||
|
dc->curr_desc_pos = circ_new_ptr(dc->num_desc,
|
||
|
dc->curr_desc_pos, 1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void direct_dm_rx_func(unsigned long arg)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = (struct direct_dm_ctrl *)arg;
|
||
|
unsigned long paddr;
|
||
|
void *addr;
|
||
|
int ret;
|
||
|
int i;
|
||
|
bool upper_layer_req = false;
|
||
|
int rcvd = 0;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&dc->rx_lock, flags);
|
||
|
|
||
|
upper_layer_req = dc->info_rgn->silent_log;
|
||
|
if (!upper_layer_req && !dc->usb_active) {
|
||
|
mif_info_limited("usb is not activated\n");
|
||
|
spin_unlock_irqrestore(&dc->rx_lock, flags);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < dc->num_desc; i++) {
|
||
|
struct direct_dm_desc curr_desc = dc->desc_rgn[dc->curr_desc_pos];
|
||
|
|
||
|
if (!(curr_desc.status & BIT(DDM_DESC_S_DONE))) {
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("DDM_DESC_S_DONE is not set %d 0x%llx\n",
|
||
|
dc->curr_desc_pos, curr_desc.cp_buff_paddr);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (curr_desc.status & BIT(DDM_DESC_S_TOUT)) {
|
||
|
mif_err_limited("DDM_DESC_S_TOUT is set %d 0x%llx\n",
|
||
|
dc->curr_desc_pos, curr_desc.cp_buff_paddr);
|
||
|
dc->stat.err_desc_tout++;
|
||
|
}
|
||
|
|
||
|
/* TODO: support compressed log of DM log mover H/W */
|
||
|
if (curr_desc.status & BIT(DDM_DESC_S_COMPRESSED))
|
||
|
mif_err_limited("DDM_DESC_S_COMPRESSED is set %d 0x%llx\n",
|
||
|
dc->curr_desc_pos, curr_desc.cp_buff_paddr);
|
||
|
|
||
|
if (!curr_desc.length || (curr_desc.length > dc->max_packet_size)) {
|
||
|
mif_err_limited("length error:%d\n", curr_desc.length);
|
||
|
dc->stat.err_length++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
paddr = curr_desc.cp_buff_paddr -
|
||
|
dc->buff_rgn_offset - dc->cp_ddm_pbase + dc->buff_pbase;
|
||
|
addr = phys_to_virt(paddr);
|
||
|
|
||
|
if (dc->buff_rgn_cached && !dc->hw_iocc)
|
||
|
dma_sync_single_for_cpu(dc->dev, paddr,
|
||
|
dc->max_packet_size, DMA_FROM_DEVICE);
|
||
|
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("pos:%d len:%d a:%pK/0x%lx/0x%llx upper:%d done:%d\n",
|
||
|
dc->curr_desc_pos, curr_desc.length,
|
||
|
addr, paddr, curr_desc.cp_buff_paddr,
|
||
|
upper_layer_req, dc->desc_rgn[dc->curr_desc_pos].status);
|
||
|
|
||
|
if (unlikely(upper_layer_req)) {
|
||
|
ret = direct_dm_send_to_upper_layer(dc, &curr_desc, addr);
|
||
|
if (ret)
|
||
|
break;
|
||
|
|
||
|
rcvd++;
|
||
|
} else {
|
||
|
ret = direct_dm_send_to_usb(dc, &curr_desc, addr);
|
||
|
if (ret)
|
||
|
break;
|
||
|
|
||
|
rcvd++;
|
||
|
|
||
|
if (dc->curr_desc_pos == dc->curr_done_pos) {
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("prev desc is enqueued:%d\n",
|
||
|
dc->curr_done_pos);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("rcvd:%d\n", rcvd);
|
||
|
|
||
|
spin_unlock_irqrestore(&dc->rx_lock, flags);
|
||
|
}
|
||
|
|
||
|
static void direct_dm_run_rx_func(struct direct_dm_ctrl *dc)
|
||
|
{
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (dc->use_rx_timer && hrtimer_active(&dc->rx_timer))
|
||
|
hrtimer_cancel(&dc->rx_timer);
|
||
|
|
||
|
if (dc->use_rx_task)
|
||
|
tasklet_hi_schedule(&dc->rx_task);
|
||
|
else
|
||
|
direct_dm_rx_func((unsigned long)dc);
|
||
|
}
|
||
|
|
||
|
/* IRQ handler */
|
||
|
static irqreturn_t direct_dm_irq_handler(int irq, void *arg)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = (struct direct_dm_ctrl *)arg;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
direct_dm_run_rx_func(dc);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
/* Callback from USB driver */
|
||
|
static void direct_dm_usb_active_noti(void *arg)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = (struct direct_dm_ctrl *)arg;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&dc->rx_lock, flags);
|
||
|
|
||
|
mif_info("usb is activated\n");
|
||
|
dc->usb_active = true;
|
||
|
|
||
|
spin_unlock_irqrestore(&dc->rx_lock, flags);
|
||
|
|
||
|
if (dc->usb_req_failed) {
|
||
|
mif_info("run rx func\n");
|
||
|
direct_dm_run_rx_func(dc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void direct_dm_usb_disable_noti(void *arg)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = (struct direct_dm_ctrl *)arg;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&dc->rx_lock, flags);
|
||
|
|
||
|
mif_info("usb is deactivated\n");
|
||
|
dc->usb_active = false;
|
||
|
dc->usb_req_failed = true;
|
||
|
|
||
|
spin_unlock_irqrestore(&dc->rx_lock, flags);
|
||
|
|
||
|
if (dc->use_rx_timer && hrtimer_active(&dc->rx_timer)) {
|
||
|
mif_info("cancel rx timer\n");
|
||
|
hrtimer_cancel(&dc->rx_timer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void direct_dm_usb_completion_noti(void *addr, int length, void *arg)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = (struct direct_dm_ctrl *)arg;
|
||
|
unsigned long paddr;
|
||
|
u32 pos;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err_limited("dc is null\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dc->stat.usb_complete_cnt++;
|
||
|
|
||
|
paddr = virt_to_phys(addr);
|
||
|
if ((paddr < dc->buff_pbase) ||
|
||
|
(paddr >= (dc->buff_pbase + dc->buff_rgn_size))) {
|
||
|
mif_err("addr error:%pK 0x%lx 0x%lx 0x%x\n",
|
||
|
addr, paddr, dc->buff_pbase, dc->buff_rgn_offset);
|
||
|
dc->stat.err_usb_complete++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (dc->buff_rgn_cached && !dc->hw_iocc)
|
||
|
dma_sync_single_for_device(dc->dev, paddr,
|
||
|
dc->max_packet_size, DMA_FROM_DEVICE);
|
||
|
|
||
|
spin_lock_irqsave(&dc->rx_lock, flags);
|
||
|
pos = (paddr - dc->buff_pbase) / dc->max_packet_size;
|
||
|
dc->desc_rgn[pos].status &= ~BIT(DDM_DESC_S_DONE);
|
||
|
|
||
|
if (dc->curr_done_pos != pos)
|
||
|
mif_err("pos error! pos:%d done:%d len:%d a:%pK/0x%lx\n",
|
||
|
pos, dc->curr_done_pos, length, addr, paddr);
|
||
|
|
||
|
if ((length <= 0) || (length > dc->max_packet_size)) {
|
||
|
mif_err_limited("length error:%d\n", length);
|
||
|
dc->usb_req_failed = true;
|
||
|
}
|
||
|
|
||
|
if (unlikely(dc->enable_debug))
|
||
|
mif_info("pos:%d done:%d len:%d a:%pK/0x%lx\n",
|
||
|
pos, dc->curr_done_pos, length, addr, paddr);
|
||
|
|
||
|
dc->curr_done_pos = circ_new_ptr(dc->num_desc,
|
||
|
dc->curr_done_pos, 1);
|
||
|
|
||
|
spin_unlock_irqrestore(&dc->rx_lock, flags);
|
||
|
}
|
||
|
|
||
|
/* sysfs */
|
||
|
static ssize_t ctrl_status_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"version:%d shm_rgn_index:%d\n",
|
||
|
dc->version, dc->shm_rgn_index);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"hw_iocc:%d info_desc_rgn_cached:%d buff_rgn_cached:%d\n",
|
||
|
dc->hw_iocc, dc->info_desc_rgn_cached, dc->buff_rgn_cached);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"info_rgn_offset:0x%08x info_rgn_size:0x%08x\n",
|
||
|
dc->info_rgn_offset, dc->info_rgn_size);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"desc_rgn_offset:0x%08x desc_rgn_size:0x%08x num_desc:%d\n",
|
||
|
dc->desc_rgn_offset, dc->desc_rgn_size, dc->num_desc);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"buff_rgn_offset:0x%08x buff_rgn_size:0x%08x\n",
|
||
|
dc->buff_rgn_offset, dc->buff_rgn_size);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"max_packet_size:%d usb_req_num:%d irq_index:%d\n",
|
||
|
dc->max_packet_size, dc->usb_req_num, dc->irq_index);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"cp_ddm_pbase:0x%08x\n", dc->cp_ddm_pbase);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"curr_desc_pos:%d\n", dc->curr_desc_pos);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"curr_done_pos:%d\n", dc->curr_done_pos);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"use_rx_task:%d\n", dc->use_rx_task);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"use_rx_timer:%d\n", dc->use_rx_timer);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"rx_timer_period_msec:%d\n", dc->rx_timer_period_msec);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"enable_debug:%d\n", dc->enable_debug);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"usb_req_failed:%d\n", dc->usb_req_failed);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t stat_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"err_desc_tout:%lld\n", dc->stat.err_desc_tout);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"err_length:%lld\n", dc->stat.err_length);
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "\n");
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"usb_req_cnt:%lld\n", dc->stat.usb_req_cnt);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"err_usb_req:%lld\n", dc->stat.err_usb_req);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"usb_complete_cnt:%lld\n", dc->stat.usb_complete_cnt);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"err_usb_complete:%lld\n", dc->stat.err_usb_complete);
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "\n");
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"upper_layer_req_cnt:%lld\n", dc->stat.upper_layer_req_cnt);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"err_upper_layer_req:%lld\n", dc->stat.err_upper_layer_req);
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "\n");
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"rx_timer_req:%lld\n", dc->stat.rx_timer_req);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"rx_timer_expire:%lld\n", dc->stat.rx_timer_expire);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t info_rgn_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"version:%d max_packet_size:%d\n",
|
||
|
dc->info_rgn->version, dc->info_rgn->max_packet_size);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"silent_log:%d\n",
|
||
|
dc->info_rgn->silent_log);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"cp_desc_pbase:0x%llx cp_buff_pbase:0x%llx\n",
|
||
|
dc->info_rgn->cp_desc_pbase, dc->info_rgn->cp_buff_pbase);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t desc_rgn_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
int i;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"curr_desc_pos:%d\n", dc->curr_desc_pos);
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"curr_done_pos:%d\n", dc->curr_done_pos);
|
||
|
|
||
|
for (i = 0; i < dc->num_desc; i++) {
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"%d 0x%llx 0x%x 0x%x %d\n",
|
||
|
i, dc->desc_rgn[i].cp_buff_paddr, dc->desc_rgn[i].control,
|
||
|
dc->desc_rgn[i].status, dc->desc_rgn[i].length);
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t enable_debug_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int val;
|
||
|
int ret;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
val ? (dc->enable_debug = true) : (dc->enable_debug = false);
|
||
|
mif_info("enable_debug:%d\n", dc->enable_debug);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t enable_debug_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"enable_debug:%d\n",
|
||
|
dc->enable_debug);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t use_rx_task_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int val;
|
||
|
int ret;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
val ? (dc->use_rx_task = true) : (dc->use_rx_task = false);
|
||
|
mif_info("use_rx_task:%d\n", dc->use_rx_task);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t use_rx_task_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"use_rx_task:%d\n",
|
||
|
dc->use_rx_task);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t use_rx_timer_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int val;
|
||
|
int ret;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
val ? (dc->use_rx_timer = true) : (dc->use_rx_timer = false);
|
||
|
mif_info("use_rx_timer:%d\n", dc->use_rx_timer);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t use_rx_timer_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"use_rx_timer:%d\n",
|
||
|
dc->use_rx_timer);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t rx_timer_period_msec_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int val;
|
||
|
int ret;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
if (!dc->use_rx_timer) {
|
||
|
mif_err("use_rx_timer is not set\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
dc->rx_timer_period_msec = val;
|
||
|
mif_info("rx_timer_period_msec:%d\n", dc->rx_timer_period_msec);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t rx_timer_period_msec_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
ssize_t count = 0;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
if (!dc->use_rx_timer) {
|
||
|
mif_err("use_rx_timer is not set\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
count += scnprintf(&buf[count], PAGE_SIZE - count,
|
||
|
"rx_timer_period_msec:%d\n",
|
||
|
dc->rx_timer_period_msec);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static u32 _test_dm_desc_pos;
|
||
|
static ssize_t test_dm_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
void *addr;
|
||
|
int val;
|
||
|
int ret;
|
||
|
int i;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (!val || (val > dc->num_desc)) {
|
||
|
mif_err("val error:%d\n", val);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < val; i++) {
|
||
|
addr = dc->desc_rgn[_test_dm_desc_pos].cp_buff_paddr -
|
||
|
dc->buff_rgn_offset - dc->cp_ddm_pbase + dc->buff_vbase;
|
||
|
|
||
|
mif_info("pos:%d a:%pK/0x%llx\n",
|
||
|
_test_dm_desc_pos, addr,
|
||
|
dc->desc_rgn[_test_dm_desc_pos].cp_buff_paddr);
|
||
|
|
||
|
if (dc->desc_rgn[_test_dm_desc_pos].status & BIT(DDM_DESC_S_DONE)) {
|
||
|
mif_err("DDM_DESC_S_DONE is already set. pos:%d\n",
|
||
|
_test_dm_desc_pos);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
memset(addr, _test_dm_desc_pos, dc->max_packet_size);
|
||
|
dc->desc_rgn[_test_dm_desc_pos].length = dc->max_packet_size;
|
||
|
dc->desc_rgn[_test_dm_desc_pos].status |= BIT(DDM_DESC_S_DONE);
|
||
|
|
||
|
if (dc->desc_rgn[_test_dm_desc_pos].control & BIT(DDM_DESC_C_END))
|
||
|
_test_dm_desc_pos = 0;
|
||
|
else
|
||
|
_test_dm_desc_pos++;
|
||
|
}
|
||
|
|
||
|
direct_dm_irq_handler(dc->irq_index, dc);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t test_silent_log_store(struct device *dev,
|
||
|
struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int val;
|
||
|
int ret;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
ret = kstrtoint(buf, 0, &val);
|
||
|
if (ret) {
|
||
|
mif_err("kstrtoint() error:%d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
val ? (dc->info_rgn->silent_log = 1) :
|
||
|
(dc->info_rgn->silent_log = 0);
|
||
|
mif_info("silent_log:%d\n", dc->info_rgn->silent_log);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static DEVICE_ATTR_RO(ctrl_status);
|
||
|
static DEVICE_ATTR_RO(stat);
|
||
|
static DEVICE_ATTR_RO(info_rgn);
|
||
|
static DEVICE_ATTR_RO(desc_rgn);
|
||
|
static DEVICE_ATTR_RW(enable_debug);
|
||
|
static DEVICE_ATTR_RW(use_rx_task);
|
||
|
static DEVICE_ATTR_RW(use_rx_timer);
|
||
|
static DEVICE_ATTR_RW(rx_timer_period_msec);
|
||
|
static DEVICE_ATTR_WO(test_dm);
|
||
|
static DEVICE_ATTR_WO(test_silent_log);
|
||
|
|
||
|
static struct attribute *direct_dm_attrs[] = {
|
||
|
&dev_attr_ctrl_status.attr,
|
||
|
&dev_attr_stat.attr,
|
||
|
&dev_attr_info_rgn.attr,
|
||
|
&dev_attr_desc_rgn.attr,
|
||
|
&dev_attr_enable_debug.attr,
|
||
|
&dev_attr_use_rx_task.attr,
|
||
|
&dev_attr_use_rx_timer.attr,
|
||
|
&dev_attr_rx_timer_period_msec.attr,
|
||
|
&dev_attr_test_dm.attr,
|
||
|
&dev_attr_test_silent_log.attr,
|
||
|
NULL,
|
||
|
};
|
||
|
ATTRIBUTE_GROUPS(direct_dm);
|
||
|
|
||
|
/* Initialize */
|
||
|
int direct_dm_init(struct link_device *ld)
|
||
|
{
|
||
|
struct direct_dm_ctrl *dc = _dc;
|
||
|
int i;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
dc->ld = ld;
|
||
|
|
||
|
dc->info_rgn->version = dc->version;
|
||
|
dc->info_rgn->max_packet_size = dc->max_packet_size;
|
||
|
dc->info_rgn->cp_desc_pbase = dc->cp_ddm_pbase + dc->desc_rgn_offset;
|
||
|
dc->info_rgn->cp_buff_pbase = dc->cp_ddm_pbase + dc->buff_rgn_offset;
|
||
|
mif_info("version:%d max_packet_size:%d\n",
|
||
|
dc->info_rgn->version, dc->info_rgn->max_packet_size);
|
||
|
mif_info("cp_desc_pbase:0x%llx cp_buff_pbase:0x%llx\n",
|
||
|
dc->info_rgn->cp_desc_pbase, dc->info_rgn->cp_buff_pbase);
|
||
|
|
||
|
memset(dc->desc_vbase, 0, dc->desc_rgn_size);
|
||
|
for (i = 0; i < dc->num_desc; i++) {
|
||
|
dc->desc_rgn[i].cp_buff_paddr = dc->cp_ddm_pbase +
|
||
|
dc->buff_rgn_offset + (dc->max_packet_size * i);
|
||
|
|
||
|
dc->desc_rgn[i].control |= BIT(DDM_DESC_C_INT);
|
||
|
|
||
|
if (i == (dc->num_desc - 1))
|
||
|
dc->desc_rgn[i].control |= BIT(DDM_DESC_C_END);
|
||
|
}
|
||
|
|
||
|
if (unlikely(dc->enable_debug)) {
|
||
|
for (i = 0; i < dc->num_desc; i++) {
|
||
|
mif_info("%d a:0x%llx c:0x%x s:0x%x l:%d\n",
|
||
|
i, dc->desc_rgn[i].cp_buff_paddr, dc->desc_rgn[i].control,
|
||
|
dc->desc_rgn[i].status, dc->desc_rgn[i].length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memset(dc->buff_vbase, 0, dc->buff_rgn_size);
|
||
|
|
||
|
dc->curr_desc_pos = 0;
|
||
|
dc->curr_done_pos = 0;
|
||
|
_test_dm_desc_pos = 0;
|
||
|
|
||
|
dc->usb_req_failed = false;
|
||
|
|
||
|
memset(&dc->stat, 0, sizeof(dc->stat));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(direct_dm_init);
|
||
|
|
||
|
int direct_dm_deinit(void)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(direct_dm_deinit);
|
||
|
|
||
|
/* Create */
|
||
|
static int direct_dm_setup_region(struct direct_dm_ctrl *dc)
|
||
|
{
|
||
|
unsigned long cp_pbase;
|
||
|
unsigned long ddm_pbase;
|
||
|
u32 ddm_rgn_size;
|
||
|
|
||
|
if (!dc) {
|
||
|
mif_err("dc is null\n");
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
cp_pbase = cp_shmem_get_base(0, SHMEM_CP);
|
||
|
if (!cp_pbase) {
|
||
|
mif_err("cp_pbase is null\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
ddm_pbase = cp_shmem_get_base(0, dc->shm_rgn_index);
|
||
|
if (!ddm_pbase) {
|
||
|
mif_err("ddm_pbase is null\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
ddm_rgn_size = cp_shmem_get_size(0, dc->shm_rgn_index);
|
||
|
if (!ddm_rgn_size) {
|
||
|
mif_err("ddm_rgn_size is null\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
mif_info("cp_pbase:0x%lx ddm_pbase:0x%lx ddm_rgn_size:0x%08x\n",
|
||
|
cp_pbase, ddm_pbase, ddm_rgn_size);
|
||
|
|
||
|
/* TODO: support slim modem */
|
||
|
dc->cp_ddm_pbase = ddm_pbase - cp_pbase + CP_CPU_BASE_ADDRESS;
|
||
|
mif_info("cp_ddm_pbase:0x%08x\n", dc->cp_ddm_pbase);
|
||
|
|
||
|
if (dc->info_desc_rgn_cached) {
|
||
|
dc->info_vbase = phys_to_virt(ddm_pbase + dc->info_rgn_offset);
|
||
|
dc->desc_vbase = phys_to_virt(ddm_pbase + dc->desc_rgn_offset);
|
||
|
if (!dc->hw_iocc) {
|
||
|
mif_err("cached region is not supported yet without hw_iocc\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
} else {
|
||
|
dc->info_vbase = cp_shmem_get_nc_region(
|
||
|
ddm_pbase + dc->info_rgn_offset,
|
||
|
dc->info_rgn_size + dc->desc_rgn_size);
|
||
|
if (!dc->info_vbase) {
|
||
|
mif_err("dc->info_base error\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
dc->desc_vbase = dc->info_vbase + dc->info_rgn_size;
|
||
|
}
|
||
|
memset(dc->info_vbase, 0, dc->info_rgn_size + dc->desc_rgn_size);
|
||
|
mif_info("info_rgn_size:0x%08x desc_rgn_size:0x%08x\n",
|
||
|
dc->info_rgn_size, dc->desc_rgn_size);
|
||
|
|
||
|
dc->buff_rgn_size = ddm_rgn_size -
|
||
|
(dc->info_rgn_size + dc->desc_rgn_size);
|
||
|
dc->buff_pbase = ddm_pbase + dc->buff_rgn_offset;
|
||
|
if (dc->buff_rgn_cached) {
|
||
|
dc->buff_vbase = phys_to_virt(dc->buff_pbase);
|
||
|
} else {
|
||
|
dc->buff_vbase = cp_shmem_get_nc_region(
|
||
|
dc->buff_pbase, dc->buff_rgn_size);
|
||
|
if (!dc->buff_vbase) {
|
||
|
mif_err("dc->buff_vbase error\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
}
|
||
|
memset(dc->buff_vbase, 0, dc->buff_rgn_size);
|
||
|
dc->num_desc = dc->buff_rgn_size / dc->max_packet_size;
|
||
|
if (dc->buff_rgn_cached && !dc->hw_iocc)
|
||
|
dma_sync_single_for_device(dc->dev, dc->buff_pbase,
|
||
|
dc->buff_rgn_size, DMA_FROM_DEVICE);
|
||
|
mif_info("buff_rgn_size:0x%08x num_desc:%d\n",
|
||
|
dc->buff_rgn_size, dc->num_desc);
|
||
|
|
||
|
dc->info_rgn = (struct direct_dm_info_rgn *)dc->info_vbase;
|
||
|
dc->desc_rgn = (struct direct_dm_desc *)dc->desc_vbase;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int direct_dm_register_irq(struct direct_dm_ctrl *dc)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = cp_mbox_register_handler(dc->irq_index, 0,
|
||
|
direct_dm_irq_handler, dc);
|
||
|
if (ret) {
|
||
|
mif_err("cp_mbox_register_handler() error:%d\n", ret);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int direct_dm_read_dt(struct device_node *np,
|
||
|
struct direct_dm_ctrl *dc)
|
||
|
{
|
||
|
mif_dt_read_u32(np, "version", dc->version);
|
||
|
mif_dt_read_u32(np, "shm_rgn_index", dc->shm_rgn_index);
|
||
|
mif_dt_read_u32(np, "hw_iocc", dc->hw_iocc);
|
||
|
mif_dt_read_u32(np, "info_desc_rgn_cached", dc->info_desc_rgn_cached);
|
||
|
mif_dt_read_u32(np, "buff_rgn_cached", dc->buff_rgn_cached);
|
||
|
|
||
|
mif_dt_read_u32(np, "info_rgn_offset", dc->info_rgn_offset);
|
||
|
mif_dt_read_u32(np, "info_rgn_size", dc->info_rgn_size);
|
||
|
mif_dt_read_u32(np, "desc_rgn_offset", dc->desc_rgn_offset);
|
||
|
mif_dt_read_u32(np, "desc_rgn_size", dc->desc_rgn_size);
|
||
|
mif_dt_read_u32(np, "buff_rgn_offset", dc->buff_rgn_offset);
|
||
|
|
||
|
mif_dt_read_u32(np, "max_packet_size", dc->max_packet_size);
|
||
|
mif_dt_read_u32(np, "usb_req_num", dc->usb_req_num);
|
||
|
mif_dt_read_u32(np, "irq_index", dc->irq_index);
|
||
|
|
||
|
mif_dt_read_bool(np, "use_rx_task", dc->use_rx_task);
|
||
|
mif_dt_read_bool(np, "use_rx_timer", dc->use_rx_timer);
|
||
|
mif_dt_read_u32(np, "rx_timer_period_msec", dc->rx_timer_period_msec);
|
||
|
|
||
|
mif_info("version:%d shm_rgn_index:%d\n",
|
||
|
dc->version, dc->shm_rgn_index);
|
||
|
mif_info("hw_iocc:%d info_desc_rgn_cached:%d buff_rgn_cached:%d\n",
|
||
|
dc->hw_iocc, dc->info_desc_rgn_cached, dc->buff_rgn_cached);
|
||
|
|
||
|
mif_info("info_rgn_offset:0x%08x info_rgn_size:0x%08x\n",
|
||
|
dc->info_rgn_offset, dc->info_rgn_size);
|
||
|
mif_info("desc_rgn_offset:0x%08x desc_rgn_size:0x%08x\n",
|
||
|
dc->desc_rgn_offset, dc->desc_rgn_size);
|
||
|
mif_info("buff_rgn_offset:0x%08x\n", dc->buff_rgn_offset);
|
||
|
|
||
|
mif_info("max_packet_size:%d usb_req_num:%d irq_index:%d\n",
|
||
|
dc->max_packet_size, dc->usb_req_num, dc->irq_index);
|
||
|
mif_info("use_rx_timer:%d rx_timer_period_msec:%d\n",
|
||
|
dc->use_rx_timer, dc->rx_timer_period_msec);
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int direct_dm_create(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device *dev = &pdev->dev;
|
||
|
struct device_node *np = dev->of_node;
|
||
|
struct direct_dm_ctrl *dc = NULL;
|
||
|
int ret;
|
||
|
|
||
|
mif_info("+++\n");
|
||
|
|
||
|
if (!np) {
|
||
|
mif_err("of_node is null\n");
|
||
|
ret = -EINVAL;
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
_dc = devm_kzalloc(dev, sizeof(struct direct_dm_ctrl), GFP_KERNEL);
|
||
|
if (!_dc) {
|
||
|
mif_err_limited("devm_kzalloc() error\n");
|
||
|
ret = -ENOMEM;
|
||
|
goto err;
|
||
|
}
|
||
|
dc = _dc;
|
||
|
|
||
|
dc->dev = dev;
|
||
|
|
||
|
ret = direct_dm_read_dt(np, dc);
|
||
|
if (ret) {
|
||
|
mif_err("direct_dm_read_dt() error:%d\n", ret);
|
||
|
goto err_setup;
|
||
|
}
|
||
|
|
||
|
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
|
||
|
|
||
|
ret = direct_dm_register_irq(dc);
|
||
|
if (ret) {
|
||
|
mif_err("direct_dm_register_irq() error:%d\n", ret);
|
||
|
goto err_setup;
|
||
|
}
|
||
|
|
||
|
ret = direct_dm_setup_region(dc);
|
||
|
if (ret) {
|
||
|
mif_err("direct_dm_setup_region() error:%d\n", ret);
|
||
|
goto err_setup;
|
||
|
}
|
||
|
|
||
|
ret = init_dm_direct_path(dc->usb_req_num,
|
||
|
direct_dm_usb_completion_noti,
|
||
|
direct_dm_usb_active_noti,
|
||
|
direct_dm_usb_disable_noti,
|
||
|
(void *)dc);
|
||
|
if (ret) {
|
||
|
mif_err("init_dm_direct_path() error:%d\n", ret);
|
||
|
goto err_setup;
|
||
|
}
|
||
|
|
||
|
spin_lock_init(&dc->rx_lock);
|
||
|
|
||
|
tasklet_init(&dc->rx_task, direct_dm_rx_func, (unsigned long)dc);
|
||
|
|
||
|
spin_lock_init(&dc->rx_timer_lock);
|
||
|
hrtimer_init(&dc->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||
|
dc->rx_timer.function = direct_dm_rx_timer;
|
||
|
|
||
|
dev_set_drvdata(dev, dc);
|
||
|
|
||
|
ret = sysfs_create_groups(&dev->kobj, direct_dm_groups);
|
||
|
if (ret != 0) {
|
||
|
mif_err("sysfs_create_group() error:%d\n", ret);
|
||
|
goto err_setup;
|
||
|
}
|
||
|
|
||
|
mif_info("---\n");
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_setup:
|
||
|
devm_kfree(dev, dc);
|
||
|
dc = NULL;
|
||
|
|
||
|
err:
|
||
|
panic("Direct DM driver probe failed\n");
|
||
|
mif_err("xxx\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(direct_dm_create);
|
||
|
|
||
|
/* Platform driver */
|
||
|
static int direct_dm_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
return direct_dm_create(pdev);
|
||
|
}
|
||
|
|
||
|
static int direct_dm_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int direct_dm_suspend(struct device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int direct_dm_resume(struct device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct dev_pm_ops direct_dm_pm_ops = {
|
||
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(direct_dm_suspend, direct_dm_resume)
|
||
|
};
|
||
|
|
||
|
static const struct of_device_id direct_dm_dt_match[] = {
|
||
|
{ .compatible = "samsung,cpif-direct-dm", },
|
||
|
{},
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, direct_dm_dt_match);
|
||
|
|
||
|
static struct platform_driver direct_dm_driver = {
|
||
|
.probe = direct_dm_probe,
|
||
|
.remove = direct_dm_remove,
|
||
|
.driver = {
|
||
|
.name = "direct_dm",
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = of_match_ptr(direct_dm_dt_match),
|
||
|
.pm = &direct_dm_pm_ops,
|
||
|
.suppress_bind_attrs = true,
|
||
|
},
|
||
|
};
|
||
|
module_platform_driver(direct_dm_driver);
|
||
|
|
||
|
MODULE_LICENSE("GPL");
|
||
|
MODULE_DESCRIPTION("Samsung direct DM driver");
|