mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 07:18:51 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
100
drivers/misc/modem_v1/Kconfig
Normal file
100
drivers/misc/modem_v1/Kconfig
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
menuconfig SEC_SIPC_MODEM_IF
|
||||
bool "Samsung Mobile SIPC Modem Interface"
|
||||
default n
|
||||
---help---
|
||||
Samsung Modem Interface Driver
|
||||
|
||||
if SEC_SIPC_MODEM_IF
|
||||
|
||||
config SEC_MODEM_SS310AP
|
||||
bool "Exynos7580 and SS310 feature"
|
||||
select UMTS_MODEM_SS310AP
|
||||
select LINK_DEVICE_SHMEM
|
||||
select CP_SECURE_BOOT
|
||||
default y
|
||||
|
||||
menu "Configuration Description"
|
||||
config BOOT_DEVICE_SPI
|
||||
bool "boot device: SPI"
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_MEMORY
|
||||
bool "Memory-type (i.e. shared memory) interface medium"
|
||||
default n
|
||||
|
||||
config LINK_POWER_MANAGEMENT
|
||||
bool "Power management (PM) for a link device"
|
||||
default n
|
||||
|
||||
config LINK_POWER_MANAGEMENT_WITH_FSM
|
||||
bool "FSM-based Power management (PM) for a link device"
|
||||
depends on LINK_POWER_MANAGEMENT
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_WITH_SBD_ARCH
|
||||
bool "Link device with the SBD architecture from MIPI-LLI"
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_NAPI
|
||||
bool "Use Rx polling (NAPI)"
|
||||
default n
|
||||
help
|
||||
NAPI is a new driver API designed to reduce CPU and interrupt load
|
||||
when the driver is receiving lots of packets.
|
||||
|
||||
config LINK_DEVICE_C2C
|
||||
bool "Pseudo shared-memory with chip-to-chip (C2C) interface"
|
||||
select LINK_DEVICE_MEMORY
|
||||
select LINK_POWER_MANAGEMENT
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_LLI
|
||||
bool "link device: MIPI-LLI"
|
||||
select MIPI_LLI
|
||||
select LINK_DEVICE_MEMORY
|
||||
select LINK_POWER_MANAGEMENT
|
||||
select LINK_DEVICE_WITH_SBD_ARCH
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_SHMEM
|
||||
bool "Real system-level shared-memory on a system bus"
|
||||
default n
|
||||
|
||||
config LINK_DEVICE_HSIC
|
||||
bool "link device: HSIC"
|
||||
default n
|
||||
|
||||
config LTE_MODEM_XMM7260
|
||||
bool "modem chip : IMC XMM7260"
|
||||
default n
|
||||
|
||||
config UMTS_MODEM_SS310AP
|
||||
bool "modem chip : SS310 with exynos7580"
|
||||
default n
|
||||
|
||||
config DEBUG_PKTLOG
|
||||
bool "IPC pcap logging feature"
|
||||
default n
|
||||
|
||||
config LINK_CONTROL_MSG_IOSM
|
||||
bool "Control Message Type: IPC Over Shared Memory (IOSM)"
|
||||
default n
|
||||
|
||||
config CP_SECURE_BOOT
|
||||
bool "Support CP Secure Boot"
|
||||
default n
|
||||
|
||||
config SHARE_MIF_FREQ_INFO
|
||||
bool "Share MIF frequency information with CP"
|
||||
default y
|
||||
|
||||
config GPIO_DS_DETECT
|
||||
bool "Support GPIO DS Detect"
|
||||
default n
|
||||
|
||||
config FREE_CP_RSVD_MEMORY
|
||||
bool "Free CP reserved memory after CP goes to sleep for Wifi model"
|
||||
depends on LINK_DEVICE_SHMEM
|
||||
default n
|
||||
endmenu
|
||||
endif
|
||||
23
drivers/misc/modem_v1/Makefile
Normal file
23
drivers/misc/modem_v1/Makefile
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Makefile of modem_v1
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/misc/modem_v1
|
||||
|
||||
obj-y += modem_main.o modem_variation.o modem_io_device.o modem_utils.o
|
||||
|
||||
obj-$(CONFIG_LINK_DEVICE_MEMORY) += link_device_memory_main.o \
|
||||
link_device_memory_snapshot.o \
|
||||
link_device_memory_command.o \
|
||||
link_device_memory_flow_control.o \
|
||||
link_device_memory_debug.o
|
||||
obj-$(CONFIG_LINK_DEVICE_SHMEM) += link_device_shmem.o \
|
||||
link_device_memory_snapshot.o \
|
||||
link_device_memory_flow_control.o \
|
||||
link_device_memory_debug.o \
|
||||
link_device_memory_sbd.o \
|
||||
modem_notifier.o
|
||||
|
||||
obj-$(CONFIG_UMTS_MODEM_SS310AP) += modem_ctrl_ss310ap.o
|
||||
|
||||
obj-$(CONFIG_LINK_CONTROL_MSG_IOSM) += link_ctrlmsg_iosm.o
|
||||
obj-$(CONFIG_LINK_CONTROL_MSG_COMMAND) += link_ctrlmsg_command.o
|
||||
obj-$(CONFIG_DEBUG_PKTLOG) += modem_pktlog.o
|
||||
310
drivers/misc/modem_v1/include/circ_queue.h
Normal file
310
drivers/misc/modem_v1/include/circ_queue.h
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/**
|
||||
@file circ_queue.h
|
||||
@brief header file for general circular queue operations
|
||||
@date 2014/02/18
|
||||
@author Hankook Jang (hankook.jang@samsung.com)
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_CIRCULAR_QUEUE_H__
|
||||
#define __MODEM_CIRCULAR_QUEUE_H__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define GROUP_CIRC_QUEUE
|
||||
|
||||
#ifdef GROUP_CIRC_QUEUE
|
||||
/**
|
||||
@defgroup group_circ_queue Circular Queue
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief the structure for a circular queue in a memory-type interface
|
||||
*/
|
||||
struct circ_queue {
|
||||
spinlock_t lock;
|
||||
|
||||
/**
|
||||
* the flag and counter for checking busy status of a circualr queue
|
||||
*/
|
||||
atomic_t busy;
|
||||
|
||||
/**
|
||||
* the start address of the data buffer in a circualr queue
|
||||
*/
|
||||
void __iomem *buff;
|
||||
|
||||
/**
|
||||
* the size of the data buffer in a circular queue
|
||||
*/
|
||||
unsigned int size;
|
||||
|
||||
/**
|
||||
* the pointer to the "HEAD (IN)" variable that contains a byte offset
|
||||
* from @b @@buff
|
||||
*/
|
||||
void __iomem *head;
|
||||
|
||||
/**
|
||||
* the pointer to the "TAIL (OUT)" variable that contains a byte offset
|
||||
* from @b @@buff
|
||||
*/
|
||||
void __iomem *tail;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief get the start address of the data buffer in a circular queue
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
|
||||
@return the start address of the data buffer in the @e @@q
|
||||
*/
|
||||
static inline char *get_buff(struct circ_queue *q)
|
||||
{
|
||||
return q->buff;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the size of the data buffer in a circular queue
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
|
||||
@return the size of the data buffer in the @e @@q
|
||||
*/
|
||||
static inline unsigned int get_size(struct circ_queue *q)
|
||||
{
|
||||
return q->size;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the "HEAD (IN)" pointer value of a circular queue
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
|
||||
@return the "HEAD (IN)" pointer value of the @e @@q
|
||||
*/
|
||||
static inline unsigned int get_head(struct circ_queue *q)
|
||||
{
|
||||
return ioread32(q->head);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief set the "HEAD (IN)" pointer value of a circular queue with @b
|
||||
@@in
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
@param in the value to be stored into the "HEAD (IN)" pointer
|
||||
*/
|
||||
static inline void set_head(struct circ_queue *q, unsigned int in)
|
||||
{
|
||||
iowrite32(in, q->head);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the "TAIL (OUT)" pointer value of a circular queue
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
|
||||
@return the "TAIL (OUT)" pointer value of the @e @@q
|
||||
*/
|
||||
static inline unsigned int get_tail(struct circ_queue *q)
|
||||
{
|
||||
return ioread32(q->tail);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief set the "TAIL (OUT)" pointer value of a circular queue with @e
|
||||
@@out
|
||||
|
||||
@param q the pointer to a circular queue
|
||||
@param out the value to be stored into the "TAIL (OUT)" pointer
|
||||
*/
|
||||
static inline void set_tail(struct circ_queue *q, unsigned int out)
|
||||
{
|
||||
iowrite32(out, q->tail);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief check whether or not both "IN" and "OUT" pointer values are
|
||||
valid
|
||||
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param in the value of the "HEAD (IN)" pointer
|
||||
@param out the value of the "TAIL (OUT)" pointer
|
||||
|
||||
@retval "true" if all pointer values are valid
|
||||
@retval "false" if either IN or OUT pointer value is NOT valid
|
||||
*/
|
||||
static inline bool circ_valid(unsigned int qsize,
|
||||
unsigned int in,
|
||||
unsigned int out)
|
||||
{
|
||||
if (unlikely(in >= qsize))
|
||||
return false;
|
||||
|
||||
if (unlikely(out >= qsize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief check whether or not a circular queue is empty
|
||||
|
||||
@param in the value of the "HEAD (IN)" pointer
|
||||
@param out the value of the "TAIL (OUT)" pointer
|
||||
|
||||
@retval "true" if a circular queue is empty
|
||||
@retval "false" if a circular queue is NOT empty
|
||||
*/
|
||||
static inline bool circ_empty(unsigned int in, unsigned int out)
|
||||
{
|
||||
return (in == out);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the size of free space in a circular queue
|
||||
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param in the value of the "HEAD (IN)" pointer
|
||||
@param out the value of the "TAIL (OUT)" pointer
|
||||
|
||||
@return the size of free space in a circular queue
|
||||
*/
|
||||
static inline unsigned int circ_get_space(unsigned int qsize,
|
||||
unsigned int in,
|
||||
unsigned int out)
|
||||
{
|
||||
return (in < out) ? (out - in - 1) : (qsize + out - in - 1);
|
||||
}
|
||||
|
||||
static inline bool circ_full(unsigned int qsize, unsigned int in,
|
||||
unsigned int out)
|
||||
{
|
||||
return (circ_get_space(qsize, in, out) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the size of data in a circular queue
|
||||
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param in the value of the "HEAD (IN)" pointer
|
||||
@param out the value of the "TAIL (OUT)" pointer
|
||||
|
||||
@return the size of data in a circular queue
|
||||
*/
|
||||
static inline unsigned int circ_get_usage(unsigned int qsize,
|
||||
unsigned int in,
|
||||
unsigned int out)
|
||||
{
|
||||
return (in >= out) ? (in - out) : (qsize - out + in);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief calculate a new pointer value for a circular queue
|
||||
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param p the old value of a queue pointer
|
||||
@param len the length to be added to the @e @@p pointer value
|
||||
|
||||
@return the new value for the queue pointer
|
||||
*/
|
||||
static inline unsigned int circ_new_ptr(unsigned int qsize,
|
||||
unsigned int p,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int np = (p + len);
|
||||
while (np >= qsize)
|
||||
np -= qsize;
|
||||
return np;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief copy the data in a circular queue to a local buffer
|
||||
|
||||
@param dst the start address of the local buffer
|
||||
@param src the start address of the data buffer in a circular queue
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param out the offset in the data buffer to be read
|
||||
@param len the length of data to be read
|
||||
|
||||
@remark This function should be invoked after checking the data length.
|
||||
*/
|
||||
static inline void circ_read(u8 *dst, u8 *src, unsigned int qsize,
|
||||
unsigned int out, unsigned int len)
|
||||
{
|
||||
if ((out + len) <= qsize) {
|
||||
/* ----- (out) (in) ----- */
|
||||
/* ----- 7f 00 00 7e ----- */
|
||||
memcpy(dst, (src + out), len);
|
||||
} else {
|
||||
unsigned int len1;
|
||||
|
||||
/* (in) ----------- (out) */
|
||||
/* 00 7e ----------- 7f 00 */
|
||||
|
||||
/* 1) data start (out) ~ buffer end */
|
||||
len1 = qsize - out;
|
||||
memcpy(dst, (src + out), len1);
|
||||
|
||||
/* 2) buffer start ~ data end (in?) */
|
||||
memcpy((dst + len1), src, (len - len1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief copy the data in a local buffer to a circular queue
|
||||
|
||||
@param dst the start address of the data buffer in a circular queue
|
||||
@param src the start address of the data in a local buffer
|
||||
@param qsize the size of the data buffer in a circular queue
|
||||
@param in the offset in the data buffer for the data to be stored
|
||||
@param len the length of data to be stored
|
||||
|
||||
@remark This function should be invoked after checking the free space.
|
||||
*/
|
||||
static inline void circ_write(u8 *dst, u8 *src, unsigned int qsize,
|
||||
unsigned int in, unsigned int len)
|
||||
{
|
||||
if ((in + len) < qsize) {
|
||||
/* (in) ----------- (out) */
|
||||
/* 00 7e ----------- 7f 00 */
|
||||
memcpy((dst + in), src, len);
|
||||
} else {
|
||||
unsigned int space;
|
||||
|
||||
/* ----- (out) (in) ----- */
|
||||
/* ----- 7f 00 00 7e ----- */
|
||||
|
||||
/* 1) space start (in) ~ buffer end */
|
||||
space = qsize - in;
|
||||
memcpy((dst + in), src, ((len > space) ? space : len));
|
||||
|
||||
/* 2) buffer start ~ data end */
|
||||
if (len > space)
|
||||
memcpy(dst, (src + space), (len - space));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
// End of group_circ_queue
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
119
drivers/misc/modem_v1/include/link_device_memory_config.h
Normal file
119
drivers/misc/modem_v1/include/link_device_memory_config.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
@file link_device_mem_config.h
|
||||
@brief configurations of memory-type interface
|
||||
@date 2014/02/05
|
||||
@author Hankook Jang (hankook.jang@samsung.com)
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_LINK_DEVICE_MEM_CONFIG_H__
|
||||
#define __MODEM_LINK_DEVICE_MEM_CONFIG_H__
|
||||
|
||||
#define GROUP_MEM_TYPE
|
||||
/**
|
||||
@defgroup group_mem_type Memory Type
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_TYPE_SHMEM
|
||||
/**
|
||||
@defgroup group_mem_type_shmem Shared Memory
|
||||
@ingroup group_mem_type
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_DEVICE
|
||||
/**
|
||||
@defgroup group_mem_link_device Memory Link Device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_SBD
|
||||
/**
|
||||
@defgroup group_mem_link_sbd Shared Buffer Descriptor
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_SNAPSHOT
|
||||
/**
|
||||
@defgroup group_mem_link_snapshot Memory Snapshot
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_INTERRUPT
|
||||
/**
|
||||
@defgroup group_mem_link_interrupt Interrupt
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_SETUP
|
||||
/**
|
||||
@defgroup group_mem_link_setup Link Setup
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_METHOD
|
||||
/**
|
||||
@defgroup group_mem_link_method Link Method
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_COMMAND
|
||||
/**
|
||||
@defgroup group_mem_link_command Link Command
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_IPC_DEVICE
|
||||
/**
|
||||
@defgroup group_mem_ipc_device Logical IPC Device
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_IPC_TX
|
||||
/**
|
||||
@defgroup group_mem_ipc_tx TX
|
||||
@ingroup group_mem_ipc_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_IPC_RX
|
||||
/**
|
||||
@defgroup group_mem_ipc_rx RX
|
||||
@ingroup group_mem_ipc_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_FLOW_CONTROL
|
||||
/**
|
||||
@defgroup group_mem_flow_control Flow Control
|
||||
@ingroup group_mem_ipc_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_CP_CRASH
|
||||
/**
|
||||
@defgroup group_mem_cp_crash CP Crash Dump
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_DEBUG
|
||||
/**
|
||||
@defgroup group_mem_link_debug Debugging
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#define GROUP_MEM_LINK_IOSM_MESSAGE
|
||||
/**
|
||||
@defgroup group_mem_link_iosm_message Link IOSM Message
|
||||
@ingroup group_mem_link_device
|
||||
*/
|
||||
|
||||
#endif
|
||||
450
drivers/misc/modem_v1/include/sbd.h
Normal file
450
drivers/misc/modem_v1/include/sbd.h
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_INCLUDE_SBD_H__
|
||||
#define __MODEM_INCLUDE_SBD_H__
|
||||
|
||||
/**
|
||||
@file sbd.h
|
||||
@brief header file for shared buffer descriptor (SBD) architecture
|
||||
designed by MIPI Alliance
|
||||
@date 2014/02/05
|
||||
@author Hankook Jang (hankook.jang@samsung.com)
|
||||
*/
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SBD
|
||||
/**
|
||||
@addtogroup group_mem_link_sbd
|
||||
@{
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "../modem_v1.h"
|
||||
|
||||
#include "link_device_memory_config.h"
|
||||
#include "circ_queue.h"
|
||||
|
||||
/*
|
||||
Abbreviations
|
||||
=============
|
||||
SB, sb Shared Buffer
|
||||
SBD, sbd Shared Buffer Descriptor
|
||||
SBDV, sbdv Shared Buffer Descriptor Vector (Array)
|
||||
--------
|
||||
RB, rb Ring Buffer
|
||||
RBO, rbo Ring Buffer Offset (offset of an RB)
|
||||
RBD, rbd Ring Buffer Descriptor (descriptor of an RB)
|
||||
RBDO, rbdo Ring Buffer Descriptor Offset (offset of an RBD)
|
||||
--------
|
||||
RBP, rbp Ring Buffer Pointer (read pointer, write pointer)
|
||||
RBPS, rbps Ring Buffer Pointer Set (set of RBPs)
|
||||
--------
|
||||
CH, ch Channel
|
||||
CHD, chd Channel Descriptor
|
||||
--------
|
||||
desc descriptor
|
||||
*/
|
||||
|
||||
#define CMD_DESC_RGN_OFFSET 0
|
||||
#define CMD_DESC_RGN_SIZE SZ_64K
|
||||
|
||||
#define CTRL_RGN_OFFSET (CMD_DESC_RGN_OFFSET)
|
||||
#define CTRL_RGN_SIZE (1 * SZ_1K)
|
||||
|
||||
#define CMD_RGN_OFFSET (CTRL_RGN_OFFSET + CTRL_RGN_SIZE)
|
||||
#define CMD_RGN_SIZE (1 * SZ_1K)
|
||||
|
||||
#define DESC_RGN_OFFSET (CMD_RGN_OFFSET + CMD_RGN_SIZE)
|
||||
#define DESC_RGN_SIZE (CMD_DESC_RGN_SIZE - CTRL_RGN_SIZE - CMD_RGN_SIZE)
|
||||
|
||||
#define BUFF_RGN_OFFSET (CMD_DESC_RGN_SIZE)
|
||||
|
||||
/**
|
||||
@brief SBD Ring Buffer Descriptor
|
||||
(i.e. IPC Channel Descriptor Structure in MIPI LLI_IPC_AN)
|
||||
*/
|
||||
struct __packed sbd_rb_desc {
|
||||
/*
|
||||
ch Channel number defined in the Samsung IPC specification
|
||||
--------
|
||||
reserved
|
||||
*/
|
||||
u16 ch;
|
||||
u16 reserved;
|
||||
|
||||
/*
|
||||
direction 0 (UL, TX, AP-to-CP), 1 (DL, RX, CP-to-AP)
|
||||
--------
|
||||
signaling 0 (polling), 1 (interrupt)
|
||||
*/
|
||||
u16 direction;
|
||||
u16 signaling;
|
||||
|
||||
/*
|
||||
Mask to be written to the signal register (viz. 1 << @@id)
|
||||
(i.e. write_signal)
|
||||
*/
|
||||
u32 sig_mask;
|
||||
|
||||
/*
|
||||
length Length of an SBD Ring Buffer
|
||||
--------
|
||||
id (1) ID for a link channel that consists of an RB pair
|
||||
(2) Index into each array in the set of RBP arrays
|
||||
N.B. set of RBP arrays =
|
||||
{[ul_rp], [ul_wp], [dl_rp], [dl_wp]}
|
||||
*/
|
||||
u16 length;
|
||||
u16 id;
|
||||
|
||||
/*
|
||||
buff_size Size of each data buffer in an SBD RB (default 2048)
|
||||
--------
|
||||
payload_offset Offset of the payload in each data buffer (default 0)
|
||||
*/
|
||||
u16 buff_size;
|
||||
u16 payload_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief SBD Channel Descriptor
|
||||
*/
|
||||
struct __packed sbd_rb_channel {
|
||||
u32 ul_rbd_offset; /* UL RB Descriptor Offset */
|
||||
u32 ul_sbdv_offset; /* UL SBD Vectors's Offset */
|
||||
u32 dl_rbd_offset; /* DL RB Descriptor's Offset */
|
||||
u32 dl_sbdv_offset; /* DL SBD Vector's Offset */
|
||||
};
|
||||
|
||||
/**
|
||||
@brief SBD Global Descriptor
|
||||
*/
|
||||
struct __packed sbd_global_desc {
|
||||
/*
|
||||
Version
|
||||
*/
|
||||
u32 version;
|
||||
|
||||
/*
|
||||
Number of link channels
|
||||
*/
|
||||
u32 num_channels;
|
||||
|
||||
/*
|
||||
Offset of the array of "SBD Ring Buffer Pointers Set" in SHMEM
|
||||
*/
|
||||
u32 rbps_offset;
|
||||
|
||||
/*
|
||||
Array of SBD channel descriptors
|
||||
*/
|
||||
struct sbd_rb_channel rb_ch[MAX_LINK_CHANNELS];
|
||||
|
||||
/*
|
||||
Array of SBD ring buffer descriptor pairs
|
||||
*/
|
||||
struct sbd_rb_desc rb_desc[MAX_LINK_CHANNELS][ULDL];
|
||||
};
|
||||
|
||||
#if 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
@brief SBD ring buffer (with logical view)
|
||||
@remark physical SBD ring buffer
|
||||
= {length, *rp, *wp, offset array, size array}
|
||||
*/
|
||||
struct sbd_ring_buffer {
|
||||
/*
|
||||
Spin-lock for each SBD RB
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
/*
|
||||
Pointer to the "SBD link" device instance to which an RB belongs
|
||||
*/
|
||||
struct sbd_link_device *sl;
|
||||
|
||||
/*
|
||||
UL/DL socket buffer queues
|
||||
*/
|
||||
struct sk_buff_head skb_q;
|
||||
|
||||
/*
|
||||
Whether or not link-layer header is used
|
||||
*/
|
||||
bool lnk_hdr;
|
||||
|
||||
/*
|
||||
Variables for receiving a frame with the SIPC5 "EXT_LEN" attribute
|
||||
(With SBD architecture, a frame with EXT_LEN can be scattered into
|
||||
consecutive multiple data buffer slots.)
|
||||
*/
|
||||
bool more;
|
||||
unsigned int total;
|
||||
unsigned int rcvd;
|
||||
|
||||
/*
|
||||
Link ID, SIPC channel, and IPC direction
|
||||
*/
|
||||
u16 id; /* for @desc->id */
|
||||
u16 ch; /* for @desc->ch */
|
||||
u16 dir; /* for @desc->direction */
|
||||
u16 len; /* for @desc->length */
|
||||
u16 buff_size; /* for @desc->buff_size */
|
||||
u16 payload_offset; /* for @desc->payload_offset */
|
||||
|
||||
/*
|
||||
Pointer to the array of pointers to each data buffer
|
||||
*/
|
||||
u8 **buff;
|
||||
|
||||
/*
|
||||
Pointer to the data buffer region in SHMEM
|
||||
*/
|
||||
u8 *buff_rgn;
|
||||
|
||||
/*
|
||||
Pointers to variables in the shared region for a physical SBD RB
|
||||
*/
|
||||
u16 *rp; /* sl->rp[@dir][@id] */
|
||||
u16 *wp; /* sl->wp[@dir][@id] */
|
||||
u32 *addr_v; /* Vector array of offsets */
|
||||
u32 *size_v; /* Vector array of sizes */
|
||||
|
||||
/*
|
||||
Pointer to the IO device and the link device to which an SBD RB belongs
|
||||
*/
|
||||
struct io_device *iod;
|
||||
struct link_device *ld;
|
||||
};
|
||||
|
||||
struct sbd_link_attr {
|
||||
/*
|
||||
Link ID and SIPC channel number
|
||||
*/
|
||||
u16 id;
|
||||
u16 ch;
|
||||
|
||||
/*
|
||||
Whether or not link-layer header is used
|
||||
*/
|
||||
bool lnk_hdr;
|
||||
|
||||
/*
|
||||
Length of an SBD RB
|
||||
*/
|
||||
unsigned int rb_len[ULDL];
|
||||
|
||||
/*
|
||||
Size of the data buffer for each SBD in an SBD RB
|
||||
*/
|
||||
unsigned int buff_size[ULDL];
|
||||
};
|
||||
|
||||
struct sbd_ipc_device {
|
||||
/*
|
||||
Pointer to the IO device to which an SBD IPC device belongs
|
||||
*/
|
||||
struct io_device *iod;
|
||||
|
||||
/*
|
||||
SBD IPC device ID == Link ID --> rb.id
|
||||
*/
|
||||
u16 id;
|
||||
|
||||
/*
|
||||
SIPC Channel ID --> rb.ch
|
||||
*/
|
||||
u16 ch;
|
||||
|
||||
atomic_t config_done;
|
||||
|
||||
/*
|
||||
UL/DL SBD RB pair in the kernel space
|
||||
*/
|
||||
struct sbd_ring_buffer rb[ULDL];
|
||||
};
|
||||
|
||||
struct sbd_link_device {
|
||||
/*
|
||||
Pointer to the link device to which an SBD link belongs
|
||||
*/
|
||||
struct link_device *ld;
|
||||
|
||||
/*
|
||||
Flag for checking whether or not an SBD link is active
|
||||
*/
|
||||
atomic_t active;
|
||||
|
||||
/*
|
||||
Version of SBD IPC
|
||||
*/
|
||||
unsigned int version;
|
||||
|
||||
/*
|
||||
Start address of SHMEM
|
||||
@shmem = SHMEM.VA
|
||||
*/
|
||||
u8 *shmem;
|
||||
unsigned int shmem_size;
|
||||
|
||||
/*
|
||||
Variables for DESC & BUFF allocation management
|
||||
*/
|
||||
unsigned int desc_alloc_offset;
|
||||
unsigned int buff_alloc_offset;
|
||||
|
||||
/*
|
||||
The number of link channels for AP-CP IPC
|
||||
*/
|
||||
unsigned int num_channels;
|
||||
|
||||
/*
|
||||
Table of link attributes
|
||||
*/
|
||||
struct sbd_link_attr link_attr[MAX_LINK_CHANNELS];
|
||||
|
||||
/*
|
||||
Logical IPC devices
|
||||
*/
|
||||
struct sbd_ipc_device ipc_dev[MAX_LINK_CHANNELS];
|
||||
|
||||
/*
|
||||
(1) Conversion tables from "Link ID (ID)" to "SIPC Channel Number (CH)"
|
||||
(2) Conversion tables from "SIPC Channel Number (CH)" to "Link ID (ID)"
|
||||
*/
|
||||
u16 id2ch[MAX_LINK_CHANNELS];
|
||||
u16 ch2id[MAX_SIPC_CHANNELS];
|
||||
|
||||
/*
|
||||
Pointers to each array of arrays of SBD RB Pointers,
|
||||
viz. rp[UL] = pointer to ul_rp[]
|
||||
rp[DL] = pointer to dl_rp[]
|
||||
wp[UL] = pointer to ul_wp[]
|
||||
wp[DL] = pointer to dl_wp[]
|
||||
*/
|
||||
u16 *rp[ULDL];
|
||||
u16 *wp[ULDL];
|
||||
|
||||
/*
|
||||
Above are variables for managing and controlling SBD IPC
|
||||
========================================================================
|
||||
Below are pointers to the descriptor sections in SHMEM
|
||||
*/
|
||||
|
||||
/*
|
||||
Pointer to the SBD global descriptor header
|
||||
*/
|
||||
struct sbd_global_desc *g_desc;
|
||||
|
||||
u16 *rbps;
|
||||
|
||||
unsigned long rxdone_mask;
|
||||
};
|
||||
|
||||
static inline void sbd_activate(struct sbd_link_device *sl)
|
||||
{
|
||||
if (sl)
|
||||
atomic_set(&sl->active, 1);
|
||||
}
|
||||
|
||||
static inline void sbd_deactivate(struct sbd_link_device *sl)
|
||||
{
|
||||
if (sl)
|
||||
atomic_set(&sl->active, 0);
|
||||
}
|
||||
|
||||
static inline bool sbd_active(struct sbd_link_device *sl)
|
||||
{
|
||||
if (!sl)
|
||||
return false;
|
||||
return atomic_read(&sl->active) ? true : false;
|
||||
}
|
||||
|
||||
static inline u16 sbd_ch2id(struct sbd_link_device *sl, u16 ch)
|
||||
{
|
||||
return sl->ch2id[ch];
|
||||
}
|
||||
|
||||
static inline u16 sbd_id2ch(struct sbd_link_device *sl, u16 id)
|
||||
{
|
||||
return sl->id2ch[id];
|
||||
}
|
||||
|
||||
static inline struct sbd_ipc_device *sbd_ch2dev(struct sbd_link_device *sl,
|
||||
u16 ch)
|
||||
{
|
||||
u16 id = sbd_ch2id(sl, ch);
|
||||
return (id < MAX_LINK_CHANNELS) ? &sl->ipc_dev[id] : NULL;
|
||||
}
|
||||
|
||||
static inline struct sbd_ipc_device *sbd_id2dev(struct sbd_link_device *sl,
|
||||
u16 id)
|
||||
{
|
||||
return (id < MAX_LINK_CHANNELS) ? &sl->ipc_dev[id] : NULL;
|
||||
}
|
||||
|
||||
static inline struct sbd_ring_buffer *sbd_ch2rb(struct sbd_link_device *sl,
|
||||
unsigned int ch,
|
||||
enum direction dir)
|
||||
{
|
||||
u16 id = sbd_ch2id(sl, ch);
|
||||
return (id < MAX_LINK_CHANNELS) ? &sl->ipc_dev[id].rb[dir] : NULL;
|
||||
}
|
||||
|
||||
static inline struct sbd_ring_buffer *sbd_id2rb(struct sbd_link_device *sl,
|
||||
unsigned int id,
|
||||
enum direction dir)
|
||||
{
|
||||
return (id < MAX_LINK_CHANNELS) ? &sl->ipc_dev[id].rb[dir] : NULL;
|
||||
}
|
||||
|
||||
static inline bool rb_empty(struct sbd_ring_buffer *rb)
|
||||
{
|
||||
return circ_empty(*rb->rp, *rb->wp);
|
||||
}
|
||||
|
||||
static inline unsigned int rb_space(struct sbd_ring_buffer *rb)
|
||||
{
|
||||
return circ_get_space(rb->len, *rb->wp, *rb->rp);
|
||||
}
|
||||
|
||||
static inline unsigned int rb_usage(struct sbd_ring_buffer *rb)
|
||||
{
|
||||
return circ_get_usage(rb->len, *rb->wp, *rb->rp);
|
||||
}
|
||||
|
||||
static inline unsigned int rb_full(struct sbd_ring_buffer *rb)
|
||||
{
|
||||
return (rb_space(rb) == 0);
|
||||
}
|
||||
|
||||
int create_sbd_link_device(struct link_device *ld, struct sbd_link_device *sl,
|
||||
u8 *shmem_base, unsigned int shmem_size);
|
||||
|
||||
int init_sbd_link(struct sbd_link_device *sl);
|
||||
|
||||
int sbd_pio_tx(struct sbd_ring_buffer *rb, struct sk_buff *skb);
|
||||
struct sk_buff *sbd_pio_rx(struct sbd_ring_buffer *rb);
|
||||
|
||||
#define SBD_UL_LIMIT 16 /* Uplink burst limit */
|
||||
|
||||
/**
|
||||
// End of group_mem_link_sbd
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
32
drivers/misc/modem_v1/include/shm_ipc.h
Normal file
32
drivers/misc/modem_v1/include/shm_ipc.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Samsung Electronics Co.Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Shared Memory driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#ifndef SHMEM_IPC_H
|
||||
#define SHMEM_IPC_H
|
||||
|
||||
struct shdmem_info {
|
||||
unsigned int base;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
unsigned long shm_get_phys_base(void);
|
||||
unsigned shm_get_phys_size(void);
|
||||
unsigned shm_get_boot_size(void);
|
||||
unsigned shm_get_ipc_rgn_offset(void);
|
||||
unsigned shm_get_ipc_rgn_size(void);
|
||||
unsigned long shm_get_security_param2(unsigned long mode, u32 bl_size);
|
||||
unsigned long shm_get_security_param3(unsigned long mode, u32 main_size);
|
||||
|
||||
void __iomem *shm_request_region(unsigned long sh_addr, unsigned size);
|
||||
void __iomem *shm_get_boot_region(void);
|
||||
void __iomem *shm_get_ipc_region(void);
|
||||
void shm_release_region(void *v_addr);
|
||||
#endif
|
||||
278
drivers/misc/modem_v1/include/sipc5.h
Normal file
278
drivers/misc/modem_v1/include/sipc5.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SIPC5_H__
|
||||
#define __SIPC5_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "../modem_v1.h"
|
||||
|
||||
/* SIPC5 link-layer header */
|
||||
struct __packed sipc5_link_header {
|
||||
u8 cfg;
|
||||
u8 ch;
|
||||
u16 len;
|
||||
union {
|
||||
struct multi_frame_control ctrl;
|
||||
u16 ext_len;
|
||||
};
|
||||
};
|
||||
|
||||
#define SIPC5_START_MASK (0b11111000)
|
||||
#define SIPC5_CONFIG_MASK (0b00000111)
|
||||
#define SIPC5_EXT_FIELD_MASK (0b00000011)
|
||||
|
||||
#define SIPC5_PADDING_EXIST (0b00000100)
|
||||
#define SIPC5_EXT_FIELD_EXIST (0b00000010)
|
||||
#define SIPC5_CTL_FIELD_EXIST (0b00000001)
|
||||
|
||||
#define SIPC5_MULTI_FRAME_CFG (0b00000011)
|
||||
#define SIPC5_EXT_LENGTH_CFG (0b00000010)
|
||||
|
||||
#define SIPC5_CONFIG_OFFSET 0
|
||||
#define SIPC5_CONFIG_SIZE 1
|
||||
|
||||
#define SIPC5_CH_ID_OFFSET 1
|
||||
#define SIPC5_CH_ID_SIZE 1
|
||||
|
||||
#define SIPC5_LEN_OFFSET 2
|
||||
#define SIPC5_LEN_SIZE 2
|
||||
|
||||
#define SIPC5_CTRL_OFFSET 4
|
||||
#define SIPC5_CTRL_SIZE 1
|
||||
|
||||
#define SIPC5_EXT_LEN_OFFSET 4
|
||||
#define SIPC5_EXT_LEN_SIZE 2
|
||||
|
||||
#define SIPC5_MIN_HEADER_SIZE 4
|
||||
#define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5
|
||||
#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6
|
||||
#define SIPC5_MAX_HEADER_SIZE SIPC5_HEADER_SIZE_WITH_EXT_LEN
|
||||
|
||||
static inline bool sipc5_start_valid(u8 *frm)
|
||||
{
|
||||
return (*frm & SIPC5_START_MASK) == SIPC5_START_MASK;
|
||||
}
|
||||
|
||||
static inline bool sipc5_padding_exist(u8 *frm)
|
||||
{
|
||||
return (*frm & SIPC5_PADDING_EXIST) ? true : false;
|
||||
}
|
||||
|
||||
static inline bool sipc5_multi_frame(u8 *frm)
|
||||
{
|
||||
return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_MULTI_FRAME_CFG;
|
||||
}
|
||||
|
||||
static inline bool sipc5_ext_len(u8 *frm)
|
||||
{
|
||||
return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_EXT_LENGTH_CFG;
|
||||
}
|
||||
|
||||
static inline u8 sipc5_get_ch(u8 *frm)
|
||||
{
|
||||
return frm[SIPC5_CH_ID_OFFSET];
|
||||
}
|
||||
|
||||
static inline u8 sipc5_get_ctrl(u8 *frm)
|
||||
{
|
||||
return frm[SIPC5_CTRL_OFFSET];
|
||||
}
|
||||
|
||||
static inline unsigned int sipc5_calc_padding_size(unsigned int len)
|
||||
{
|
||||
unsigned int residue = len & (sizeof(size_t) - 1);
|
||||
return residue ? (sizeof(size_t) - residue) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the length of the header of an SIPC5 link frame
|
||||
@param frm the pointer to an SIPC5 link frame
|
||||
@return the size of the header of an SIPC5 link frame
|
||||
*/
|
||||
static inline unsigned int sipc5_get_hdr_len(u8 *frm)
|
||||
{
|
||||
if (unlikely(frm[0] & SIPC5_EXT_FIELD_EXIST)) {
|
||||
if (sipc5_multi_frame(frm))
|
||||
return SIPC5_HEADER_SIZE_WITH_CTL_FLD;
|
||||
else
|
||||
return SIPC5_HEADER_SIZE_WITH_EXT_LEN;
|
||||
} else {
|
||||
return SIPC5_MIN_HEADER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the real length of an SIPC5 link frame WITHOUT padding
|
||||
@param frm the pointer to an SIPC5 link frame
|
||||
@return the real length of an SIPC5 link frame WITHOUT padding
|
||||
*/
|
||||
static inline unsigned int sipc5_get_frame_len(u8 *frm)
|
||||
{
|
||||
u16 *sz16 = (u16 *)(frm + SIPC5_LEN_OFFSET);
|
||||
u32 *sz32 = (u32 *)(frm + SIPC5_LEN_OFFSET);
|
||||
|
||||
if (unlikely(frm[0] & SIPC5_EXT_FIELD_EXIST)) {
|
||||
if (sipc5_multi_frame(frm))
|
||||
return *sz16;
|
||||
else
|
||||
return *sz32;
|
||||
} else {
|
||||
return *sz16;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief get the total length of an SIPC5 link frame with padding
|
||||
@param frm the pointer to an SIPC5 link frame
|
||||
@return the total length of an SIPC5 link frame with padding
|
||||
*/
|
||||
static inline unsigned int sipc5_get_total_len(u8 *frm)
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned int pad;
|
||||
|
||||
len = sipc5_get_frame_len(frm);
|
||||
pad = sipc5_padding_exist(frm) ? sipc5_calc_padding_size(len) : 0;
|
||||
return len + pad;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for FMT channel
|
||||
*/
|
||||
static inline bool sipc5_fmt_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC5_CH_ID_FMT_0 && ch <= SIPC5_CH_ID_FMT_9) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for RFS channel
|
||||
*/
|
||||
static inline bool sipc5_rfs_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC5_CH_ID_RFS_0 && ch <= SIPC5_CH_ID_RFS_9) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for BOOT channel
|
||||
*/
|
||||
static inline bool sipc5_boot_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC5_CH_ID_BOOT_0 && ch <= SIPC5_CH_ID_BOOT_9) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for DUMP channel
|
||||
*/
|
||||
static inline bool sipc5_dump_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC5_CH_ID_DUMP_0 && ch <= SIPC5_CH_ID_DUMP_9) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for BOOT/DUMP channel
|
||||
*/
|
||||
static inline bool sipc5_udl_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC5_CH_ID_BOOT_0 && ch <= SIPC5_CH_ID_DUMP_9) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param ch the channel ID
|
||||
@return true if the channel ID is for IPC channel
|
||||
*/
|
||||
static inline bool sipc5_ipc_ch(u8 ch)
|
||||
{
|
||||
return (ch > 0 && (ch < SIPC5_CH_ID_BOOT_0 || ch > SIPC5_CH_ID_DUMP_9))
|
||||
? true : false;
|
||||
}
|
||||
|
||||
struct sipc5_frame_data {
|
||||
/* Frame length calculated from the length fields */
|
||||
unsigned int len;
|
||||
|
||||
/* The length of link layer header */
|
||||
unsigned int hdr_len;
|
||||
|
||||
/* The length of received header */
|
||||
unsigned int hdr_rcvd;
|
||||
|
||||
/* The length of link layer payload */
|
||||
unsigned int pay_len;
|
||||
|
||||
/* The length of received data */
|
||||
unsigned int pay_rcvd;
|
||||
|
||||
/* The length of link layer padding */
|
||||
unsigned int pad_len;
|
||||
|
||||
/* The length of received padding */
|
||||
unsigned int pad_rcvd;
|
||||
|
||||
/* Header buffer */
|
||||
u8 hdr[SIPC5_MAX_HEADER_SIZE];
|
||||
};
|
||||
|
||||
#if 1
|
||||
#endif
|
||||
|
||||
#define STD_UDL_STEP_MASK 0x0000000F
|
||||
#define STD_UDL_SEND 0x1
|
||||
#define STD_UDL_CRC 0xC
|
||||
|
||||
struct std_dload_info {
|
||||
u32 size;
|
||||
u32 mtu;
|
||||
u32 num_frames;
|
||||
} __packed;
|
||||
|
||||
|
||||
/**
|
||||
@brief get BOOT/DUMP command
|
||||
|
||||
@param frm the pointer to an SIPC5 link frame
|
||||
|
||||
@return the standard BOOT/DUMP command in an SIPC5 BOOT/DUMP frame
|
||||
*/
|
||||
static inline u32 std_udl_get_cmd(u8 *frm)
|
||||
{
|
||||
u8 *cmd = frm + sipc5_get_hdr_len(frm);
|
||||
return *((u32 *)cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief check whether or not a command came with a payload
|
||||
|
||||
@param cmd the standard BOOT/DUMP command
|
||||
|
||||
@retval "true" if the STD_UDL command has a payload
|
||||
@retval "false" otherwise
|
||||
*/
|
||||
static inline bool std_udl_with_payload(u32 cmd)
|
||||
{
|
||||
u32 mask = cmd & STD_UDL_STEP_MASK;
|
||||
return (mask && mask < STD_UDL_CRC) ? true : false;
|
||||
}
|
||||
|
||||
#endif
|
||||
430
drivers/misc/modem_v1/link_ctrlmsg_iosm.c
Normal file
430
drivers/misc/modem_v1/link_ctrlmsg_iosm.c
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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 "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
#include "link_device_memory_ctrlmsg_iosm.h"
|
||||
|
||||
#ifdef GROUP_MEM_LINK_IOSM_MESSAGE
|
||||
|
||||
#define pr_circ_idx(hdr) \
|
||||
mif_info("circ: in=%d, out=%d\n", hdr->w_idx, hdr->r_idx);
|
||||
|
||||
struct iosm_msg_area_head {
|
||||
u32 w_idx; /* Write index */
|
||||
u32 r_idx; /* Read index */
|
||||
u32 reserved[2];
|
||||
u32 num_msg; /* Is this actually required? */
|
||||
} __packed;
|
||||
|
||||
struct iosm_msg {
|
||||
u8 msg_id; /* message id */
|
||||
u8 trs_id; /* transaction id */
|
||||
union {
|
||||
struct __packed {
|
||||
u8 reserved[2];
|
||||
u32 addr;
|
||||
} ap_ready;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 cfg;
|
||||
} open_ch;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 cfg;
|
||||
} close_ch;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 cfg;
|
||||
} conf_ch_req;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 cfg;
|
||||
} conf_ch_rsp;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
} stop_tx_ch;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
} start_tx_ch;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 msg_id;
|
||||
} ack;
|
||||
struct __packed {
|
||||
u8 ch_id;
|
||||
u8 msg_id;
|
||||
u8 err_class;
|
||||
u8 err_subclass;
|
||||
} nack;
|
||||
u8 reserved[10];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define IOSM_NUM_ELEMENTS ( \
|
||||
(IOSM_MSG_AREA_SIZE - sizeof(struct iosm_msg_area_head)) \
|
||||
/ sizeof(struct iosm_msg))
|
||||
|
||||
/* Message Area definition */
|
||||
struct iosm_msg_area {
|
||||
struct iosm_msg_area_head hdr;
|
||||
struct iosm_msg elements[IOSM_NUM_ELEMENTS];
|
||||
} __packed;
|
||||
|
||||
static struct workqueue_struct *iosm_wq;
|
||||
static struct mutex iosm_mtx;
|
||||
static atomic_t mdm_ready;
|
||||
|
||||
static const char const *tx_iosm_str[] = {
|
||||
[IOSM_A2C_AP_READY] = "AP_READY",
|
||||
[IOSM_A2C_CONF_CH_REQ] = "CONF_CH_REQ",
|
||||
[IOSM_A2C_OPEN_CH] = "OPEN_CH",
|
||||
[IOSM_A2C_CLOSE_CH] = "CLOSE_CH",
|
||||
[IOSM_A2C_STOP_TX_CH] = "STOP_TX_CH",
|
||||
[IOSM_A2C_START_TX_CH] = "START_TX_CH",
|
||||
[IOSM_A2C_ACK] = "ACK",
|
||||
[IOSM_A2C_NACK] = "NACK",
|
||||
};
|
||||
|
||||
/* 8-bit transaction ID: holds unique id value (1 ~ 255)
|
||||
* different number than the last message and incremented by one
|
||||
* also to be echoed back in the response of received message
|
||||
*/
|
||||
static atomic_t tid = ATOMIC_INIT(-1);
|
||||
|
||||
static inline int get_transaction_id(void)
|
||||
{
|
||||
return atomic_inc_return(&tid) % IOSM_TRANS_ID_MAX + 1;
|
||||
}
|
||||
|
||||
static inline int check_ul_space(u32 qlen, u32 in, u32 out)
|
||||
{
|
||||
u32 usage, space;
|
||||
|
||||
if (!circ_valid(qlen, in, out)) {
|
||||
mif_err("ERR! TXQ DIRTY (qlen:%d in:%d out:%d)\n",
|
||||
qlen, in, out);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
usage = circ_get_usage(qlen, in, out);
|
||||
if (unlikely(usage > SBD_UL_LIMIT)) {
|
||||
mif_err("TXQ BUSY (qlen:%d in:%d out:%d usage:%d)\n",
|
||||
qlen, in, out, usage);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
space = circ_get_space(qlen, in, out);
|
||||
if (unlikely(space < 1)) {
|
||||
mif_err("TXQ NOSPC (qlen:%d in:%d out:%d)\n", qlen, in, out);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
inline void create_iosm_message(struct iosm_msg *txmsg, u8 mid, u32 *args)
|
||||
{
|
||||
struct iosm_msg *msg;
|
||||
|
||||
txmsg->msg_id = mid;
|
||||
|
||||
switch (mid) {
|
||||
case IOSM_A2C_AP_READY:
|
||||
/* set global descriptor address */
|
||||
txmsg->ap_ready.addr = IOSM_MSG_DESC_OFFSET;
|
||||
break;
|
||||
case IOSM_A2C_CONF_CH_REQ:
|
||||
txmsg->conf_ch_req.ch_id = *((u32 *)args);
|
||||
txmsg->conf_ch_req.cfg = 0x1;
|
||||
break;
|
||||
case IOSM_A2C_OPEN_CH:
|
||||
case IOSM_A2C_CLOSE_CH:
|
||||
txmsg->close_ch.ch_id = *((u32 *)args);
|
||||
txmsg->close_ch.cfg = 0x7;
|
||||
break;
|
||||
case IOSM_A2C_ACK:
|
||||
case IOSM_A2C_NACK:
|
||||
msg = (struct iosm_msg *) args;
|
||||
txmsg->ack.msg_id = msg->msg_id;
|
||||
if (msg->msg_id == IOSM_C2A_CONF_CH_RSP)
|
||||
txmsg->ack.ch_id = msg->conf_ch_rsp.ch_id;
|
||||
if (msg->msg_id == IOSM_C2A_STOP_TX_CH)
|
||||
txmsg->ack.ch_id = msg->stop_tx_ch.ch_id;
|
||||
if (msg->msg_id == IOSM_C2A_START_TX_CH)
|
||||
txmsg->ack.ch_id = msg->start_tx_ch.ch_id;
|
||||
/* trans_id shouldn't be modified from end of this function */
|
||||
txmsg->trs_id = msg->trs_id;
|
||||
return;
|
||||
default:
|
||||
mif_err("0x%x message is not supported.\n", mid);
|
||||
}
|
||||
|
||||
txmsg->trs_id = get_transaction_id();
|
||||
}
|
||||
|
||||
void tx_iosm_message(struct mem_link_device *mld, u8 id, u32 *args)
|
||||
{
|
||||
struct iosm_msg_area *base;
|
||||
struct iosm_msg_area_head *hdr;
|
||||
struct iosm_msg *msg;
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
int space, retry_cnt = 500;
|
||||
|
||||
if (!cp_online(mc))
|
||||
return;
|
||||
|
||||
mutex_lock(&iosm_mtx);
|
||||
|
||||
base = (struct iosm_msg_area *) (mld->base + IOSM_MSG_TX_OFFSET);
|
||||
hdr = &base->hdr;
|
||||
|
||||
/* A message sender reads the read and write index and
|
||||
* determines whether there are free elements.
|
||||
*/
|
||||
space = check_ul_space(IOSM_NUM_ELEMENTS, hdr->w_idx, hdr->r_idx);
|
||||
if (space <= 0) {
|
||||
mutex_unlock(&iosm_mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
msg = &base->elements[hdr->w_idx];
|
||||
create_iosm_message(msg, id, args);
|
||||
|
||||
/* The write index is incremented and interrupt is triggered
|
||||
* to the message receiver.
|
||||
*/
|
||||
hdr->w_idx = circ_new_ptr(IOSM_NUM_ELEMENTS, hdr->w_idx, 1);
|
||||
pr_circ_idx(hdr);
|
||||
|
||||
mutex_unlock(&iosm_mtx);
|
||||
|
||||
if (cp_online(mc) && mld->forbid_cp_sleep)
|
||||
mld->forbid_cp_sleep(mld);
|
||||
|
||||
/* As of now, tx path of iosm message should always guarantee
|
||||
* process context. We don't have to care rx path because cp
|
||||
* might try to mount lli i/f before sending data. */
|
||||
while (!mld->link_active(mld)) {
|
||||
if (--retry_cnt == 0) {
|
||||
modemctl_notify_event(MDM_EVENT_CP_FORCE_CRASH);
|
||||
return;
|
||||
}
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
send_ipc_irq(mld, mask2int(MASK_CMD_VALID));
|
||||
mif_info("sent msg %s\n", tx_iosm_str[msg->msg_id]);
|
||||
|
||||
if (cp_online(mc) && mld->permit_cp_sleep)
|
||||
mld->permit_cp_sleep(mld);
|
||||
}
|
||||
|
||||
void mdm_ready_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
int err;
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
|
||||
mif_err("%s: %s.state:%s cp_boot_done:%d\n", ld->name,
|
||||
mc->name, mc_state(mc), atomic_read(&mld->cp_boot_done));
|
||||
|
||||
if (!ld->sbd_ipc) {
|
||||
mif_err("%s: LINK_ATTR_SBD_IPC is NOT set\n", ld->name);
|
||||
return;
|
||||
}
|
||||
|
||||
ld->netif_stop_mask = 0;
|
||||
atomic_set(&ld->netif_stopped, 0);
|
||||
atomic_set(&mc->forced_cp_crash, 0);
|
||||
|
||||
mc->iod->modem_state_changed(mc->iod, STATE_ONLINE);
|
||||
|
||||
#ifdef CONFIG_LINK_POWER_MANAGEMENT
|
||||
if (mld->start_pm) {
|
||||
mld->start_pm(mld);
|
||||
gpio_set_value(mld->gpio_cp_wakeup, 0);
|
||||
gpio_set_value(mld->gpio_ap_status, 0);
|
||||
}
|
||||
|
||||
if (cp_online(mc) && mld->forbid_cp_sleep)
|
||||
mld->forbid_cp_sleep(mld);
|
||||
#endif
|
||||
|
||||
tx_iosm_message(mld, IOSM_A2C_ACK, (u32 *)msg);
|
||||
|
||||
err = init_sbd_link(&mld->sbd_link_dev);
|
||||
if (err < 0) {
|
||||
mif_err("%s: init_sbd_link fail(%d)\n", ld->name, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mld->attrs & LINK_ATTR(LINK_ATTR_IPC_ALIGNED))
|
||||
ld->aligned = true;
|
||||
else
|
||||
ld->aligned = false;
|
||||
|
||||
sbd_activate(&mld->sbd_link_dev);
|
||||
|
||||
tx_iosm_message(mld, IOSM_A2C_AP_READY, 0);
|
||||
|
||||
mif_info("%s: %s mdm_ready done\n", ld->name, mc->name);
|
||||
}
|
||||
|
||||
void conf_ch_rsp_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
struct sbd_link_device *sl = &mld->sbd_link_dev;
|
||||
int dev_id = sbd_ch2id(sl, msg->ack.ch_id);
|
||||
struct sbd_ipc_device *sid = sbd_id2dev(sl, dev_id);
|
||||
|
||||
atomic_set(&sid->config_done, 1);
|
||||
tx_iosm_message(mld, IOSM_A2C_ACK, (u32 *)msg);
|
||||
|
||||
mif_info("ch_id : %d, dev_id : %d\n", sid->ch, dev_id);
|
||||
}
|
||||
|
||||
void stop_tx_ch_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
stop_net_iface(ld, msg->stop_tx_ch.ch_id);
|
||||
tx_iosm_message(mld, IOSM_A2C_ACK, (u32 *)msg);
|
||||
}
|
||||
|
||||
void start_tx_ch_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
resume_net_iface(ld, msg->start_tx_ch.ch_id);
|
||||
tx_iosm_message(mld, IOSM_A2C_ACK, (u32 *)msg);
|
||||
}
|
||||
|
||||
static void action(struct io_device *iod, void *args)
|
||||
{
|
||||
struct mem_link_device *mld = (struct mem_link_device *) args;
|
||||
|
||||
tx_iosm_message(mld, IOSM_A2C_CONF_CH_REQ, (u32 *)&iod->id);
|
||||
}
|
||||
|
||||
void ack_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
|
||||
mif_err("got ack for msg id = 0x%x\n", msg->ack.msg_id);
|
||||
|
||||
switch (msg->ack.msg_id) {
|
||||
case IOSM_A2C_AP_READY:
|
||||
atomic_set(&mdm_ready, 1);
|
||||
iodevs_for_each(ld->msd, action, mld);
|
||||
break;
|
||||
case IOSM_A2C_OPEN_CH:
|
||||
#ifdef CONFIG_LINK_POWER_MANAGEMENT
|
||||
if (msg->ack.ch_id == SIPC5_CH_ID_FMT_0) {
|
||||
if (cp_online(mc) && mld->permit_cp_sleep)
|
||||
mld->permit_cp_sleep(mld);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case IOSM_A2C_CLOSE_CH:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nack_handler(struct mem_link_device *mld, struct iosm_msg *msg)
|
||||
{
|
||||
mif_err("got nack for msg id = 0x%x\n", msg->nack.msg_id);
|
||||
}
|
||||
|
||||
static struct {
|
||||
u16 cmd;
|
||||
char *name;
|
||||
void (*handler)(struct mem_link_device *mld, struct iosm_msg *msg);
|
||||
} iosm_handler[] = {
|
||||
{ IOSM_C2A_MDM_READY, "MDM_READY", mdm_ready_handler },
|
||||
{ IOSM_C2A_CONF_CH_RSP, "CONFG_CH_RSP", conf_ch_rsp_handler },
|
||||
{ IOSM_C2A_STOP_TX_CH, "STOP_TX_CH", stop_tx_ch_handler },
|
||||
{ IOSM_C2A_START_TX_CH, "START_TX_CH", start_tx_ch_handler },
|
||||
{ IOSM_C2A_ACK, "ACK", ack_handler },
|
||||
{ IOSM_C2A_NACK, "NACK", nack_handler },
|
||||
};
|
||||
|
||||
void iosm_event_work(struct work_struct *work)
|
||||
{
|
||||
struct iosm_msg_area *base;
|
||||
struct iosm_msg_area_head *hdr;
|
||||
struct iosm_msg *msg;
|
||||
u32 i, size;
|
||||
struct mem_link_device *mld =
|
||||
container_of(work, struct mem_link_device, iosm_w);
|
||||
|
||||
base = (struct iosm_msg_area *) (mld->base + IOSM_MSG_RX_OFFSET);
|
||||
hdr = &base->hdr;
|
||||
|
||||
if (unlikely(circ_empty(hdr->w_idx, hdr->r_idx))) {
|
||||
mif_info("iosm message area is full\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The message receiver determines the number of available messages
|
||||
* based on the read and write index.
|
||||
*/
|
||||
size = circ_get_usage(IOSM_NUM_ELEMENTS, hdr->w_idx, hdr->r_idx);
|
||||
mif_debug("number of available messages = %d\n", size);
|
||||
|
||||
while (size--) {
|
||||
msg = &base->elements[hdr->r_idx];
|
||||
for (i = 0; i < ARRAY_SIZE(iosm_handler); i++)
|
||||
if (iosm_handler[i].cmd == msg->msg_id) {
|
||||
mif_info("got msg %s\n", iosm_handler[i].name);
|
||||
(*iosm_handler[i].handler)(mld, msg);
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(iosm_handler))
|
||||
mif_err("0x%x message is not supported\n", msg->msg_id);
|
||||
/* read index is increamented by the number of read messages */
|
||||
hdr->r_idx = circ_new_ptr(IOSM_NUM_ELEMENTS, hdr->r_idx, 1);
|
||||
}
|
||||
pr_circ_idx(hdr);
|
||||
}
|
||||
|
||||
void iosm_event_bh(struct mem_link_device *mld, u16 cmd)
|
||||
{
|
||||
queue_work(iosm_wq, &mld->iosm_w);
|
||||
}
|
||||
|
||||
static int __init iosm_init(void)
|
||||
{
|
||||
iosm_wq = create_singlethread_workqueue("iosm_wq");
|
||||
if (!iosm_wq) {
|
||||
mif_err("ERR! fail to create tx_wq\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
mutex_init(&iosm_mtx);
|
||||
atomic_set(&mdm_ready, 0);
|
||||
mif_info("iosm_msg size = %ld, num of iosm elements = %ld\n",
|
||||
(long)sizeof(struct iosm_msg), (long)IOSM_NUM_ELEMENTS);
|
||||
return 0;
|
||||
}
|
||||
module_init(iosm_init);
|
||||
|
||||
static void __exit iosm_exit(void)
|
||||
{
|
||||
destroy_workqueue(iosm_wq);
|
||||
}
|
||||
module_exit(iosm_exit);
|
||||
|
||||
#endif
|
||||
47
drivers/misc/modem_v1/link_ctrlmsg_iosm.h
Normal file
47
drivers/misc/modem_v1/link_ctrlmsg_iosm.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Samsung Electronics.
|
||||
*
|
||||
* Author: Chulhee Park <chul2.park@samsung.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINK_CONTROL_MSG_IOSM_H
|
||||
#define LINK_CONTROL_MSG_IOSM_H
|
||||
|
||||
/* direction: CP -> AP */
|
||||
#define IOSM_C2A_MDM_READY 0x80
|
||||
#define IOSM_C2A_CONF_CH_RSP 0xA3 /* The answer of flow control msg */
|
||||
#define IOSM_C2A_STOP_TX_CH 0xB0
|
||||
#define IOSM_C2A_START_TX_CH 0xB1
|
||||
#define IOSM_C2A_ACK 0xE0
|
||||
#define IOSM_C2A_NACK 0xE1
|
||||
|
||||
/* direction: AP -> CP */
|
||||
#define IOSM_A2C_AP_READY 0x00
|
||||
#define IOSM_A2C_CONF_CH_REQ 0x22 /* flow control on/off */
|
||||
#define IOSM_A2C_OPEN_CH 0x24
|
||||
#define IOSM_A2C_CLOSE_CH 0x25
|
||||
#define IOSM_A2C_STOP_TX_CH 0x30
|
||||
#define IOSM_A2C_START_TX_CH 0x30
|
||||
#define IOSM_A2C_ACK 0x60
|
||||
#define IOSM_A2C_NACK 0x61
|
||||
|
||||
#define IOSM_TRANS_ID_MAX 255
|
||||
#define IOSM_MSG_AREA_SIZE (CTRL_RGN_SIZE / 2)
|
||||
#define IOSM_MSG_TX_OFFSET CMD_RGN_OFFSET
|
||||
#define IOSM_MSG_RX_OFFSET (CMD_RGN_OFFSET + IOSM_MSG_AREA_SIZE)
|
||||
#define IOSM_MSG_DESC_OFFSET (CMD_RGN_OFFSET + CMD_RGN_SIZE)
|
||||
|
||||
void tx_iosm_message(struct mem_link_device *mld, u8 id, u32 *args);
|
||||
void iosm_event_work(struct work_struct *work);
|
||||
void iosm_event_bh(struct mem_link_device *mld, u16 cmd);
|
||||
#endif
|
||||
148
drivers/misc/modem_v1/link_device_hsic.h
Normal file
148
drivers/misc/modem_v1/link_device_hsic.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_LINK_DEVICE_USB_H__
|
||||
#define __MODEM_LINK_DEVICE_USB_H__
|
||||
|
||||
#include <linux/rtc.h>
|
||||
|
||||
enum {
|
||||
IF_USB_BOOT_EP = 0,
|
||||
IF_USB_FMT_EP = 0,
|
||||
IF_USB_RAW_EP,
|
||||
IF_USB_RFS_EP,
|
||||
IF_USB_CMD_EP,
|
||||
_IF_USB_ACMNUM_MAX,
|
||||
};
|
||||
|
||||
#define PS_DATA_CH_01 0xa
|
||||
#define MULTI_URB 4
|
||||
|
||||
enum {
|
||||
BOOT_DOWN = 0,
|
||||
IPC_CHANNEL
|
||||
};
|
||||
|
||||
enum ch_state {
|
||||
STATE_SUSPENDED,
|
||||
STATE_RESUMED,
|
||||
};
|
||||
|
||||
struct link_pm_info {
|
||||
struct usb_link_device *usb_ld;
|
||||
};
|
||||
|
||||
struct if_usb_devdata;
|
||||
struct usb_id_info {
|
||||
int intf_id;
|
||||
int urb_cnt;
|
||||
int flags;
|
||||
#define FLAG_BOOT_DOWN 0x0001 /* USB/HSIC BOOTROM interface */
|
||||
#define FLAG_IPC_CHANNEL 0x0002 /* normal packet rx/tx interface */
|
||||
#define FLAG_SEND_NZLP 0x0004 /* hw doesn't require ZLP for tx */
|
||||
unsigned int rx_buf_size;
|
||||
struct usb_link_device *usb_ld;
|
||||
|
||||
char *description;
|
||||
int (*bind)(struct if_usb_devdata *, struct usb_interface *,
|
||||
struct usb_link_device *);
|
||||
void (*unbind)(struct if_usb_devdata *, struct usb_interface *);
|
||||
int (*rx_fixup)(struct if_usb_devdata *, struct sk_buff *skb);
|
||||
struct sk_buff *(*tx_fixup)(struct if_usb_devdata *dev,
|
||||
struct sk_buff *skb, gfp_t flags);
|
||||
void (*intr_complete)(struct urb *urb);
|
||||
};
|
||||
|
||||
struct mif_skb_pool;
|
||||
|
||||
struct if_usb_devdata {
|
||||
struct usb_interface *data_intf;
|
||||
struct usb_link_device *usb_ld;
|
||||
struct usb_device *usbdev;
|
||||
unsigned int tx_pipe;
|
||||
unsigned int rx_pipe;
|
||||
struct usb_host_endpoint *status;
|
||||
u8 disconnected;
|
||||
|
||||
int format;
|
||||
int idx;
|
||||
|
||||
/* Multi-URB style*/
|
||||
struct usb_anchor urbs;
|
||||
struct usb_anchor reading;
|
||||
|
||||
struct urb *intr_urb;
|
||||
unsigned int rx_buf_size;
|
||||
enum ch_state state;
|
||||
|
||||
struct usb_id_info *info;
|
||||
/* SubClass expend data - optional */
|
||||
void *sedata;
|
||||
struct io_device *iod;
|
||||
unsigned long flags;
|
||||
|
||||
struct sk_buff_head free_rx_q;
|
||||
struct sk_buff_head sk_tx_q;
|
||||
unsigned tx_pend;
|
||||
struct timespec txpend_ts;
|
||||
struct rtc_time txpend_tm;
|
||||
struct usb_anchor tx_deferd_urbs;
|
||||
struct net_device *ndev;
|
||||
int net_suspend;
|
||||
bool net_connected;
|
||||
|
||||
bool defered_rx;
|
||||
struct delayed_work rx_defered_work;
|
||||
|
||||
struct mif_skb_pool *ntb_pool;
|
||||
};
|
||||
|
||||
struct usb_link_device {
|
||||
/*COMMON LINK DEVICE*/
|
||||
struct link_device ld;
|
||||
|
||||
/*USB SPECIFIC LINK DEVICE*/
|
||||
struct usb_device *usbdev;
|
||||
int max_link_ch;
|
||||
int max_acm_ch;
|
||||
int acm_cnt;
|
||||
struct if_usb_devdata *devdata;
|
||||
unsigned int suspended;
|
||||
int if_usb_connected;
|
||||
|
||||
struct delayed_work link_event;
|
||||
unsigned long events;
|
||||
|
||||
struct notifier_block phy_nfb;
|
||||
struct notifier_block pm_nfb;
|
||||
|
||||
/* for debug */
|
||||
unsigned debug_pending;
|
||||
unsigned tx_cnt;
|
||||
unsigned rx_cnt;
|
||||
unsigned tx_err;
|
||||
unsigned rx_err;
|
||||
};
|
||||
|
||||
enum bit_link_events {
|
||||
LINK_EVENT_RECOVERY,
|
||||
};
|
||||
|
||||
/* converts from struct link_device* to struct xxx_link_device* */
|
||||
#define to_usb_link_device(linkdev) \
|
||||
container_of(linkdev, struct usb_link_device, ld)
|
||||
|
||||
int usb_tx_skb(struct if_usb_devdata *pipe_data, struct sk_buff *skb);
|
||||
#endif
|
||||
849
drivers/misc/modem_v1/link_device_memory.h
Normal file
849
drivers/misc/modem_v1/link_device_memory.h
Normal file
|
|
@ -0,0 +1,849 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_LINK_DEVICE_MEMORY_H__
|
||||
#define __MODEM_LINK_DEVICE_MEMORY_H__
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sched/rt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "include/link_device_memory_config.h"
|
||||
#include "include/circ_queue.h"
|
||||
#include "include/sbd.h"
|
||||
#include "include/sipc5.h"
|
||||
|
||||
#ifdef GROUP_MEM_TYPE
|
||||
|
||||
enum mem_iface_type {
|
||||
MEM_EXT_DPRAM = 0x0001, /* External DPRAM */
|
||||
MEM_AP_IDPRAM = 0x0002, /* DPRAM in AP */
|
||||
MEM_CP_IDPRAM = 0x0004, /* DPRAM in CP */
|
||||
MEM_PLD_DPRAM = 0x0008, /* PLD or FPGA */
|
||||
MEM_SYS_SHMEM = 0x0100, /* Shared-memory (SHMEM) on a system bus */
|
||||
MEM_C2C_SHMEM = 0x0200, /* SHMEM with C2C (Chip-to-chip) interface */
|
||||
MEM_LLI_SHMEM = 0x0400, /* SHMEM with MIPI-LLI interface */
|
||||
};
|
||||
|
||||
#define MEM_DPRAM_TYPE_MASK 0x00FF
|
||||
#define MEM_SHMEM_TYPE_MASK 0xFF00
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_TYPE_SHMEM
|
||||
|
||||
#define SHM_4M_RESERVED_SZ 4056
|
||||
#define SHM_4M_FMT_TX_BUFF_SZ 4096
|
||||
#define SHM_4M_FMT_RX_BUFF_SZ 4096
|
||||
#define SHM_4M_RAW_TX_BUFF_SZ 2084864
|
||||
#define SHM_4M_RAW_RX_BUFF_SZ 2097152
|
||||
|
||||
#define SHM_UL_USAGE_LIMIT SZ_32K /* Uplink burst limit */
|
||||
|
||||
#if defined (CONFIG_SOC_EXYNOS7570)
|
||||
#define FLOW_CTRL_BIT (0x1<<3)
|
||||
#else
|
||||
#define FLOW_CTRL_BIT 0x10
|
||||
#endif
|
||||
|
||||
struct __packed shmem_4mb_phys_map {
|
||||
u32 magic;
|
||||
u32 access;
|
||||
|
||||
u32 fmt_tx_head;
|
||||
u32 fmt_tx_tail;
|
||||
|
||||
u32 fmt_rx_head;
|
||||
u32 fmt_rx_tail;
|
||||
|
||||
u32 raw_tx_head;
|
||||
u32 raw_tx_tail;
|
||||
|
||||
u32 raw_rx_head;
|
||||
u32 raw_rx_tail;
|
||||
|
||||
char reserved[SHM_4M_RESERVED_SZ];
|
||||
|
||||
char fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ];
|
||||
char fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ];
|
||||
|
||||
char raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ];
|
||||
char raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_INTERRUPT
|
||||
|
||||
#define MASK_INT_VALID 0x0080
|
||||
#define MASK_TX_FLOWCTL_SUSPEND 0x0010
|
||||
#define MASK_TX_FLOWCTL_RESUME 0x0000
|
||||
|
||||
#define MASK_CMD_VALID 0x0040
|
||||
#define MASK_CMD_FIELD 0x003F
|
||||
|
||||
#define MASK_REQ_ACK_FMT 0x0020
|
||||
#define MASK_REQ_ACK_RAW 0x0010
|
||||
#define MASK_RES_ACK_FMT 0x0008
|
||||
#define MASK_RES_ACK_RAW 0x0004
|
||||
#define MASK_SEND_FMT 0x0002
|
||||
#define MASK_SEND_RAW 0x0001
|
||||
#define MASK_SEND_DATA 0x0001
|
||||
|
||||
#define CMD_INIT_START 0x0001
|
||||
#define CMD_INIT_END 0x0002
|
||||
#define CMD_REQ_ACTIVE 0x0003
|
||||
#define CMD_RES_ACTIVE 0x0004
|
||||
#define CMD_REQ_TIME_SYNC 0x0005
|
||||
#define CMD_CRASH_RESET 0x0007
|
||||
#define CMD_PHONE_START 0x0008
|
||||
#define CMD_CRASH_EXIT 0x0009
|
||||
#define CMD_CP_DEEP_SLEEP 0x000A
|
||||
#define CMD_NV_REBUILDING 0x000B
|
||||
#define CMD_EMER_DOWN 0x000C
|
||||
#define CMD_PIF_INIT_DONE 0x000D
|
||||
#define CMD_SILENT_NV_REBUILD 0x000E
|
||||
#define CMD_NORMAL_POWER_OFF 0x000F
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_IPC_DEVICE
|
||||
|
||||
struct mem_ipc_device {
|
||||
enum dev_format id;
|
||||
char name[16];
|
||||
|
||||
struct circ_queue txq;
|
||||
struct circ_queue rxq;
|
||||
|
||||
u16 msg_mask;
|
||||
u16 req_ack_mask;
|
||||
u16 res_ack_mask;
|
||||
|
||||
struct sk_buff_head *skb_txq;
|
||||
struct sk_buff_head *skb_rxq;
|
||||
|
||||
unsigned int req_ack_cnt[MAX_DIR];
|
||||
|
||||
spinlock_t tx_lock;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_FLOW_CONTROL
|
||||
#define MAX_SKB_TXQ_DEPTH 1024
|
||||
#define TX_PERIOD_MS 1 /* 1 ms */
|
||||
#define MAX_TX_BUSY_COUNT 1024
|
||||
#define BUSY_COUNT_MASK 0xF
|
||||
|
||||
#define RES_ACK_WAIT_TIMEOUT 10 /* 10 ms */
|
||||
|
||||
#define TXQ_STOP_MASK (0x1<<0)
|
||||
#define TX_SUSPEND_MASK (0x1<<1)
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_CP_CRASH
|
||||
|
||||
#define FORCE_CRASH_ACK_TIMEOUT (10 * HZ)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SNAPSHOT
|
||||
|
||||
struct __packed mem_snapshot {
|
||||
/* Timestamp */
|
||||
struct timespec ts;
|
||||
|
||||
/* Direction (TX or RX) */
|
||||
enum direction dir;
|
||||
|
||||
/* The status of memory interface at the time */
|
||||
unsigned int magic;
|
||||
unsigned int access;
|
||||
|
||||
unsigned int head[MAX_SIPC5_DEVICES][MAX_DIR];
|
||||
unsigned int tail[MAX_SIPC5_DEVICES][MAX_DIR];
|
||||
|
||||
u16 int2ap;
|
||||
u16 int2cp;
|
||||
};
|
||||
|
||||
struct mst_buff {
|
||||
/* These two members must be first. */
|
||||
struct mst_buff *next;
|
||||
struct mst_buff *prev;
|
||||
|
||||
struct mem_snapshot snapshot;
|
||||
};
|
||||
|
||||
struct mst_buff_head {
|
||||
/* These two members must be first. */
|
||||
struct mst_buff *next;
|
||||
struct mst_buff *prev;
|
||||
|
||||
u32 qlen;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_DEVICE
|
||||
|
||||
enum mem_ipc_mode {
|
||||
MEM_LEGACY_IPC,
|
||||
MEM_SBD_IPC,
|
||||
};
|
||||
|
||||
#define MEM_CRASH_REASON_CP 0
|
||||
#define MEM_CRASH_REASON_AP 1
|
||||
#define MEM_CRASH_REASON_RIL 2
|
||||
#define MEM_CRASH_REASON_SIZE 512
|
||||
|
||||
struct crash_reason {
|
||||
u32 owner;
|
||||
char string[MEM_CRASH_REASON_SIZE];
|
||||
};
|
||||
|
||||
#define FREQ_MAX_LV (40)
|
||||
|
||||
struct freq_table {
|
||||
int num_of_table;
|
||||
u32 freq[FREQ_MAX_LV];
|
||||
};
|
||||
|
||||
struct mem_link_device {
|
||||
/**
|
||||
* COMMON and MANDATORY to all link devices
|
||||
*/
|
||||
struct link_device link_dev;
|
||||
|
||||
/**
|
||||
* MEMORY type
|
||||
*/
|
||||
enum mem_iface_type type;
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*/
|
||||
unsigned long attrs; /* Set of link_attr_bit flags */
|
||||
|
||||
/**
|
||||
* Flags
|
||||
*/
|
||||
bool dpram_magic; /* DPRAM-style magic code */
|
||||
bool iosm; /* IOSM message */
|
||||
|
||||
size_t shm_size;
|
||||
/**
|
||||
* {physical address, size, virtual address} for BOOT region
|
||||
*/
|
||||
phys_addr_t boot_start;
|
||||
size_t boot_size;
|
||||
struct page **boot_pages; /* pointer to the page table for vmap */
|
||||
u8 __iomem *boot_base;
|
||||
|
||||
/**
|
||||
* {physical address, size, virtual address} for IPC region
|
||||
*/
|
||||
phys_addr_t start;
|
||||
size_t size;
|
||||
struct page **pages; /* pointer to the page table for vmap */
|
||||
u8 __iomem *base;
|
||||
|
||||
/**
|
||||
* CP Binary size for CRC checking
|
||||
*/
|
||||
u32 cp_binary_size;
|
||||
|
||||
/**
|
||||
* (u32 *) syscp_alive[0] = Magic Code, Version
|
||||
* (u32 *) syscp_alive[1] = CP Reserved Size
|
||||
* (u32 *) syscp_alive[2] = Shared Mem Size
|
||||
*/
|
||||
struct resource *syscp_info;
|
||||
/**
|
||||
* Actual logical IPC devices (for IPC_FMT and IPC_RAW)
|
||||
*/
|
||||
struct mem_ipc_device ipc_dev[MAX_SIPC5_DEVICES];
|
||||
|
||||
/**
|
||||
* Pointers (aliases) to IPC device map
|
||||
*/
|
||||
u32 __iomem *magic;
|
||||
u32 __iomem *access;
|
||||
u32 __iomem *clk_table;
|
||||
struct mem_ipc_device *dev[MAX_SIPC5_DEVICES];
|
||||
|
||||
struct sbd_link_device sbd_link_dev;
|
||||
struct work_struct iosm_w;
|
||||
|
||||
/**
|
||||
* GPIO#, MBOX#, IRQ# for IPC
|
||||
*/
|
||||
unsigned int mbx_cp2ap_msg; /* MBOX# for IPC RX */
|
||||
unsigned int irq_cp2ap_msg; /* IRQ# for IPC RX */
|
||||
|
||||
unsigned int mbx_ap2cp_msg; /* MBOX# for IPC TX */
|
||||
unsigned int int_ap2cp_msg; /* INTR# for IPC TX */
|
||||
|
||||
unsigned int mbx_perf_req;
|
||||
unsigned int mbx_perf_req_cpu;
|
||||
unsigned int mbx_perf_req_mif;
|
||||
unsigned int mbx_perf_req_int;
|
||||
unsigned int irq_perf_req;
|
||||
unsigned int irq_perf_req_cpu;
|
||||
unsigned int irq_perf_req_mif;
|
||||
unsigned int irq_perf_req_int;
|
||||
struct work_struct pm_qos_work;
|
||||
struct work_struct pm_qos_work_cpu;
|
||||
struct work_struct pm_qos_work_mif;
|
||||
struct work_struct pm_qos_work_int;
|
||||
|
||||
struct freq_table cpu_table;
|
||||
struct freq_table mif_table;
|
||||
struct freq_table int_table;
|
||||
|
||||
unsigned int *ap_clk_table;
|
||||
unsigned int ap_clk_cnt;
|
||||
|
||||
unsigned int *mif_clk_table;
|
||||
unsigned int mif_clk_cnt;
|
||||
|
||||
unsigned int *int_clk_table;
|
||||
unsigned int int_clk_cnt;
|
||||
|
||||
int current_mif_val;
|
||||
|
||||
unsigned int mbx_cp2ap_status; /* MBOX# for TX FLOWCTL */
|
||||
unsigned int irq_cp2ap_status; /* INTR# for TX FLOWCTL */
|
||||
unsigned int tx_flowctrl_cmd;
|
||||
|
||||
/**
|
||||
* Member variables for TX & RX
|
||||
*/
|
||||
struct mst_buff_head msb_rxq;
|
||||
struct mst_buff_head msb_log;
|
||||
|
||||
struct tasklet_struct rx_tsk;
|
||||
|
||||
struct hrtimer tx_timer;
|
||||
struct hrtimer sbd_tx_timer;
|
||||
|
||||
struct work_struct page_reclaim_work;
|
||||
|
||||
/**
|
||||
* Member variables for CP booting and crash dump
|
||||
*/
|
||||
struct delayed_work udl_rx_dwork;
|
||||
struct std_dload_info img_info; /* Information of each binary image */
|
||||
atomic_t cp_boot_done;
|
||||
|
||||
/**
|
||||
* Mandatory methods for the common memory-type interface framework
|
||||
*/
|
||||
void (*send_ap2cp_irq)(struct mem_link_device *mld, u16 mask);
|
||||
|
||||
/**
|
||||
* Optional methods for some kind of memory-type interface media
|
||||
*/
|
||||
u16 (*recv_cp2ap_irq)(struct mem_link_device *mld);
|
||||
u16 (*read_ap2cp_irq)(struct mem_link_device *mld);
|
||||
u16 (*recv_cp2ap_status)(struct mem_link_device *mld);
|
||||
void (*finalize_cp_start)(struct mem_link_device *mld);
|
||||
void (*unmap_region)(void *rgn);
|
||||
void (*debug_info)(void);
|
||||
void (*cmd_handler)(struct mem_link_device *mld, u16 cmd);
|
||||
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
/* for logging MEMORY dump */
|
||||
struct work_struct dump_work;
|
||||
char dump_path[MIF_MAX_PATH_LEN];
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_POWER_MANAGEMENT
|
||||
#ifdef CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM
|
||||
unsigned int gpio_ap_wakeup; /* CP-to-AP wakeup GPIO */
|
||||
unsigned int gpio_cp_wakeup; /* AP-to-CP wakeup GPIO */
|
||||
unsigned int gpio_cp_status; /* CP-to-AP status GPIO */
|
||||
unsigned int gpio_ap_status; /* AP-to-CP status GPIO */
|
||||
#else
|
||||
unsigned int gpio_ap_wakeup; /* CP-to-AP wakeup GPIO */
|
||||
struct modem_irq irq_ap_wakeup; /* CP-to-AP wakeup IRQ */
|
||||
|
||||
unsigned int gpio_cp_wakeup; /* AP-to-CP wakeup GPIO */
|
||||
|
||||
unsigned int gpio_cp_status; /* CP-to-AP status GPIO */
|
||||
struct modem_irq irq_cp_status; /* CP-to-AP status IRQ */
|
||||
|
||||
unsigned int gpio_ap_status; /* AP-to-CP status GPIO */
|
||||
|
||||
struct wake_lock ap_wlock; /* locked by ap_wakeup */
|
||||
struct wake_lock cp_wlock; /* locked by cp_status */
|
||||
|
||||
struct workqueue_struct *pm_wq;
|
||||
struct delayed_work cp_sleep_dwork; /* to hold ap2cp_wakeup */
|
||||
|
||||
spinlock_t pm_lock;
|
||||
|
||||
unsigned long long last_cp2ap_intr;
|
||||
#endif
|
||||
atomic_t ref_cnt;
|
||||
|
||||
unsigned int gpio_ipc_int2cp; /* AP-to-CP send signal GPIO */
|
||||
spinlock_t sig_lock;
|
||||
|
||||
void (*start_pm)(struct mem_link_device *mld);
|
||||
void (*stop_pm)(struct mem_link_device *mld);
|
||||
void (*forbid_cp_sleep)(struct mem_link_device *mld);
|
||||
void (*permit_cp_sleep)(struct mem_link_device *mld);
|
||||
bool (*link_active)(struct mem_link_device *mld);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
struct dentry *dbgfs_dir;
|
||||
struct debugfs_blob_wrapper mem_dump_blob;
|
||||
struct dentry *dbgfs_frame;
|
||||
#endif
|
||||
|
||||
atomic_t forced_cp_crash;
|
||||
struct timer_list crash_ack_timer;
|
||||
|
||||
spinlock_t state_lock;
|
||||
enum link_state state;
|
||||
|
||||
struct pktlog_data *pktlog;
|
||||
struct crash_reason crash_reason;
|
||||
};
|
||||
|
||||
#define to_mem_link_device(ld) \
|
||||
container_of(ld, struct mem_link_device, link_dev)
|
||||
#define ld_to_mem_link_device(ld) \
|
||||
container_of(ld, struct mem_link_device, link_dev)
|
||||
#define sbd_to_mem_link_device(sl) \
|
||||
container_of(sl, struct mem_link_device, sbd_link_dev)
|
||||
|
||||
#define MEM_IPC_MAGIC 0xAA
|
||||
#define MEM_CRASH_MAGIC 0xDEADDEAD
|
||||
#define MEM_BOOT_MAGIC 0x424F4F54
|
||||
#define MEM_DUMP_MAGIC 0x44554D50
|
||||
|
||||
#define MAX_TABLE_COUNT 8
|
||||
|
||||
struct clock_table_info {
|
||||
char table_name[4];
|
||||
u32 table_count;
|
||||
};
|
||||
|
||||
struct clock_table {
|
||||
char parser_version[4];
|
||||
u32 total_table_count;
|
||||
struct clock_table_info table_info[MAX_TABLE_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_TYPE
|
||||
|
||||
static inline bool mem_type_shmem(enum mem_iface_type type)
|
||||
{
|
||||
return (type & MEM_SHMEM_TYPE_MASK) ? true : false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_INTERRUPT
|
||||
|
||||
static inline bool int_valid(u16 x)
|
||||
{
|
||||
return (x & MASK_INT_VALID) ? true : false;
|
||||
}
|
||||
|
||||
static inline u16 mask2int(u16 mask)
|
||||
{
|
||||
return mask | MASK_INT_VALID;
|
||||
}
|
||||
|
||||
/**
|
||||
@remark This must be invoked after validation with int_valid().
|
||||
*/
|
||||
static inline bool cmd_valid(u16 x)
|
||||
{
|
||||
return (x & MASK_CMD_VALID) ? true : false;
|
||||
|
||||
}
|
||||
|
||||
static inline bool chk_same_cmd(struct mem_link_device *mld, u16 x)
|
||||
{
|
||||
if (mld->tx_flowctrl_cmd == x) {
|
||||
return true;
|
||||
} else {
|
||||
mld->tx_flowctrl_cmd = x;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u16 int2cmd(u16 x)
|
||||
{
|
||||
return x & MASK_CMD_FIELD;
|
||||
}
|
||||
|
||||
static inline u16 cmd2int(u16 cmd)
|
||||
{
|
||||
return mask2int(cmd | MASK_CMD_VALID);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_IPC_DEVICE
|
||||
|
||||
static inline struct circ_queue *cq(struct mem_ipc_device *dev,
|
||||
enum direction dir)
|
||||
{
|
||||
return (dir == TX) ? &dev->txq : &dev->rxq;
|
||||
}
|
||||
|
||||
static inline unsigned int get_txq_head(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_head(&dev->txq);
|
||||
}
|
||||
|
||||
static inline void set_txq_head(struct mem_ipc_device *dev, unsigned int in)
|
||||
{
|
||||
set_head(&dev->txq, in);
|
||||
}
|
||||
|
||||
static inline unsigned int get_txq_tail(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_tail(&dev->txq);
|
||||
}
|
||||
|
||||
static inline void set_txq_tail(struct mem_ipc_device *dev, unsigned int out)
|
||||
{
|
||||
set_tail(&dev->txq, out);
|
||||
}
|
||||
|
||||
static inline char *get_txq_buff(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_buff(&dev->txq);
|
||||
}
|
||||
|
||||
static inline unsigned int get_txq_buff_size(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_size(&dev->txq);
|
||||
}
|
||||
|
||||
static inline unsigned int get_rxq_head(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_head(&dev->rxq);
|
||||
}
|
||||
|
||||
static inline void set_rxq_head(struct mem_ipc_device *dev, unsigned int in)
|
||||
{
|
||||
set_head(&dev->rxq, in);
|
||||
}
|
||||
|
||||
static inline unsigned int get_rxq_tail(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_tail(&dev->rxq);
|
||||
}
|
||||
|
||||
static inline void set_rxq_tail(struct mem_ipc_device *dev, unsigned int out)
|
||||
{
|
||||
set_tail(&dev->rxq, out);
|
||||
}
|
||||
|
||||
static inline char *get_rxq_buff(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_buff(&dev->rxq);
|
||||
}
|
||||
|
||||
static inline unsigned int get_rxq_buff_size(struct mem_ipc_device *dev)
|
||||
{
|
||||
return get_size(&dev->rxq);
|
||||
}
|
||||
|
||||
static inline u16 msg_mask(struct mem_ipc_device *dev)
|
||||
{
|
||||
return dev->msg_mask;
|
||||
}
|
||||
|
||||
static inline u16 req_ack_mask(struct mem_ipc_device *dev)
|
||||
{
|
||||
return dev->req_ack_mask;
|
||||
}
|
||||
|
||||
static inline u16 res_ack_mask(struct mem_ipc_device *dev)
|
||||
{
|
||||
return dev->res_ack_mask;
|
||||
}
|
||||
|
||||
static inline bool req_ack_valid(struct mem_ipc_device *dev, u16 val)
|
||||
{
|
||||
if (!cmd_valid(val) && (val & req_ack_mask(dev)))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool res_ack_valid(struct mem_ipc_device *dev, u16 val)
|
||||
{
|
||||
if (!cmd_valid(val) && (val & res_ack_mask(dev)))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool rxq_empty(struct mem_ipc_device *dev)
|
||||
{
|
||||
u32 head;
|
||||
u32 tail;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->rxq.lock, flags);
|
||||
|
||||
head = get_rxq_head(dev);
|
||||
tail = get_rxq_tail(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->rxq.lock, flags);
|
||||
|
||||
return circ_empty(head, tail);
|
||||
}
|
||||
|
||||
static inline bool txq_empty(struct mem_ipc_device *dev)
|
||||
{
|
||||
u32 head;
|
||||
u32 tail;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->txq.lock, flags);
|
||||
|
||||
head = get_txq_head(dev);
|
||||
tail = get_txq_tail(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->txq.lock, flags);
|
||||
|
||||
return circ_empty(head, tail);
|
||||
}
|
||||
|
||||
static inline enum dev_format dev_id(enum sipc_ch_id ch)
|
||||
{
|
||||
return sipc5_fmt_ch(ch) ? IPC_FMT : IPC_RAW;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_DEVICE
|
||||
|
||||
static inline unsigned int get_magic(struct mem_link_device *mld)
|
||||
{
|
||||
return ioread32(mld->magic);
|
||||
}
|
||||
|
||||
static inline unsigned int get_access(struct mem_link_device *mld)
|
||||
{
|
||||
return ioread32(mld->access);
|
||||
}
|
||||
|
||||
static inline void set_magic(struct mem_link_device *mld, unsigned int val)
|
||||
{
|
||||
iowrite32(val, mld->magic);
|
||||
}
|
||||
|
||||
static inline void set_access(struct mem_link_device *mld, unsigned int val)
|
||||
{
|
||||
iowrite32(val, mld->access);
|
||||
}
|
||||
|
||||
void shmem_unlock_mif_freq(struct mem_link_device *mld);
|
||||
void shmem_restore_mif_freq(struct mem_link_device *mld);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SNAPSHOT
|
||||
|
||||
int msb_init(void);
|
||||
|
||||
struct mst_buff *msb_alloc(void);
|
||||
void msb_free(struct mst_buff *msb);
|
||||
|
||||
void msb_queue_head_init(struct mst_buff_head *list);
|
||||
void msb_queue_tail(struct mst_buff_head *list, struct mst_buff *msb);
|
||||
void msb_queue_head(struct mst_buff_head *list, struct mst_buff *msb);
|
||||
|
||||
struct mst_buff *msb_dequeue(struct mst_buff_head *list);
|
||||
|
||||
void msb_queue_purge(struct mst_buff_head *list);
|
||||
|
||||
struct mst_buff *mem_take_snapshot(struct mem_link_device *mld,
|
||||
enum direction dir);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_INTERRUPT
|
||||
|
||||
static inline void send_ipc_irq(struct mem_link_device *mld, u16 val)
|
||||
{
|
||||
if (likely(mld->send_ap2cp_irq))
|
||||
mld->send_ap2cp_irq(mld, val);
|
||||
}
|
||||
|
||||
void mem_irq_handler(struct mem_link_device *mld, struct mst_buff *msb);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SETUP
|
||||
|
||||
void __iomem *mem_vmap(phys_addr_t pa, size_t size, struct page *pages[]);
|
||||
void mem_vunmap(void *va);
|
||||
|
||||
int mem_register_boot_rgn(struct mem_link_device *mld, phys_addr_t start,
|
||||
size_t size);
|
||||
void mem_unregister_boot_rgn(struct mem_link_device *mld);
|
||||
int mem_setup_boot_map(struct mem_link_device *mld);
|
||||
|
||||
int mem_register_ipc_rgn(struct mem_link_device *mld, phys_addr_t start,
|
||||
size_t size);
|
||||
void mem_unregister_ipc_rgn(struct mem_link_device *mld);
|
||||
void mem_setup_ipc_map(struct mem_link_device *mld);
|
||||
|
||||
struct mem_link_device *mem_create_link_device(enum mem_iface_type type,
|
||||
struct modem_data *modem);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_COMMAND
|
||||
|
||||
int mem_reset_ipc_link(struct mem_link_device *mld);
|
||||
void mem_cmd_handler(struct mem_link_device *mld, u16 cmd);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_FLOW_CONTROL
|
||||
|
||||
void tx_flowctrl_suspend(struct mem_link_device *mld);
|
||||
void tx_flowctrl_resume(struct mem_link_device *mld);
|
||||
void txq_stop(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
void txq_start(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
|
||||
int under_tx_flow_ctrl(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
int check_tx_flow_ctrl(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
|
||||
void send_req_ack(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
void recv_res_ack(struct mem_link_device *mld, struct mem_ipc_device *dev,
|
||||
struct mem_snapshot *mst);
|
||||
|
||||
void recv_req_ack(struct mem_link_device *mld, struct mem_ipc_device *dev,
|
||||
struct mem_snapshot *mst);
|
||||
void send_res_ack(struct mem_link_device *mld, struct mem_ipc_device *dev);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_CP_CRASH
|
||||
|
||||
void mem_handle_cp_crash(struct mem_link_device *mld, enum modem_state state);
|
||||
void mem_forced_cp_crash(struct mem_link_device *mld);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_LINK_DEBUG
|
||||
|
||||
void print_pm_status(struct mem_link_device *mld);
|
||||
|
||||
void print_req_ack(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev, enum direction dir);
|
||||
void print_res_ack(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev, enum direction dir);
|
||||
|
||||
void print_mem_snapshot(struct mem_link_device *mld, struct mem_snapshot *mst);
|
||||
void print_dev_snapshot(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev);
|
||||
|
||||
void save_mem_dump(struct mem_link_device *mld);
|
||||
void mem_dump_work(struct work_struct *ws);
|
||||
|
||||
#endif
|
||||
|
||||
static inline struct sk_buff *mem_alloc_skb(unsigned int len)
|
||||
{
|
||||
gfp_t priority;
|
||||
struct sk_buff *skb;
|
||||
|
||||
priority = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
|
||||
|
||||
skb = alloc_skb(len + NET_SKB_PAD, priority);
|
||||
if (!skb) {
|
||||
mif_err("ERR! alloc_skb(len:%d + pad:%d, gfp:0x%x) fail\n",
|
||||
len, NET_SKB_PAD, priority);
|
||||
#ifdef CONFIG_SEC_DEBUG_MIF_OOM
|
||||
show_mem(SHOW_MEM_FILTER_NODES);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb_reserve(skb, NET_SKB_PAD);
|
||||
return skb;
|
||||
}
|
||||
|
||||
#ifdef GROUP_MEM_LINK_IOSM_MESSAGE
|
||||
|
||||
/* direction: CP -> AP */
|
||||
#define IOSM_C2A_MDM_READY 0x80
|
||||
#define IOSM_C2A_CONF_CH_RSP 0xA3 /* answer of flow control msg */
|
||||
#define IOSM_C2A_STOP_TX_CH 0xB0
|
||||
#define IOSM_C2A_START_TX_CH 0xB1
|
||||
#define IOSM_C2A_ACK 0xE0
|
||||
#define IOSM_C2A_NACK 0xE1
|
||||
|
||||
/* direction: AP -> CP */
|
||||
#define IOSM_A2C_AP_READY 0x00
|
||||
#define IOSM_A2C_CONF_CH_REQ 0x22 /* flow control on/off */
|
||||
#define IOSM_A2C_OPEN_CH 0x24
|
||||
#define IOSM_A2C_CLOSE_CH 0x25
|
||||
#define IOSM_A2C_STOP_TX_CH 0x30
|
||||
#define IOSM_A2C_START_TX_CH 0x30
|
||||
#define IOSM_A2C_ACK 0x60
|
||||
#define IOSM_A2C_NACK 0x61
|
||||
|
||||
#define IOSM_TRANS_ID_MAX 255
|
||||
#define IOSM_MSG_AREA_SIZE (CTRL_RGN_SIZE / 2)
|
||||
#define IOSM_MSG_TX_OFFSET CMD_RGN_OFFSET
|
||||
#define IOSM_MSG_RX_OFFSET (CMD_RGN_OFFSET + IOSM_MSG_AREA_SIZE)
|
||||
#define IOSM_MSG_DESC_OFFSET (CMD_RGN_OFFSET + CMD_RGN_SIZE)
|
||||
|
||||
void tx_iosm_message(struct mem_link_device *, u8, u32 *);
|
||||
void iosm_event_work(struct work_struct *work);
|
||||
void iosm_event_bh(struct mem_link_device *mld, u16 cmd);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
|
||||
extern int is_rndis_use(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
218
drivers/misc/modem_v1/link_device_memory_command.c
Normal file
218
drivers/misc/modem_v1/link_device_memory_command.c
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
|
||||
#ifdef GROUP_MEM_LINK_COMMAND
|
||||
|
||||
static bool rild_ready(struct link_device *ld)
|
||||
{
|
||||
struct io_device *fmt_iod;
|
||||
struct io_device *rfs_iod;
|
||||
int fmt_opened;
|
||||
int rfs_opened;
|
||||
|
||||
fmt_iod = link_get_iod_with_channel(ld, SIPC5_CH_ID_FMT_0);
|
||||
if (!fmt_iod) {
|
||||
mif_err("%s: No FMT io_device\n", ld->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
rfs_iod = link_get_iod_with_channel(ld, SIPC5_CH_ID_RFS_0);
|
||||
if (!rfs_iod) {
|
||||
mif_err("%s: No RFS io_device\n", ld->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt_opened = atomic_read(&fmt_iod->opened);
|
||||
rfs_opened = atomic_read(&rfs_iod->opened);
|
||||
mif_err("%s: %s.opened=%d, %s.opened=%d\n", ld->name,
|
||||
fmt_iod->name, fmt_opened, rfs_iod->name, rfs_opened);
|
||||
if (fmt_opened > 0 && rfs_opened > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_WITH_SBD_ARCH
|
||||
static void cmd_init_start_handler(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
int err;
|
||||
|
||||
mif_err("%s: INIT_START <- %s (%s.state:%s cp_boot_done:%d)\n",
|
||||
ld->name, mc->name, mc->name, mc_state(mc),
|
||||
atomic_read(&mld->cp_boot_done));
|
||||
|
||||
if (!ld->sbd_ipc) {
|
||||
mif_err("%s: LINK_ATTR_SBD_IPC is NOT set\n", ld->name);
|
||||
return;
|
||||
}
|
||||
|
||||
err = init_sbd_link(&mld->sbd_link_dev);
|
||||
if (err < 0) {
|
||||
mif_err("%s: init_sbd_link fail(%d)\n", ld->name, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mld->attrs & LINK_ATTR(LINK_ATTR_IPC_ALIGNED))
|
||||
ld->aligned = true;
|
||||
else
|
||||
ld->aligned = false;
|
||||
|
||||
sbd_activate(&mld->sbd_link_dev);
|
||||
|
||||
mif_err("%s: PIF_INIT_DONE -> %s\n", ld->name, mc->name);
|
||||
send_ipc_irq(mld, cmd2int(CMD_PIF_INIT_DONE));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cmd_phone_start_handler(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
mif_err("%s: CP_START <- %s (%s.state:%s cp_boot_done:%d)\n",
|
||||
ld->name, mc->name, mc->name, mc_state(mc),
|
||||
atomic_read(&mld->cp_boot_done));
|
||||
|
||||
#ifdef CONFIG_LINK_POWER_MANAGEMENT
|
||||
if (mld->start_pm)
|
||||
mld->start_pm(mld);
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&ld->lock, flags);
|
||||
|
||||
if (ld->state == LINK_STATE_IPC) {
|
||||
/*
|
||||
If there is no INIT_END command from AP, CP sends a CP_START
|
||||
command to AP periodically until it receives INIT_END from AP
|
||||
even though it has already been in ONLINE state.
|
||||
*/
|
||||
if (rild_ready(ld)) {
|
||||
mif_err("%s: INIT_END -> %s\n", ld->name, mc->name);
|
||||
send_ipc_irq(mld, cmd2int(CMD_INIT_END));
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = mem_reset_ipc_link(mld);
|
||||
if (err) {
|
||||
mif_err("%s: mem_reset_ipc_link fail(%d)\n", ld->name, err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rild_ready(ld)) {
|
||||
mif_err("%s: INIT_END -> %s\n", ld->name, mc->name);
|
||||
send_ipc_irq(mld, cmd2int(CMD_INIT_END));
|
||||
atomic_set(&mld->cp_boot_done, 1);
|
||||
}
|
||||
|
||||
ld->state = LINK_STATE_IPC;
|
||||
|
||||
complete_all(&mc->init_cmpl);
|
||||
|
||||
exit:
|
||||
spin_unlock_irqrestore(&ld->lock, flags);
|
||||
}
|
||||
|
||||
static void cmd_crash_reset_handler(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ld->lock, flags);
|
||||
ld->state = LINK_STATE_OFFLINE;
|
||||
spin_unlock_irqrestore(&ld->lock, flags);
|
||||
|
||||
evt_log(0, "%s<-%s: ERR! CP_CRASH_RESET\n", ld->name, mc->name);
|
||||
|
||||
mem_handle_cp_crash(mld, STATE_CRASH_RESET);
|
||||
}
|
||||
|
||||
static void cmd_crash_exit_handler(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ld->lock, flags);
|
||||
ld->state = LINK_STATE_CP_CRASH;
|
||||
spin_unlock_irqrestore(&ld->lock, flags);
|
||||
|
||||
if (timer_pending(&mc->crash_ack_timer))
|
||||
del_timer(&mc->crash_ack_timer);
|
||||
|
||||
if (atomic_read(&mc->forced_cp_crash))
|
||||
evt_log(0, "%s<-%s: CP_CRASH_ACK\n", ld->name, mc->name);
|
||||
else
|
||||
evt_log(0, "%s<-%s: ERR! CP_CRASH_EXIT\n", ld->name, mc->name);
|
||||
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
if (!atomic_read(&mc->forced_cp_crash))
|
||||
queue_work(system_nrt_wq, &mld->dump_work);
|
||||
#endif
|
||||
|
||||
mem_handle_cp_crash(mld, STATE_CRASH_EXIT);
|
||||
}
|
||||
|
||||
void mem_cmd_handler(struct mem_link_device *mld, u16 cmd)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef CONFIG_LINK_DEVICE_WITH_SBD_ARCH
|
||||
case CMD_INIT_START:
|
||||
cmd_init_start_handler(mld);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case CMD_PHONE_START:
|
||||
cmd_phone_start_handler(mld);
|
||||
break;
|
||||
|
||||
case CMD_CRASH_RESET:
|
||||
cmd_crash_reset_handler(mld);
|
||||
break;
|
||||
|
||||
case CMD_CRASH_EXIT:
|
||||
cmd_crash_exit_handler(mld);
|
||||
break;
|
||||
|
||||
default:
|
||||
mif_err("%s: Unknown command 0x%04X\n", ld->name, cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
176
drivers/misc/modem_v1/link_device_memory_debug.c
Normal file
176
drivers/misc/modem_v1/link_device_memory_debug.c
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
|
||||
#ifdef GROUP_MEM_LINK_DEBUG
|
||||
|
||||
void print_pm_status(struct mem_link_device *mld)
|
||||
{
|
||||
#if defined(DEBUG_MODEM_IF) && defined(CONFIG_LINK_POWER_MANAGEMENT)
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
unsigned int magic;
|
||||
int ap_wakeup;
|
||||
int ap_status;
|
||||
int cp_wakeup;
|
||||
int cp_status;
|
||||
|
||||
magic = get_magic(mld);
|
||||
ap_wakeup = gpio_get_value(mld->gpio_ap_wakeup);
|
||||
ap_status = gpio_get_value(mld->gpio_ap_status);
|
||||
cp_wakeup = gpio_get_value(mld->gpio_cp_wakeup);
|
||||
cp_status = gpio_get_value(mld->gpio_cp_status);
|
||||
|
||||
/*
|
||||
** PM {ap_wakeup:cp_wakeup:cp_status:ap_status:magic} <CALLER>
|
||||
*/
|
||||
evt_log(0, "%s: PM {%d:%d:%d:%d:%X} %d <%pf>\n", ld->name,
|
||||
ap_wakeup, cp_wakeup, cp_status, ap_status, magic,
|
||||
atomic_read(&mld->ref_cnt), CALLER);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_req_ack(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev, enum direction dir)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
enum dev_format id = dev->id;
|
||||
unsigned int qsize = get_size(cq(dev, dir));
|
||||
unsigned int in = mst->head[id][dir];
|
||||
unsigned int out = mst->tail[id][dir];
|
||||
unsigned int usage = circ_get_usage(qsize, in, out);
|
||||
unsigned int space = circ_get_space(qsize, in, out);
|
||||
|
||||
evt_log(0, "REQ_ACK: %s%s%s: %s_%s.%d "
|
||||
"{in:%u out:%u usage:%u space:%u}\n",
|
||||
ld->name, arrow(dir), mc->name, dev->name, q_dir(dir),
|
||||
dev->req_ack_cnt[dir], in, out, usage, space);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_res_ack(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev, enum direction dir)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
enum dev_format id = dev->id;
|
||||
enum direction opp_dir = opposite(dir); /* opposite direction */
|
||||
unsigned int qsize = get_size(cq(dev, opp_dir));
|
||||
unsigned int in = mst->head[id][opp_dir];
|
||||
unsigned int out = mst->tail[id][opp_dir];
|
||||
unsigned int usage = circ_get_usage(qsize, in, out);
|
||||
unsigned int space = circ_get_space(qsize, in, out);
|
||||
|
||||
evt_log(0, "RES_ACK: %s%s%s: %s_%s.%d "
|
||||
"{in:%u out:%u usage:%u space:%u}\n",
|
||||
ld->name, arrow(dir), mc->name, dev->name, q_dir(opp_dir),
|
||||
dev->req_ack_cnt[opp_dir], in, out, usage, space);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_mem_snapshot(struct mem_link_device *mld, struct mem_snapshot *mst)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
evt_log(0, "%s: [%s] ACC{%X %d} FMT{TI:%u TO:%u RI:%u RO:%u} "
|
||||
"RAW{TI:%u TO:%u RI:%u RO:%u} INTR{RX:0x%X TX:0x%X}\n",
|
||||
ld->name, ipc_dir(mst->dir), mst->magic, mst->access,
|
||||
mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX],
|
||||
mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX],
|
||||
mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX],
|
||||
mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX],
|
||||
mst->int2ap, mst->int2cp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_dev_snapshot(struct mem_link_device *mld, struct mem_snapshot *mst,
|
||||
struct mem_ipc_device *dev)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
enum dev_format id = dev->id;
|
||||
|
||||
if (id > IPC_RAW)
|
||||
return;
|
||||
|
||||
evt_log(0, "%s: [%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u} | "
|
||||
"INTR{0x%02X}\n",
|
||||
ld->name, ipc_dir(mst->dir), dev->name,
|
||||
mst->head[id][TX], mst->tail[id][TX],
|
||||
mst->head[id][RX], mst->tail[id][RX],
|
||||
(mst->dir == RX) ? mst->int2ap : mst->int2cp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void save_mem_dump(struct mem_link_device *mld)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
char *path = mld->dump_path;
|
||||
struct file *fp;
|
||||
struct utc_time t;
|
||||
|
||||
get_utc_time(&t);
|
||||
snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump",
|
||||
MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min,
|
||||
t.sec);
|
||||
|
||||
fp = mif_open_file(path);
|
||||
if (!fp) {
|
||||
mif_err("%s: ERR! %s open fail\n", ld->name, path);
|
||||
return;
|
||||
}
|
||||
mif_err("%s: %s opened\n", ld->name, path);
|
||||
|
||||
mif_save_file(fp, mld->base, mld->size);
|
||||
|
||||
mif_close_file(fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mem_dump_work(struct work_struct *ws)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
struct mem_link_device *mld;
|
||||
|
||||
mld = container_of(ws, struct mem_link_device, dump_work);
|
||||
if (!mld) {
|
||||
mif_err("ERR! no mld\n");
|
||||
return;
|
||||
}
|
||||
|
||||
save_mem_dump(mld);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
219
drivers/misc/modem_v1/link_device_memory_flow_control.c
Normal file
219
drivers/misc/modem_v1/link_device_memory_flow_control.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
|
||||
#ifdef GROUP_MEM_FLOW_CONTROL
|
||||
|
||||
void txq_stop(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
if (dev->id == IPC_RAW && atomic_read(&dev->txq.busy) == 0) {
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
if (!test_bit(TXQ_STOP_MASK, &ld->tx_flowctrl_mask)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->txq.lock, flags);
|
||||
|
||||
atomic_set(&dev->txq.busy, 1);
|
||||
set_bit(TXQ_STOP_MASK, &ld->tx_flowctrl_mask);
|
||||
stop_net_ifaces(&mld->link_dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->txq.lock, flags);
|
||||
|
||||
send_req_ack(mld, dev);
|
||||
mif_err_limited("%s: %s TXQ BUSY, tx_flowctrl_mask=0x%04lx\n",
|
||||
ld->name, dev->name, ld->tx_flowctrl_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tx_flowctrl_suspend(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
if (!test_bit(TX_SUSPEND_MASK, &ld->tx_flowctrl_mask)) {
|
||||
unsigned long flags;
|
||||
struct mem_ipc_device *dev = mld->dev[IPC_RAW];
|
||||
|
||||
spin_lock_irqsave(&dev->txq.lock, flags);
|
||||
|
||||
set_bit(TX_SUSPEND_MASK, &ld->tx_flowctrl_mask);
|
||||
stop_net_ifaces(&mld->link_dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->txq.lock, flags);
|
||||
|
||||
mif_err_limited("%s: %s TX suspended, tx_flowctrl_mask=0x%04lx\n",
|
||||
ld->name, dev->name, ld->tx_flowctrl_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void txq_start(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
if (dev->id == IPC_RAW && atomic_read(&dev->txq.busy) > 0) {
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
if (test_bit(TXQ_STOP_MASK, &ld->tx_flowctrl_mask)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->txq.lock, flags);
|
||||
|
||||
atomic_set(&dev->txq.busy, 0);
|
||||
clear_bit(TXQ_STOP_MASK, &ld->tx_flowctrl_mask);
|
||||
|
||||
if (ld->tx_flowctrl_mask == 0) {
|
||||
resume_net_ifaces(&mld->link_dev);
|
||||
mif_err_limited("%s:%s TXQ restart, tx_flowctrl_mask=0x%04lx\n",
|
||||
ld->name, dev->name, ld->tx_flowctrl_mask);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->txq.lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tx_flowctrl_resume(struct mem_link_device *mld)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
|
||||
if (test_bit(TX_SUSPEND_MASK, &ld->tx_flowctrl_mask)) {
|
||||
unsigned long flags;
|
||||
struct mem_ipc_device *dev = mld->dev[IPC_RAW];
|
||||
|
||||
spin_lock_irqsave(&dev->txq.lock, flags);
|
||||
|
||||
clear_bit(TX_SUSPEND_MASK, &ld->tx_flowctrl_mask);
|
||||
|
||||
if (ld->tx_flowctrl_mask == 0) {
|
||||
resume_net_ifaces(&mld->link_dev);
|
||||
mif_err_limited("%s:%s TX resumed, tx_flowctrl_mask=0x%04lx\n",
|
||||
ld->name, dev->name, ld->tx_flowctrl_mask);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->txq.lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
int under_tx_flow_ctrl(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
return atomic_read(&dev->txq.busy);
|
||||
}
|
||||
|
||||
int check_tx_flow_ctrl(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
struct link_device *ld = &mld->link_dev;
|
||||
struct modem_ctl *mc = ld->mc;
|
||||
int busy_count = atomic_read(&dev->txq.busy);
|
||||
|
||||
if (txq_empty(dev)) {
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
if (cp_online(mc)) {
|
||||
mif_err("%s->%s: %s_TXQ: No RES_ACK, but EMPTY (busy_cnt %d)\n",
|
||||
ld->name, mc->name, dev->name, busy_count);
|
||||
}
|
||||
#endif
|
||||
txq_start(mld, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
atomic_inc(&dev->txq.busy);
|
||||
|
||||
if (cp_online(mc) && count_flood(busy_count, BUSY_COUNT_MASK)) {
|
||||
send_req_ack(mld, dev);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
void send_req_ack(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
struct mst_buff *msb;
|
||||
#endif
|
||||
|
||||
send_ipc_irq(mld, mask2int(req_ack_mask(dev)));
|
||||
dev->req_ack_cnt[TX] += 1;
|
||||
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
msb = mem_take_snapshot(mld, TX);
|
||||
if (!msb)
|
||||
return;
|
||||
print_req_ack(mld, &msb->snapshot, dev, TX);
|
||||
#if 0
|
||||
msb_queue_tail(&mld->msb_log, msb);
|
||||
#else
|
||||
msb_free(msb);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void recv_res_ack(struct mem_link_device *mld, struct mem_ipc_device *dev,
|
||||
struct mem_snapshot *mst)
|
||||
{
|
||||
dev->req_ack_cnt[TX] -= 1;
|
||||
|
||||
txq_start(mld, dev);
|
||||
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
print_res_ack(mld, mst, dev, RX);
|
||||
#endif
|
||||
}
|
||||
|
||||
void recv_req_ack(struct mem_link_device *mld, struct mem_ipc_device *dev,
|
||||
struct mem_snapshot *mst)
|
||||
{
|
||||
dev->req_ack_cnt[RX] += 1;
|
||||
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
print_req_ack(mld, mst, dev, RX);
|
||||
#endif
|
||||
}
|
||||
|
||||
void send_res_ack(struct mem_link_device *mld, struct mem_ipc_device *dev)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
struct mst_buff *msb;
|
||||
#endif
|
||||
|
||||
send_ipc_irq(mld, mask2int(res_ack_mask(dev)));
|
||||
dev->req_ack_cnt[RX] -= 1;
|
||||
|
||||
#ifdef DEBUG_MODEM_IF_FLOW_CTRL
|
||||
msb = mem_take_snapshot(mld, TX);
|
||||
if (!msb)
|
||||
return;
|
||||
print_res_ack(mld, &msb->snapshot, dev, TX);
|
||||
#if 0
|
||||
msb_queue_tail(&mld->msb_log, msb);
|
||||
#else
|
||||
msb_free(msb);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
2030
drivers/misc/modem_v1/link_device_memory_main.c
Normal file
2030
drivers/misc/modem_v1/link_device_memory_main.c
Normal file
File diff suppressed because it is too large
Load diff
657
drivers/misc/modem_v1/link_device_memory_sbd.c
Normal file
657
drivers/misc/modem_v1/link_device_memory_sbd.c
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
/**
|
||||
@file link_device_memory_main.c
|
||||
@brief common functions for all types of memory interface media
|
||||
@date 2014/02/05
|
||||
@author Hankook Jang (hankook.jang@samsung.com)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
#include "include/sbd.h"
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SBD
|
||||
/**
|
||||
@weakgroup group_mem_link_sbd
|
||||
@{
|
||||
*/
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SETUP
|
||||
/**
|
||||
@weakgroup group_mem_link_setup
|
||||
@{
|
||||
*/
|
||||
|
||||
static void print_sbd_config(struct sbd_link_device *sl)
|
||||
{
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
int i;
|
||||
|
||||
pr_err("mif: SBD_IPC {shmem_base:0x%lX shmem_size:%d}\n",
|
||||
(unsigned long)sl->shmem, sl->shmem_size);
|
||||
|
||||
pr_err("mif: SBD_IPC {version:%d num_channels:%d rbps_offset:%d}\n",
|
||||
sl->g_desc->version, sl->g_desc->num_channels,
|
||||
sl->g_desc->rbps_offset);
|
||||
|
||||
for (i = 0; i < sl->num_channels; i++) {
|
||||
struct sbd_rb_channel *rb_ch = &sl->g_desc->rb_ch[i];
|
||||
struct sbd_rb_desc *rbd;
|
||||
|
||||
rbd = &sl->g_desc->rb_desc[i][UL];
|
||||
pr_err("mif: RB_DESC[%-2d][UL](offset:%d) = "
|
||||
"{id:%-2d ch:%-3d dir:%s} "
|
||||
"{sbdv_offset:%-5d rb_len:%-3d} "
|
||||
"{buff_size:%-4d payload_offset:%d}\n",
|
||||
i, rb_ch->ul_rbd_offset, rbd->id, rbd->ch,
|
||||
udl_str(rbd->direction), rb_ch->ul_sbdv_offset,
|
||||
rbd->length, rbd->buff_size, rbd->payload_offset);
|
||||
|
||||
rbd = &sl->g_desc->rb_desc[i][DL];
|
||||
pr_err("mif: RB_DESC[%-2d][DL](offset:%d) = "
|
||||
"{id:%-2d ch:%-3d dir:%s} "
|
||||
"{sbdv_offset:%-5d rb_len:%-3d} "
|
||||
"{buff_size:%d payload_offset:%d}\n",
|
||||
i, rb_ch->dl_rbd_offset, rbd->id, rbd->ch,
|
||||
udl_str(rbd->direction), rb_ch->dl_sbdv_offset,
|
||||
rbd->length, rbd->buff_size, rbd->payload_offset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_desc_alloc(struct sbd_link_device *sl, unsigned int offset)
|
||||
{
|
||||
sl->desc_alloc_offset = offset;
|
||||
}
|
||||
|
||||
static void *desc_alloc(struct sbd_link_device *sl, size_t size)
|
||||
{
|
||||
u8 *desc = (sl->shmem + sl->desc_alloc_offset);
|
||||
sl->desc_alloc_offset += size;
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void init_buff_alloc(struct sbd_link_device *sl, unsigned int offset)
|
||||
{
|
||||
sl->buff_alloc_offset = offset;
|
||||
}
|
||||
|
||||
static u8 *buff_alloc(struct sbd_link_device *sl, unsigned int size)
|
||||
{
|
||||
u8 *buff = (sl->shmem + sl->buff_alloc_offset);
|
||||
sl->buff_alloc_offset += size;
|
||||
return buff;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief set up an SBD RB descriptor in SHMEM
|
||||
*/
|
||||
static void setup_sbd_rb_desc(struct sbd_rb_desc *rb_desc,
|
||||
struct sbd_ring_buffer *rb)
|
||||
{
|
||||
rb_desc->ch = rb->ch;
|
||||
|
||||
rb_desc->direction = rb->dir;
|
||||
rb_desc->signaling = 1;
|
||||
|
||||
rb_desc->sig_mask = MASK_INT_VALID | MASK_SEND_DATA;
|
||||
|
||||
rb_desc->length = rb->len;
|
||||
rb_desc->id = rb->id;
|
||||
|
||||
rb_desc->buff_size = rb->buff_size;
|
||||
rb_desc->payload_offset = rb->payload_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief set up an SBD RB
|
||||
|
||||
(1) build an SBD RB instance in the kernel space\n
|
||||
(2) allocate an SBD array in SHMEM\n
|
||||
(3) allocate a data buffer array in SHMEM if possible\n
|
||||
*/
|
||||
static int setup_sbd_rb(struct sbd_link_device *sl, struct sbd_ring_buffer *rb,
|
||||
enum direction dir, struct sbd_link_attr *link_attr)
|
||||
{
|
||||
size_t alloc_size;
|
||||
unsigned int i;
|
||||
|
||||
rb->sl = sl;
|
||||
|
||||
rb->lnk_hdr = link_attr->lnk_hdr;
|
||||
|
||||
rb->more = false;
|
||||
rb->total = 0;
|
||||
rb->rcvd = 0;
|
||||
|
||||
/*
|
||||
Initialize an SBD RB instance in the kernel space.
|
||||
*/
|
||||
rb->id = link_attr->id;
|
||||
rb->ch = link_attr->ch ?: SIPC_CH_ID_PDP_0;
|
||||
rb->dir = dir;
|
||||
rb->len = link_attr->rb_len[dir];
|
||||
rb->buff_size = link_attr->buff_size[dir];
|
||||
rb->payload_offset = 0;
|
||||
|
||||
/*
|
||||
Prepare array of pointers to the data buffer for each SBD
|
||||
*/
|
||||
alloc_size = (rb->len * sizeof(u8 *));
|
||||
rb->buff = kmalloc(alloc_size, GFP_ATOMIC);
|
||||
if (!rb->buff)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
(1) Allocate an array of data buffers in SHMEM.
|
||||
(2) Register the address of each data buffer.
|
||||
*/
|
||||
alloc_size = (rb->len * rb->buff_size);
|
||||
rb->buff_rgn = (u8 *)buff_alloc(sl, alloc_size);
|
||||
if (!rb->buff_rgn)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < rb->len; i++)
|
||||
rb->buff[i] = rb->buff_rgn + (i * rb->buff_size);
|
||||
|
||||
#if 0
|
||||
mif_err("RB[%d:%d][%s] buff_rgn {addr:0x%p offset:%d size:%lu}\n",
|
||||
rb->id, rb->ch, udl_str(dir), rb->buff_rgn,
|
||||
calc_offset(rb->buff_rgn, sl->shmem), alloc_size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Prepare SBD array in SHMEM.
|
||||
*/
|
||||
rb->rp = &sl->rp[rb->dir][rb->id];
|
||||
rb->wp = &sl->wp[rb->dir][rb->id];
|
||||
|
||||
alloc_size = (rb->len * sizeof(u32));
|
||||
|
||||
rb->addr_v = (u32 *)desc_alloc(sl, alloc_size);
|
||||
if (!rb->addr_v)
|
||||
return -ENOMEM;
|
||||
|
||||
rb->size_v = (u32 *)desc_alloc(sl, alloc_size);
|
||||
if (!rb->size_v)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
Register each data buffer to the corresponding SBD.
|
||||
*/
|
||||
for (i = 0; i < rb->len; i++) {
|
||||
rb->addr_v[i] = calc_offset(rb->buff[i], sl->shmem);
|
||||
rb->size_v[i] = 0;
|
||||
}
|
||||
|
||||
rb->iod = link_get_iod_with_channel(sl->ld, rb->ch);
|
||||
rb->ld = sl->ld;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_desc_rgn(struct sbd_link_device *sl)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
#if 1
|
||||
mif_err("SHMEM {base:0x%p size:%d}\n",
|
||||
sl->shmem, sl->shmem_size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Allocate @g_desc.
|
||||
*/
|
||||
size = sizeof(struct sbd_global_desc);
|
||||
sl->g_desc = (struct sbd_global_desc *)desc_alloc(sl, size);
|
||||
|
||||
#if 1
|
||||
mif_err("G_DESC_OFFSET = %d(0x%p)\n",
|
||||
calc_offset(sl->g_desc, sl->shmem),
|
||||
sl->g_desc);
|
||||
|
||||
mif_err("RB_CH_OFFSET = %d (0x%p)\n",
|
||||
calc_offset(sl->g_desc->rb_ch, sl->shmem),
|
||||
sl->g_desc->rb_ch);
|
||||
|
||||
mif_err("RBD_PAIR_OFFSET = %d (0x%p)\n",
|
||||
calc_offset(sl->g_desc->rb_desc, sl->shmem),
|
||||
sl->g_desc->rb_desc);
|
||||
#endif
|
||||
|
||||
size = sizeof(u16) * ULDL * RDWR * sl->num_channels;
|
||||
sl->rbps = (u16 *)desc_alloc(sl, size);
|
||||
#if 1
|
||||
mif_err("RBP_SET_OFFSET = %d (0x%p)\n",
|
||||
calc_offset(sl->rbps, sl->shmem), sl->rbps);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Set up @g_desc.
|
||||
*/
|
||||
sl->g_desc->version = sl->version;
|
||||
sl->g_desc->num_channels = sl->num_channels;
|
||||
sl->g_desc->rbps_offset = calc_offset(sl->rbps, sl->shmem);
|
||||
|
||||
/*
|
||||
Set up pointers to each RBP array.
|
||||
*/
|
||||
sl->rp[UL] = sl->rbps + sl->num_channels * 0;
|
||||
sl->wp[UL] = sl->rbps + sl->num_channels * 1;
|
||||
sl->rp[DL] = sl->rbps + sl->num_channels * 2;
|
||||
sl->wp[DL] = sl->rbps + sl->num_channels * 3;
|
||||
|
||||
#if 1
|
||||
mif_err("Complete!!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void setup_link_attr(struct sbd_link_attr *link_attr, u16 id, u16 ch,
|
||||
struct modem_io_t *io_dev)
|
||||
{
|
||||
link_attr->id = id;
|
||||
link_attr->ch = ch;
|
||||
|
||||
if (io_dev->attrs & IODEV_ATTR(ATTR_NO_LINK_HEADER))
|
||||
link_attr->lnk_hdr = false;
|
||||
else
|
||||
link_attr->lnk_hdr = true;
|
||||
|
||||
link_attr->rb_len[UL] = io_dev->ul_num_buffers;
|
||||
link_attr->buff_size[UL] = io_dev->ul_buffer_size;
|
||||
link_attr->rb_len[DL] = io_dev->dl_num_buffers;
|
||||
link_attr->buff_size[DL] = io_dev->dl_buffer_size;
|
||||
}
|
||||
|
||||
static int init_sbd_ipc(struct sbd_link_device *sl,
|
||||
struct sbd_ipc_device ipc_dev[],
|
||||
struct sbd_link_attr link_attr[])
|
||||
{
|
||||
int i;
|
||||
|
||||
setup_desc_rgn(sl);
|
||||
|
||||
for (i = 0; i < sl->num_channels; i++) {
|
||||
struct sbd_rb_channel *rb_ch = &sl->g_desc->rb_ch[i];
|
||||
struct sbd_rb_desc *rb_desc;
|
||||
struct sbd_ring_buffer *rb;
|
||||
int ret;
|
||||
|
||||
ipc_dev[i].id = link_attr[i].id;
|
||||
ipc_dev[i].ch = link_attr[i].ch;
|
||||
|
||||
/*
|
||||
Setup UL Ring Buffer in the ipc_dev[$i]
|
||||
*/
|
||||
rb = &ipc_dev[i].rb[UL];
|
||||
ret = setup_sbd_rb(sl, rb, UL, &link_attr[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
Setup UL RB_DESC & UL RB_CH in the g_desc
|
||||
*/
|
||||
rb_desc = &sl->g_desc->rb_desc[i][UL];
|
||||
setup_sbd_rb_desc(rb_desc, rb);
|
||||
rb_ch->ul_rbd_offset = calc_offset(rb_desc, sl->shmem);
|
||||
rb_ch->ul_sbdv_offset = calc_offset(rb->addr_v, sl->shmem);
|
||||
|
||||
/*
|
||||
Setup DL Ring Buffer in the ipc_dev[$i]
|
||||
*/
|
||||
rb = &ipc_dev[i].rb[DL];
|
||||
ret = setup_sbd_rb(sl, rb, DL, &link_attr[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
Setup DL RB_DESC & DL RB_CH in the g_desc
|
||||
*/
|
||||
rb_desc = &sl->g_desc->rb_desc[i][DL];
|
||||
setup_sbd_rb_desc(rb_desc, rb);
|
||||
rb_ch->dl_rbd_offset = calc_offset(rb_desc, sl->shmem);
|
||||
rb_ch->dl_sbdv_offset = calc_offset(rb->addr_v, sl->shmem);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_ipc_device(struct sbd_link_device *sl, u16 id,
|
||||
struct sbd_ipc_device *ipc_dev)
|
||||
{
|
||||
u16 ch = sbd_id2ch(sl, id);
|
||||
struct sbd_ring_buffer *rb;
|
||||
|
||||
ipc_dev->id = id;
|
||||
ipc_dev->ch = ch;
|
||||
|
||||
atomic_set(&ipc_dev->config_done, 0);
|
||||
|
||||
rb = &ipc_dev->rb[UL];
|
||||
spin_lock_init(&rb->lock);
|
||||
skb_queue_head_init(&rb->skb_q);
|
||||
|
||||
rb = &ipc_dev->rb[DL];
|
||||
spin_lock_init(&rb->lock);
|
||||
skb_queue_head_init(&rb->skb_q);
|
||||
}
|
||||
|
||||
/**
|
||||
@return the number of actual link channels
|
||||
*/
|
||||
static unsigned int init_ctrl_tables(struct sbd_link_device *sl, int num_iodevs,
|
||||
struct modem_io_t iodevs[])
|
||||
{
|
||||
int i;
|
||||
unsigned int id;
|
||||
unsigned int dummy_idx = -1;
|
||||
|
||||
/*
|
||||
Fill ch2id array with MAX_LINK_CHANNELS value to prevent sbd_ch2id()
|
||||
from returning 0 for unused channels.
|
||||
*/
|
||||
for (i = 0; i < MAX_SIPC_CHANNELS; i++)
|
||||
sl->ch2id[i] = MAX_LINK_CHANNELS;
|
||||
|
||||
for (id = 0, i = 0; i < num_iodevs; i++) {
|
||||
int ch = iodevs[i].id;
|
||||
|
||||
if (sipc5_ipc_ch(ch) && !sipc_ps_ch(ch)) {
|
||||
/* Save CH# to LinkID-to-CH conversion table. */
|
||||
sl->id2ch[id] = ch;
|
||||
|
||||
/* Save LinkID to CH-to-LinkID conversion table. */
|
||||
sl->ch2id[ch] = id;
|
||||
|
||||
/* Set up the attribute table entry of a LinkID. */
|
||||
setup_link_attr(&sl->link_attr[id], id, ch, &iodevs[i]);
|
||||
|
||||
++id;
|
||||
} else if (iodevs[i].format == IPC_MULTI_RAW) {
|
||||
dummy_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_iodevs; i++) {
|
||||
int ch = iodevs[i].id;
|
||||
|
||||
if (sipc_ps_ch(ch)) {
|
||||
sl->id2ch[id] = 0;
|
||||
sl->ch2id[ch] = id;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the attribute table entry of a LinkID. */
|
||||
setup_link_attr(&sl->link_attr[id],
|
||||
id, iodevs[dummy_idx].id, &iodevs[dummy_idx]);
|
||||
|
||||
id++;
|
||||
|
||||
/* Finally, id has the number of actual link channels. */
|
||||
return id;
|
||||
}
|
||||
|
||||
int init_sbd_link(struct sbd_link_device *sl)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!sl)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(sl->shmem + DESC_RGN_OFFSET, 0, DESC_RGN_SIZE);
|
||||
|
||||
init_desc_alloc(sl, DESC_RGN_OFFSET);
|
||||
init_buff_alloc(sl, BUFF_RGN_OFFSET);
|
||||
|
||||
err = init_sbd_ipc(sl, sl->ipc_dev, sl->link_attr);
|
||||
if (!err)
|
||||
print_sbd_config(sl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int create_sbd_link_device(struct link_device *ld, struct sbd_link_device *sl,
|
||||
u8 *shmem_base, unsigned int shmem_size)
|
||||
{
|
||||
int i;
|
||||
int num_iodevs;
|
||||
struct modem_io_t *iodevs;
|
||||
|
||||
if (!ld || !sl || !shmem_base)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ld->mdm_data)
|
||||
return -EINVAL;
|
||||
|
||||
num_iodevs = ld->mdm_data->num_iodevs;
|
||||
iodevs = ld->mdm_data->iodevs;
|
||||
|
||||
sl->ld = ld;
|
||||
|
||||
sl->version = 1;
|
||||
|
||||
sl->shmem = shmem_base;
|
||||
sl->shmem_size = shmem_size;
|
||||
|
||||
sl->num_channels = init_ctrl_tables(sl, num_iodevs, iodevs);
|
||||
|
||||
for (i = 0; i < sl->num_channels; i++)
|
||||
init_ipc_device(sl, i, sbd_id2dev(sl, i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_IPC_TX
|
||||
/**
|
||||
@weakgroup group_mem_ipc_tx
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief check the free space in a SBD RB
|
||||
|
||||
@param rb the pointer to an SBD RB instance
|
||||
|
||||
@retval "> 0" the size of free space in the @b @@dev TXQ
|
||||
@retval "< 0" an error code
|
||||
*/
|
||||
static inline int check_rb_space(struct sbd_ring_buffer *rb, unsigned int qlen,
|
||||
unsigned int in, unsigned int out)
|
||||
{
|
||||
unsigned int space;
|
||||
|
||||
if (!circ_valid(qlen, in, out)) {
|
||||
mif_err("ERR! TXQ[%d:%d] DIRTY (qlen:%d in:%d out:%d)\n",
|
||||
rb->id, rb->ch, qlen, in, out);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
space = circ_get_space(qlen, in, out);
|
||||
if (unlikely(space < 1)) {
|
||||
mif_err_limited("TXQ[%d:%d] NOSPC (qlen:%d in:%d out:%d)\n",
|
||||
rb->id, rb->ch, qlen, in, out);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
int sbd_pio_tx(struct sbd_ring_buffer *rb, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
unsigned int qlen = rb->len;
|
||||
unsigned int in = *rb->wp;
|
||||
unsigned int out = *rb->rp;
|
||||
unsigned int count = skb->len;
|
||||
unsigned int space = (rb->buff_size - rb->payload_offset);
|
||||
u8 *dst;
|
||||
|
||||
ret = check_rb_space(rb, qlen, in, out);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
if (unlikely(count > space)) {
|
||||
mif_err("ERR! {id:%d ch:%d} count %d > space %d\n",
|
||||
rb->id, rb->ch, count, space);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
dst = rb->buff[in] + rb->payload_offset;
|
||||
|
||||
barrier();
|
||||
|
||||
skb_copy_from_linear_data(skb, dst, count);
|
||||
|
||||
if (sipc_ps_ch(rb->ch)) {
|
||||
struct io_device *iod = skbpriv(skb)->iod;
|
||||
unsigned int ch = iod->id;
|
||||
|
||||
rb->size_v[in] = (skb->len & 0xFFFF);
|
||||
rb->size_v[in] |= (ch << 16);
|
||||
} else {
|
||||
rb->size_v[in] = skb->len;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
*rb->wp = circ_new_ptr(qlen, in, 1);
|
||||
|
||||
/* Commit the item before incrementing the head */
|
||||
smp_mb();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifdef GROUP_MEM_IPC_RX
|
||||
/**
|
||||
@weakgroup group_mem_ipc_rx
|
||||
@{
|
||||
*/
|
||||
|
||||
static inline struct sk_buff *recv_data(struct sbd_ring_buffer *rb, u16 out)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 *src;
|
||||
unsigned int len = rb->size_v[out] & 0xFFFF;
|
||||
unsigned int space = (rb->buff_size - rb->payload_offset);
|
||||
|
||||
if (unlikely(len > space)) {
|
||||
mif_err("ERR! {id:%d ch:%d} size %d > space %d\n",
|
||||
rb->id, rb->ch, len, space);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(len);
|
||||
if (unlikely(!skb)) {
|
||||
mif_err("ERR! {id:%d ch:%d} alloc_skb(%d) fail\n",
|
||||
rb->id, rb->ch, len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
src = rb->buff[out] + rb->payload_offset;
|
||||
skb_put(skb, len);
|
||||
skb_copy_to_linear_data(skb, src, len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline void set_lnk_hdr(struct sbd_ring_buffer *rb, struct sk_buff *skb)
|
||||
{
|
||||
skbpriv(skb)->lnk_hdr = rb->lnk_hdr && !rb->more;
|
||||
}
|
||||
|
||||
static inline void set_skb_priv(struct sbd_ring_buffer *rb, struct sk_buff *skb)
|
||||
{
|
||||
unsigned int out = *rb->rp;
|
||||
|
||||
/* Record the IO device, the link device, etc. into &skb->cb */
|
||||
if (sipc_ps_ch(rb->ch)) {
|
||||
unsigned ch = (rb->size_v[out] >> 16) & 0xffff;
|
||||
skbpriv(skb)->iod = link_get_iod_with_channel(rb->ld, ch);
|
||||
skbpriv(skb)->ld = rb->ld;
|
||||
skbpriv(skb)->sipc_ch = ch;
|
||||
} else {
|
||||
skbpriv(skb)->iod = rb->iod;
|
||||
skbpriv(skb)->ld = rb->ld;
|
||||
skbpriv(skb)->sipc_ch = rb->ch;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_more(struct sbd_ring_buffer *rb, struct sk_buff *skb)
|
||||
{
|
||||
if (rb->lnk_hdr) {
|
||||
if (!rb->more) {
|
||||
if (sipc5_get_frame_len(skb->data) > rb->buff_size) {
|
||||
rb->more = true;
|
||||
rb->total = sipc5_get_frame_len(skb->data);
|
||||
rb->rcvd = skb->len;
|
||||
}
|
||||
} else {
|
||||
rb->rcvd += skb->len;
|
||||
if (rb->rcvd >= rb->total) {
|
||||
rb->more = false;
|
||||
rb->total = 0;
|
||||
rb->rcvd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct sk_buff *sbd_pio_rx(struct sbd_ring_buffer *rb)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned int qlen = rb->len;
|
||||
unsigned int out = *rb->rp;
|
||||
|
||||
skb = recv_data(rb, out);
|
||||
if (unlikely(!skb))
|
||||
return NULL;
|
||||
|
||||
set_lnk_hdr(rb, skb);
|
||||
|
||||
set_skb_priv(rb, skb);
|
||||
|
||||
check_more(rb, skb);
|
||||
|
||||
*rb->rp = circ_new_ptr(qlen, out, 1);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
/**
|
||||
// End of group_mem_link_sbd
|
||||
@}
|
||||
*/
|
||||
#endif
|
||||
|
||||
243
drivers/misc/modem_v1/link_device_memory_snapshot.c
Normal file
243
drivers/misc/modem_v1/link_device_memory_snapshot.c
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
#include "link_device_memory.h"
|
||||
|
||||
#ifdef GROUP_MEM_LINK_SNAPSHOT
|
||||
|
||||
static struct kmem_cache *msb_kmem_cache;
|
||||
|
||||
int msb_init(void)
|
||||
{
|
||||
msb_kmem_cache = kmem_cache_create("msb_kmem_cache",
|
||||
sizeof(struct mst_buff),
|
||||
0,
|
||||
(SLAB_HWCACHE_ALIGN | SLAB_PANIC),
|
||||
NULL);
|
||||
if (!msb_kmem_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mst_buff *msb_alloc(void)
|
||||
{
|
||||
return kmem_cache_zalloc(msb_kmem_cache, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
void msb_free(struct mst_buff *msb)
|
||||
{
|
||||
kmem_cache_free(msb_kmem_cache, msb);
|
||||
}
|
||||
|
||||
static inline void __msb_queue_head_init(struct mst_buff_head *list)
|
||||
{
|
||||
list->prev = list->next = (struct mst_buff *)list;
|
||||
list->qlen = 0;
|
||||
}
|
||||
|
||||
void msb_queue_head_init(struct mst_buff_head *list)
|
||||
{
|
||||
spin_lock_init(&list->lock);
|
||||
__msb_queue_head_init(list);
|
||||
}
|
||||
|
||||
static inline void __msb_insert(struct mst_buff *msb,
|
||||
struct mst_buff *prev, struct mst_buff *next,
|
||||
struct mst_buff_head *list)
|
||||
{
|
||||
msb->next = next;
|
||||
msb->prev = prev;
|
||||
next->prev = prev->next = msb;
|
||||
list->qlen++;
|
||||
}
|
||||
|
||||
static inline void __msb_queue_before(struct mst_buff_head *list,
|
||||
struct mst_buff *next,
|
||||
struct mst_buff *msb)
|
||||
{
|
||||
__msb_insert(msb, next->prev, next, list);
|
||||
}
|
||||
|
||||
static inline void __msb_queue_after(struct mst_buff_head *list,
|
||||
struct mst_buff *prev,
|
||||
struct mst_buff *msb)
|
||||
{
|
||||
__msb_insert(msb, prev, prev->next, list);
|
||||
}
|
||||
|
||||
static inline void __msb_queue_tail(struct mst_buff_head *list,
|
||||
struct mst_buff *msb)
|
||||
{
|
||||
__msb_queue_before(list, (struct mst_buff *)list, msb);
|
||||
}
|
||||
|
||||
static inline void __msb_queue_head(struct mst_buff_head *list,
|
||||
struct mst_buff *msb)
|
||||
{
|
||||
__msb_queue_after(list, (struct mst_buff *)list, msb);
|
||||
}
|
||||
|
||||
void msb_queue_tail(struct mst_buff_head *list, struct mst_buff *msb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
__msb_queue_tail(list, msb);
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
|
||||
void msb_queue_head(struct mst_buff_head *list, struct mst_buff *msb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
__msb_queue_head(list, msb);
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
|
||||
static inline struct mst_buff *__msb_peek(const struct mst_buff_head *list_)
|
||||
{
|
||||
struct mst_buff *list = ((const struct mst_buff *)list_)->next;
|
||||
if (list == (struct mst_buff *)list_)
|
||||
list = NULL;
|
||||
return list;
|
||||
}
|
||||
|
||||
static inline void __msb_unlink(struct mst_buff *msb,
|
||||
struct mst_buff_head *list)
|
||||
{
|
||||
struct mst_buff *next, *prev;
|
||||
|
||||
list->qlen--;
|
||||
|
||||
next = msb->next;
|
||||
prev = msb->prev;
|
||||
|
||||
msb->next = msb->prev = NULL;
|
||||
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline struct mst_buff *__msb_dequeue(struct mst_buff_head *list)
|
||||
{
|
||||
struct mst_buff *msb = __msb_peek(list);
|
||||
|
||||
if (msb)
|
||||
__msb_unlink(msb, list);
|
||||
|
||||
return msb;
|
||||
}
|
||||
|
||||
struct mst_buff *msb_dequeue(struct mst_buff_head *list)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mst_buff *result;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
result = __msb_dequeue(list);
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void msb_queue_purge(struct mst_buff_head *list)
|
||||
{
|
||||
struct mst_buff *msb;
|
||||
while ((msb = msb_dequeue(list)) != NULL)
|
||||
msb_free(msb);
|
||||
}
|
||||
|
||||
static void __take_sbd_status(struct mem_link_device *mld, enum direction dir,
|
||||
struct mem_snapshot *mst)
|
||||
{
|
||||
getnstimeofday(&mst->ts);
|
||||
|
||||
mst->dir = dir;
|
||||
|
||||
mst->magic = get_magic(mld);
|
||||
mst->access = get_access(mld);
|
||||
|
||||
if (mld->recv_cp2ap_irq)
|
||||
mst->int2ap = mld->recv_cp2ap_irq(mld);
|
||||
else
|
||||
mst->int2ap = 0;
|
||||
|
||||
if (mld->read_ap2cp_irq)
|
||||
mst->int2cp = mld->read_ap2cp_irq(mld);
|
||||
else
|
||||
mst->int2cp = 0;
|
||||
}
|
||||
|
||||
static void __take_mem_status(struct mem_link_device *mld, enum direction dir,
|
||||
struct mem_snapshot *mst)
|
||||
{
|
||||
int i;
|
||||
|
||||
getnstimeofday(&mst->ts);
|
||||
|
||||
mst->dir = dir;
|
||||
|
||||
mst->magic = get_magic(mld);
|
||||
mst->access = get_access(mld);
|
||||
|
||||
for (i = 0; i < MAX_SIPC5_DEVICES; i++) {
|
||||
struct mem_ipc_device *dev = mld->dev[i];
|
||||
mst->head[i][TX] = get_txq_head(dev);
|
||||
mst->tail[i][TX] = get_txq_tail(dev);
|
||||
mst->head[i][RX] = get_rxq_head(dev);
|
||||
mst->tail[i][RX] = get_rxq_tail(dev);
|
||||
}
|
||||
|
||||
if (mld->recv_cp2ap_irq)
|
||||
mst->int2ap = mld->recv_cp2ap_irq(mld);
|
||||
else
|
||||
mst->int2ap = 0;
|
||||
|
||||
if (mld->read_ap2cp_irq)
|
||||
mst->int2cp = mld->read_ap2cp_irq(mld);
|
||||
else
|
||||
mst->int2cp = 0;
|
||||
}
|
||||
|
||||
struct mst_buff *mem_take_snapshot(struct mem_link_device *mld,
|
||||
enum direction dir)
|
||||
{
|
||||
struct mst_buff *msb = msb_alloc();
|
||||
|
||||
if (!msb)
|
||||
return NULL;
|
||||
|
||||
if (sbd_active(&mld->sbd_link_dev))
|
||||
__take_sbd_status(mld, dir, &msb->snapshot);
|
||||
else
|
||||
__take_mem_status(mld, dir, &msb->snapshot);
|
||||
|
||||
return msb;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
3131
drivers/misc/modem_v1/link_device_shmem.c
Normal file
3131
drivers/misc/modem_v1/link_device_shmem.c
Normal file
File diff suppressed because it is too large
Load diff
652
drivers/misc/modem_v1/modem_ctrl_ss310ap.c
Normal file
652
drivers/misc/modem_v1/modem_ctrl_ss310ap.c
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* 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/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mcu_ipc.h>
|
||||
#include <soc/samsung/exynos-pmu.h>
|
||||
#include <soc/samsung/pmu-cp.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
|
||||
#include <linux/modem_notifier.h>
|
||||
|
||||
#ifdef CONFIG_EXYNOS_BUSMONITOR
|
||||
#include <linux/exynos-busmon.h>
|
||||
#endif
|
||||
|
||||
#define MIF_INIT_TIMEOUT (15 * HZ)
|
||||
|
||||
#ifdef CONFIG_REGULATOR_S2MPU01A
|
||||
#include <linux/mfd/samsung/s2mpu01a.h>
|
||||
static inline int change_cp_pmu_manual_reset(void)
|
||||
{
|
||||
return change_mr_reset();
|
||||
}
|
||||
#else
|
||||
static inline int change_cp_pmu_manual_reset(void) {return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UART_SEL
|
||||
extern void cp_recheck_uart_dir(void);
|
||||
#endif
|
||||
|
||||
static struct modem_ctl *g_mc;
|
||||
|
||||
static irqreturn_t cp_wdt_handler(int irq, void *arg)
|
||||
{
|
||||
struct modem_ctl *mc = (struct modem_ctl *)arg;
|
||||
struct io_device *iod;
|
||||
enum modem_state new_state;
|
||||
|
||||
mif_disable_irq(&mc->irq_cp_wdt);
|
||||
mif_err("%s: ERR! CP_WDOG occurred\n", mc->name);
|
||||
|
||||
if (mc->phone_state == STATE_ONLINE)
|
||||
modem_notify_event(MODEM_EVENT_WATCHDOG);
|
||||
|
||||
/* Disable debug Snapshot */
|
||||
mif_set_snapshot(false);
|
||||
|
||||
exynos_clear_cp_reset();
|
||||
new_state = STATE_CRASH_WATCHDOG;
|
||||
|
||||
mif_err("new_state = %s\n", cp_state_str(new_state));
|
||||
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, new_state);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t cp_fail_handler(int irq, void *arg)
|
||||
{
|
||||
struct modem_ctl *mc = (struct modem_ctl *)arg;
|
||||
struct io_device *iod;
|
||||
enum modem_state new_state;
|
||||
|
||||
mif_disable_irq(&mc->irq_cp_fail);
|
||||
mif_err("%s: ERR! CP_FAIL occurred\n", mc->name);
|
||||
|
||||
exynos_cp_active_clear();
|
||||
new_state = STATE_CRASH_RESET;
|
||||
|
||||
mif_err("new_state = %s\n", cp_state_str(new_state));
|
||||
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, new_state);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void cp_active_handler(void *arg)
|
||||
{
|
||||
struct modem_ctl *mc = (struct modem_ctl *)arg;
|
||||
struct io_device *iod;
|
||||
int cp_on = exynos_get_cp_power_status();
|
||||
int cp_active = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
|
||||
mc->sbi_lte_active_mask, mc->sbi_lte_active_pos);
|
||||
enum modem_state old_state = mc->phone_state;
|
||||
enum modem_state new_state = mc->phone_state;
|
||||
|
||||
mif_info("old_state:%s cp_on:%d cp_active:%d\n",
|
||||
cp_state_str(old_state), cp_on, cp_active);
|
||||
|
||||
if (!cp_active) {
|
||||
if (cp_on > 0) {
|
||||
new_state = STATE_OFFLINE;
|
||||
complete_all(&mc->off_cmpl);
|
||||
} else {
|
||||
mif_err("don't care!!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (old_state != new_state) {
|
||||
mif_info("new_state = %s\n", cp_state_str(new_state));
|
||||
|
||||
if (old_state == STATE_ONLINE)
|
||||
modem_notify_event(MODEM_EVENT_EXIT);
|
||||
|
||||
/* Disable debug Snapshot */
|
||||
mif_set_snapshot(false);
|
||||
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, new_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_system_rev(struct device_node *np)
|
||||
{
|
||||
int value, cnt, gpio_cnt;
|
||||
unsigned gpio_hw_rev, hw_rev = 0;
|
||||
|
||||
gpio_cnt = of_gpio_count(np);
|
||||
if (gpio_cnt < 0) {
|
||||
mif_err("failed to get gpio_count from DT(%d)\n", gpio_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < gpio_cnt; cnt++) {
|
||||
gpio_hw_rev = of_get_gpio(np, cnt);
|
||||
if (!gpio_is_valid(gpio_hw_rev)) {
|
||||
mif_err("gpio_hw_rev%d: Invalied gpio\n", cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
value = gpio_get_value(gpio_hw_rev);
|
||||
hw_rev |= (value & 0x1) << cnt;
|
||||
}
|
||||
|
||||
return hw_rev;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_DS_DETECT
|
||||
static int get_ds_detect(struct device_node *np)
|
||||
{
|
||||
unsigned gpio_ds_det;
|
||||
|
||||
gpio_ds_det = of_get_named_gpio(np, "mif,gpio_ds_det", 0);
|
||||
if (!gpio_is_valid(gpio_ds_det)) {
|
||||
mif_err("gpio_ds_det: Invalid gpio\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gpio_get_value(gpio_ds_det);
|
||||
}
|
||||
#else
|
||||
static int ds_detect = 1;
|
||||
module_param(ds_detect, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
MODULE_PARM_DESC(ds_detect, "Dual SIM detect");
|
||||
|
||||
static int get_ds_detect(struct device_node *np)
|
||||
{
|
||||
mif_info("Dual SIM detect = %d\n", ds_detect);
|
||||
return ds_detect - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int init_mailbox_regs(struct modem_ctl *mc)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mc->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
unsigned int mbx_ap_status;
|
||||
unsigned int sbi_ds_det_mask, sbi_ds_det_pos;
|
||||
unsigned int sbi_sys_rev_mask, sbi_sys_rev_pos;
|
||||
int sys_rev, ds_det, i;
|
||||
|
||||
for (i = 0; i < MAX_MBOX_NUM; i++)
|
||||
mbox_set_value(MCU_CP, i, 0);
|
||||
|
||||
if (np) {
|
||||
mif_dt_read_u32(np, "mbx_ap2cp_united_status", mbx_ap_status);
|
||||
mif_dt_read_u32(np, "sbi_sys_rev_mask", sbi_sys_rev_mask);
|
||||
mif_dt_read_u32(np, "sbi_sys_rev_pos", sbi_sys_rev_pos);
|
||||
mif_dt_read_u32(np, "sbi_ds_det_mask", sbi_ds_det_mask);
|
||||
mif_dt_read_u32(np, "sbi_ds_det_pos", sbi_ds_det_pos);
|
||||
|
||||
sys_rev = get_system_rev(np);
|
||||
ds_det = get_ds_detect(np);
|
||||
if (sys_rev < 0 || ds_det < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mbox_update_value(MCU_CP, mbx_ap_status, sys_rev,
|
||||
sbi_sys_rev_mask, sbi_sys_rev_pos);
|
||||
mbox_update_value(MCU_CP, mbx_ap_status, ds_det,
|
||||
sbi_ds_det_mask, sbi_ds_det_pos);
|
||||
|
||||
mif_info("sys_rev:%d, ds_det:%u (0x%x)\n",
|
||||
sys_rev, ds_det, mbox_get_value(MCU_CP, mbx_ap_status));
|
||||
} else {
|
||||
mif_info("non-DT project, can't set system_rev\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_on(struct modem_ctl *mc)
|
||||
{
|
||||
int ret;
|
||||
int cp_active = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
|
||||
mc->sbi_lte_active_mask, mc->sbi_lte_active_pos);
|
||||
int cp_status = mbox_extract_value(MCU_CP, mc->mbx_cp_status,
|
||||
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos);
|
||||
struct modem_data __maybe_unused *modem = mc->mdm_data;
|
||||
|
||||
mif_info("+++\n");
|
||||
mif_info("cp_active:%d cp_status:%d\n", cp_active, cp_status);
|
||||
|
||||
/* Enable debug Snapshot */
|
||||
mif_set_snapshot(true);
|
||||
|
||||
mc->phone_state = STATE_OFFLINE;
|
||||
|
||||
if (init_mailbox_regs(mc))
|
||||
mif_err("Failed to initialize mbox regs\n");
|
||||
#if !defined(CONFIG_SOC_EXYNOS7570)
|
||||
memmove(modem->ipc_base + 0x1000, mc->sysram_alive, 0x800);
|
||||
#endif
|
||||
|
||||
mbox_update_value(MCU_CP, mc->mbx_ap_status, 1,
|
||||
mc->sbi_pda_active_mask, mc->sbi_pda_active_pos);
|
||||
|
||||
if (exynos_get_cp_power_status() > 0) {
|
||||
mif_info("CP aleady Power on, Just start!\n");
|
||||
exynos_cp_release();
|
||||
} else {
|
||||
exynos_set_cp_power_onoff(CP_POWER_ON);
|
||||
}
|
||||
|
||||
msleep(300);
|
||||
ret = change_cp_pmu_manual_reset();
|
||||
mif_info("change_mr_reset -> %d\n", ret);
|
||||
|
||||
#ifdef CONFIG_UART_SEL
|
||||
mif_err("Recheck UART direction.\n");
|
||||
cp_recheck_uart_dir();
|
||||
#endif
|
||||
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_off(struct modem_ctl *mc)
|
||||
{
|
||||
mif_info("+++\n");
|
||||
|
||||
exynos_set_cp_power_onoff(CP_POWER_OFF);
|
||||
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_shutdown(struct modem_ctl *mc)
|
||||
{
|
||||
struct io_device *iod;
|
||||
unsigned long timeout = msecs_to_jiffies(3000);
|
||||
unsigned long remain;
|
||||
|
||||
mif_info("+++\n");
|
||||
|
||||
if (mc->phone_state == STATE_OFFLINE
|
||||
|| exynos_get_cp_power_status() <= 0)
|
||||
goto exit;
|
||||
|
||||
init_completion(&mc->off_cmpl);
|
||||
remain = wait_for_completion_timeout(&mc->off_cmpl, timeout);
|
||||
if (remain == 0) {
|
||||
mif_err("T-I-M-E-O-U-T\n");
|
||||
mc->phone_state = STATE_OFFLINE;
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, STATE_OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
exynos_set_cp_power_onoff(CP_POWER_OFF);
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_reset(struct modem_ctl *mc)
|
||||
{
|
||||
mif_info("+++\n");
|
||||
|
||||
//mc->phone_state = STATE_OFFLINE;
|
||||
if (mc->phone_state == STATE_OFFLINE)
|
||||
return 0;
|
||||
|
||||
if (*(unsigned int *)(mc->mdm_data->ipc_base + 0xF80)
|
||||
== 0xDEB)
|
||||
return 0;
|
||||
|
||||
if (mc->phone_state == STATE_ONLINE)
|
||||
modem_notify_event(MODEM_EVENT_RESET);
|
||||
|
||||
if (exynos_get_cp_power_status() > 0) {
|
||||
mif_info("CP aleady Power on, try reset\n");
|
||||
exynos_cp_reset();
|
||||
}
|
||||
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_boot_on(struct modem_ctl *mc)
|
||||
{
|
||||
struct link_device *ld = get_current_link(mc->iod);
|
||||
struct io_device *iod;
|
||||
int cnt = 100;
|
||||
|
||||
mif_info("+++\n");
|
||||
|
||||
if (ld->boot_on)
|
||||
ld->boot_on(ld, mc->bootd);
|
||||
|
||||
init_completion(&mc->init_cmpl);
|
||||
init_completion(&mc->off_cmpl);
|
||||
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, STATE_BOOTING);
|
||||
}
|
||||
|
||||
while (mbox_extract_value(MCU_CP, mc->mbx_cp_status,
|
||||
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos) == 0) {
|
||||
if (--cnt > 0)
|
||||
usleep_range(10000, 20000);
|
||||
else
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
mif_disable_irq(&mc->irq_cp_wdt);
|
||||
mif_enable_irq(&mc->irq_cp_fail);
|
||||
|
||||
mif_info("cp_status=%u\n",
|
||||
mbox_extract_value(MCU_CP, mc->mbx_cp_status,
|
||||
mc->sbi_cp_status_mask, mc->sbi_cp_status_pos));
|
||||
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_boot_off(struct modem_ctl *mc)
|
||||
{
|
||||
struct io_device *iod;
|
||||
unsigned long remain;
|
||||
int err = 0;
|
||||
mif_info("+++\n");
|
||||
|
||||
remain = wait_for_completion_timeout(&mc->init_cmpl, MIF_INIT_TIMEOUT);
|
||||
if (remain == 0) {
|
||||
mif_err("T-I-M-E-O-U-T\n");
|
||||
err = -EAGAIN;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mif_enable_irq(&mc->irq_cp_wdt);
|
||||
|
||||
list_for_each_entry(iod, &mc->modem_state_notify_list, list) {
|
||||
if (iod && atomic_read(&iod->opened) > 0)
|
||||
iod->modem_state_changed(iod, STATE_ONLINE);
|
||||
}
|
||||
|
||||
mif_info("---\n");
|
||||
|
||||
exit:
|
||||
mif_disable_irq(&mc->irq_cp_fail);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ss310ap_boot_done(struct modem_ctl *mc)
|
||||
{
|
||||
mif_info("+++\n");
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss310ap_force_crash_exit(struct modem_ctl *mc)
|
||||
{
|
||||
struct link_device *ld = get_current_link(mc->bootd);
|
||||
mif_err("+++\n");
|
||||
|
||||
/* Make DUMP start */
|
||||
ld->force_dump(ld, mc->bootd);
|
||||
|
||||
mif_err("---\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ss310ap_force_crash_exit_ext(void)
|
||||
{
|
||||
if (g_mc)
|
||||
ss310ap_force_crash_exit(g_mc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ss310ap_force_crash_exit_ext);
|
||||
|
||||
int ss310ap_get_evs_mode_ext(void)
|
||||
{
|
||||
int evs_mode = 0;
|
||||
|
||||
if (g_mc) {
|
||||
evs_mode = mbox_extract_value(MCU_CP, g_mc->mbx_cp_status,
|
||||
g_mc->sbi_evs_mode_mask, g_mc->sbi_evs_mode_pos);
|
||||
}
|
||||
return evs_mode;
|
||||
}
|
||||
EXPORT_SYMBOL(ss310ap_get_evs_mode_ext);
|
||||
|
||||
static int ss310ap_dump_start(struct modem_ctl *mc)
|
||||
{
|
||||
int err;
|
||||
struct link_device *ld = get_current_link(mc->bootd);
|
||||
mif_err("+++\n");
|
||||
|
||||
if (!ld->dump_start) {
|
||||
mif_err("ERR! %s->dump_start not exist\n", ld->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
err = ld->dump_start(ld, mc->bootd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
exynos_cp_release();
|
||||
|
||||
mbox_update_value(MCU_CP, mc->mbx_ap_status, 1,
|
||||
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos);
|
||||
|
||||
mif_err("---\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ss310ap_modem_boot_confirm(struct modem_ctl *mc)
|
||||
{
|
||||
mbox_update_value(MCU_CP,mc->mbx_ap_status, 1,
|
||||
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos);
|
||||
mif_info("ap_status=%u\n",
|
||||
mbox_extract_value(MCU_CP, mc->mbx_ap_status,
|
||||
mc->sbi_ap_status_mask, mc->sbi_ap_status_pos));
|
||||
}
|
||||
|
||||
static void ss310ap_get_ops(struct modem_ctl *mc)
|
||||
{
|
||||
mc->ops.modem_on = ss310ap_on;
|
||||
mc->ops.modem_off = ss310ap_off;
|
||||
mc->ops.modem_shutdown = ss310ap_shutdown;
|
||||
mc->ops.modem_reset = ss310ap_reset;
|
||||
mc->ops.modem_boot_on = ss310ap_boot_on;
|
||||
mc->ops.modem_boot_off = ss310ap_boot_off;
|
||||
mc->ops.modem_boot_done = ss310ap_boot_done;
|
||||
mc->ops.modem_force_crash_exit = ss310ap_force_crash_exit;
|
||||
mc->ops.modem_dump_start = ss310ap_dump_start;
|
||||
mc->ops.modem_boot_confirm = ss310ap_modem_boot_confirm;
|
||||
}
|
||||
|
||||
static void ss310ap_get_pdata(struct modem_ctl *mc, struct modem_data *modem)
|
||||
{
|
||||
struct modem_mbox *mbx = modem->mbx;
|
||||
|
||||
mc->int_pda_active = mbx->int_ap2cp_active;
|
||||
|
||||
mc->irq_phone_active = mbx->irq_cp2ap_active;
|
||||
|
||||
mc->mbx_ap_status = mbx->mbx_ap2cp_status;
|
||||
mc->mbx_cp_status = mbx->mbx_cp2ap_status;
|
||||
|
||||
mc->mbx_perf_req = mbx->mbx_cp2ap_perf_req;
|
||||
|
||||
mc->int_uart_noti = mbx->int_ap2cp_uart_noti;
|
||||
|
||||
mc->sbi_evs_mode_mask = mbx->sbi_evs_mode_mask;
|
||||
mc->sbi_evs_mode_pos = mbx->sbi_evs_mode_pos;
|
||||
mc->sbi_lte_active_mask = mbx->sbi_lte_active_mask;
|
||||
mc->sbi_lte_active_pos = mbx->sbi_lte_active_pos;
|
||||
mc->sbi_cp_status_mask = mbx->sbi_cp_status_mask;
|
||||
mc->sbi_cp_status_pos = mbx->sbi_cp_status_pos;
|
||||
|
||||
mc->sbi_pda_active_mask = mbx->sbi_pda_active_mask;
|
||||
mc->sbi_pda_active_pos = mbx->sbi_pda_active_pos;
|
||||
mc->sbi_ap_status_mask = mbx->sbi_ap_status_mask;
|
||||
mc->sbi_ap_status_pos = mbx->sbi_ap_status_pos;
|
||||
|
||||
mc->sbi_uart_noti_mask = mbx->sbi_uart_noti_mask;
|
||||
mc->sbi_uart_noti_pos = mbx->sbi_uart_noti_pos;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXYNOS_BUSMONITOR
|
||||
static int ss310ap_busmon_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct busmon_notifier *info = (struct busmon_notifier *)data;
|
||||
char *init_desc = info->init_desc;
|
||||
|
||||
if (init_desc != NULL &&
|
||||
(strncmp(init_desc, "CP", strlen(init_desc)) == 0 ||
|
||||
strncmp(init_desc, "APB_CORE_CP", strlen(init_desc)) == 0 ||
|
||||
strncmp(init_desc, "MIF_CP", strlen(init_desc)) == 0)) {
|
||||
struct modem_ctl *mc =
|
||||
container_of(nb, struct modem_ctl, busmon_nfb);
|
||||
|
||||
ss310ap_force_crash_exit(mc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ss310ap_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mc->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int ret = 0;
|
||||
unsigned int irq_num;
|
||||
struct resource __maybe_unused *sysram_alive;
|
||||
unsigned long flags = IRQF_NO_SUSPEND | IRQF_NO_THREAD;
|
||||
unsigned int cp_rst_n ;
|
||||
|
||||
mif_info("+++\n");
|
||||
|
||||
g_mc = mc;
|
||||
|
||||
ss310ap_get_ops(mc);
|
||||
ss310ap_get_pdata(mc, pdata);
|
||||
dev_set_drvdata(mc->dev, mc);
|
||||
|
||||
/*
|
||||
** Register CP_FAIL interrupt handler
|
||||
*/
|
||||
irq_num = platform_get_irq(pdev, 0);
|
||||
mif_init_irq(&mc->irq_cp_fail, irq_num, "cp_fail", flags);
|
||||
|
||||
ret = mif_request_irq(&mc->irq_cp_fail, cp_fail_handler, mc);
|
||||
if (ret) {
|
||||
mif_err("Failed to request_irq with(%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CP_FAIL interrupt must be enabled only during CP booting */
|
||||
mc->irq_cp_fail.active = true;
|
||||
mif_disable_irq(&mc->irq_cp_fail);
|
||||
|
||||
/*
|
||||
** Register CP_WDT interrupt handler
|
||||
*/
|
||||
irq_num = platform_get_irq(pdev, 1);
|
||||
mif_init_irq(&mc->irq_cp_wdt, irq_num, "cp_wdt", flags);
|
||||
|
||||
ret = mif_request_irq(&mc->irq_cp_wdt, cp_wdt_handler, mc);
|
||||
if (ret) {
|
||||
mif_err("Failed to request_irq with(%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* CP_WDT interrupt must be enabled only after CP booting */
|
||||
mc->irq_cp_wdt.active = true;
|
||||
mif_disable_irq(&mc->irq_cp_wdt);
|
||||
|
||||
#ifdef CONFIG_SOC_EXYNOS8890
|
||||
sysram_alive = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mc->sysram_alive = devm_ioremap_resource(&pdev->dev, sysram_alive);
|
||||
if (IS_ERR(mc->sysram_alive)) {
|
||||
ret = PTR_ERR(mc->sysram_alive);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (CONFIG_SOC_EXYNOS7870)
|
||||
mc->sysram_alive = shm_request_region(shm_get_sysram_base(),
|
||||
shm_get_sysram_size());
|
||||
if (!mc->sysram_alive)
|
||||
mif_err("Failed to memory allocation\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register LTE_ACTIVE MBOX interrupt handler
|
||||
*/
|
||||
ret = mbox_request_irq(MCU_CP, mc->irq_phone_active, cp_active_handler, mc);
|
||||
if (ret) {
|
||||
mif_err("Failed to mbox_request_irq %u with(%d)",
|
||||
mc->irq_phone_active, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&mc->off_cmpl);
|
||||
|
||||
/*
|
||||
** Get/set CP_RST_N
|
||||
*/
|
||||
if (np) {
|
||||
cp_rst_n = of_get_named_gpio(np, "modem_ctrl,gpio_cp_rst_n", 0);
|
||||
if (gpio_is_valid(cp_rst_n)) {
|
||||
mif_info("cp_rst_n: %d\n", cp_rst_n);
|
||||
ret = gpio_request(cp_rst_n, "CP_RST_N");
|
||||
if (ret) {
|
||||
mif_err("fail req gpio %s:%d\n", "CP_RST_N", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_direction_output(cp_rst_n, 1);
|
||||
} else {
|
||||
mif_err("cp_rst_n: Invalied gpio pins\n");
|
||||
}
|
||||
} else {
|
||||
mif_err("cannot find device_tree for pmu_cu!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXYNOS_BUSMONITOR
|
||||
/*
|
||||
** Register BUS Mon notifier
|
||||
*/
|
||||
mc->busmon_nfb.notifier_call = ss310ap_busmon_notifier;
|
||||
busmon_notifier_chain_register(&mc->busmon_nfb);
|
||||
#endif
|
||||
|
||||
mif_info("---\n");
|
||||
return 0;
|
||||
}
|
||||
51
drivers/misc/modem_v1/modem_debug.h
Normal file
51
drivers/misc/modem_v1/modem_debug.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef __MODEM_DEBUG_H__
|
||||
#define __MODEM_DEBUG_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define MAX_LOG_LEN (256 - sizeof(struct timespec) - sizeof(int))
|
||||
struct log_buff {
|
||||
struct timespec ts;
|
||||
int level;
|
||||
char data[MAX_LOG_LEN];
|
||||
};
|
||||
|
||||
#define MAX_TRACE_SIZE 4096
|
||||
struct log_buff_circ_queue {
|
||||
spinlock_t lock;
|
||||
|
||||
struct log_buff logb[MAX_TRACE_SIZE];
|
||||
u32 in;
|
||||
u32 out;
|
||||
|
||||
/*
|
||||
This flag is set when a log_buff_circ_queue instance becomes full for
|
||||
the first time, and must be cleared only after all logs are received by
|
||||
the bebugfs.
|
||||
*/
|
||||
bool full;
|
||||
};
|
||||
|
||||
enum modemctl_event {
|
||||
MDM_EVENT_CP_FORCE_RESET,
|
||||
MDM_EVENT_CP_FORCE_CRASH,
|
||||
MDM_EVENT_CP_ABNORMAL_RX,
|
||||
MDM_CRASH_PM_FAIL,
|
||||
MDM_CRASH_PM_CP_FAIL,
|
||||
MDM_CRASH_INVALID_RB,
|
||||
MDM_CRASH_INVALID_IOD,
|
||||
MDM_CRASH_INVALID_SKBCB,
|
||||
MDM_CRASH_INVALID_SKBIOD,
|
||||
MDM_CRASH_NO_MEM,
|
||||
MDM_CRASH_CMD_RESET = 90,
|
||||
MDM_CRASH_CMD_EXIT,
|
||||
};
|
||||
|
||||
int register_cp_crash_notifier(struct notifier_block *nb);
|
||||
void modemctl_notify_event(enum modemctl_event evt);
|
||||
|
||||
extern void evt_log(int level, const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
1478
drivers/misc/modem_v1/modem_io_device.c
Normal file
1478
drivers/misc/modem_v1/modem_io_device.c
Normal file
File diff suppressed because it is too large
Load diff
963
drivers/misc/modem_v1/modem_main.c
Normal file
963
drivers/misc/modem_v1/modem_main.c
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
/* linux/drivers/modem/modem.c
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#ifdef CONFIG_OF
|
||||
#include <linux/of_gpio.h>
|
||||
#endif
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
#include <linux/shm_ipc.h>
|
||||
#include <linux/mcu_ipc.h>
|
||||
#endif
|
||||
#if defined(CONFIG_LINK_DEVICE_LLI) &&\
|
||||
!defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
#include <linux/mipi-lli.h>
|
||||
#endif
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_variation.h"
|
||||
#include "modem_utils.h"
|
||||
|
||||
#define FMT_WAKE_TIME (HZ/2)
|
||||
#define RAW_WAKE_TIME (HZ*6)
|
||||
|
||||
static struct modem_shared *create_modem_shared_data(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct modem_shared *msd;
|
||||
int size = MAX_MIF_BUFF_SIZE;
|
||||
|
||||
msd = devm_kzalloc(dev, sizeof(struct modem_shared), GFP_KERNEL);
|
||||
if (!msd)
|
||||
return NULL;
|
||||
|
||||
/* initialize link device list */
|
||||
INIT_LIST_HEAD(&msd->link_dev_list);
|
||||
INIT_LIST_HEAD(&msd->activated_ndev_list);
|
||||
|
||||
/* initialize tree of io devices */
|
||||
msd->iodevs_tree_fmt = RB_ROOT;
|
||||
|
||||
msd->storage.cnt = 0;
|
||||
msd->storage.addr = devm_kzalloc(dev, MAX_MIF_BUFF_SIZE +
|
||||
(MAX_MIF_SEPA_SIZE * 2), GFP_KERNEL);
|
||||
if (!msd->storage.addr) {
|
||||
mif_err("IPC logger buff alloc failed!!\n");
|
||||
kfree(msd);
|
||||
return NULL;
|
||||
}
|
||||
memset(msd->storage.addr, 0, size + (MAX_MIF_SEPA_SIZE * 2));
|
||||
memcpy(msd->storage.addr, MIF_SEPARATOR, strlen(MIF_SEPARATOR));
|
||||
msd->storage.addr += MAX_MIF_SEPA_SIZE;
|
||||
memcpy(msd->storage.addr, &size, MAX_MIF_SEPA_SIZE);
|
||||
msd->storage.addr += MAX_MIF_SEPA_SIZE;
|
||||
spin_lock_init(&msd->lock);
|
||||
|
||||
return msd;
|
||||
}
|
||||
|
||||
static struct modem_ctl *create_modemctl_device(struct platform_device *pdev,
|
||||
struct modem_shared *msd)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct modem_data *pdata = pdev->dev.platform_data;
|
||||
struct modem_ctl *modemctl;
|
||||
int ret;
|
||||
|
||||
/* create modem control device */
|
||||
modemctl = devm_kzalloc(dev, sizeof(struct modem_ctl), GFP_KERNEL);
|
||||
if (!modemctl) {
|
||||
mif_err("%s: modemctl devm_kzalloc fail\n", pdata->name);
|
||||
mif_err("%s: xxx\n", pdata->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
modemctl->dev = dev;
|
||||
modemctl->name = pdata->name;
|
||||
modemctl->mdm_data = pdata;
|
||||
|
||||
modemctl->msd = msd;
|
||||
|
||||
modemctl->phone_state = STATE_OFFLINE;
|
||||
modemctl->airplane_mode = 0;
|
||||
|
||||
INIT_LIST_HEAD(&modemctl->modem_state_notify_list);
|
||||
spin_lock_init(&modemctl->lock);
|
||||
init_completion(&modemctl->init_cmpl);
|
||||
init_completion(&modemctl->off_cmpl);
|
||||
|
||||
/* init modemctl device for getting modemctl operations */
|
||||
ret = call_modem_init_func(modemctl, pdata);
|
||||
if (ret) {
|
||||
mif_err("%s: call_modem_init_func fail (err %d)\n",
|
||||
pdata->name, ret);
|
||||
mif_err("%s: xxx\n", pdata->name);
|
||||
devm_kfree(dev, modemctl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mif_info("%s is created!!!\n", pdata->name);
|
||||
|
||||
return modemctl;
|
||||
}
|
||||
|
||||
static struct io_device *create_io_device(struct platform_device *pdev,
|
||||
struct modem_io_t *io_t, struct modem_shared *msd,
|
||||
struct modem_ctl *modemctl, struct modem_data *pdata)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct io_device *iod;
|
||||
|
||||
iod = devm_kzalloc(dev, sizeof(struct io_device), GFP_KERNEL);
|
||||
if (!iod) {
|
||||
mif_err("iod == NULL\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&iod->list);
|
||||
RB_CLEAR_NODE(&iod->node_chan);
|
||||
RB_CLEAR_NODE(&iod->node_fmt);
|
||||
|
||||
iod->name = io_t->name;
|
||||
iod->id = io_t->id;
|
||||
iod->format = io_t->format;
|
||||
iod->io_typ = io_t->io_type;
|
||||
iod->link_types = io_t->links;
|
||||
iod->attrs = io_t->attrs;
|
||||
iod->app = io_t->app;
|
||||
iod->max_tx_size = io_t->ul_buffer_size;
|
||||
iod->net_typ = pdata->modem_net;
|
||||
iod->use_handover = pdata->use_handover;
|
||||
iod->ipc_version = pdata->ipc_version;
|
||||
atomic_set(&iod->opened, 0);
|
||||
spin_lock_init(&iod->info_id_lock);
|
||||
|
||||
/* link between io device and modem control */
|
||||
iod->mc = modemctl;
|
||||
|
||||
if (iod->format == IPC_FMT && iod->id == SIPC5_CH_ID_FMT_0)
|
||||
modemctl->iod = iod;
|
||||
|
||||
if (iod->format == IPC_BOOT) {
|
||||
modemctl->bootd = iod;
|
||||
mif_info("BOOT device = %s\n", iod->name);
|
||||
}
|
||||
|
||||
/* link between io device and modem shared */
|
||||
iod->msd = msd;
|
||||
|
||||
/* add iod to rb_tree */
|
||||
if (iod->format != IPC_RAW)
|
||||
insert_iod_with_format(msd, iod->format, iod);
|
||||
|
||||
if (sipc5_is_not_reserved_channel(iod->id))
|
||||
insert_iod_with_channel(msd, iod->id, iod);
|
||||
|
||||
/* register misc device or net device */
|
||||
ret = sipc5_init_io_device(iod);
|
||||
if (ret) {
|
||||
devm_kfree(dev, iod);
|
||||
mif_err("sipc5_init_io_device fail (%d)\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mif_info("%s created\n", iod->name);
|
||||
return iod;
|
||||
}
|
||||
|
||||
static int attach_devices(struct io_device *iod, enum modem_link tx_link)
|
||||
{
|
||||
struct modem_shared *msd = iod->msd;
|
||||
struct link_device *ld;
|
||||
|
||||
/* find link type for this io device */
|
||||
list_for_each_entry(ld, &msd->link_dev_list, list) {
|
||||
if (IS_CONNECTED(iod, ld)) {
|
||||
/* The count 1 bits of iod->link_types is count
|
||||
* of link devices of this iod.
|
||||
* If use one link device,
|
||||
* or, 2+ link devices and this link is tx_link,
|
||||
* set iod's link device with ld
|
||||
*/
|
||||
if ((count_bits(iod->link_types) <= 1) ||
|
||||
(tx_link == ld->link_type)) {
|
||||
mif_debug("set %s->%s\n", iod->name, ld->name);
|
||||
set_current_link(iod, ld);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if use rx dynamic switch, set tx_link at modem_io_t of
|
||||
* board-*-modems.c
|
||||
*/
|
||||
if (!get_current_link(iod)) {
|
||||
mif_err("%s->link == NULL\n", iod->name);
|
||||
BUG();
|
||||
}
|
||||
|
||||
wake_lock_init(&iod->wakelock, WAKE_LOCK_SUSPEND, iod->name);
|
||||
|
||||
switch (iod->id) {
|
||||
case SIPC5_CH_ID_FMT_0 ... SIPC5_CH_ID_FMT_9:
|
||||
iod->waketime = FMT_WAKE_TIME;
|
||||
break;
|
||||
|
||||
case SIPC5_CH_ID_BOOT_0 ... SIPC5_CH_ID_DUMP_9:
|
||||
iod->waketime = RAW_WAKE_TIME;
|
||||
break;
|
||||
|
||||
default:
|
||||
iod->waketime = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iod->format) {
|
||||
case IPC_FMT:
|
||||
iod->waketime = FMT_WAKE_TIME;
|
||||
break;
|
||||
|
||||
case IPC_BOOT ... IPC_DUMP:
|
||||
iod->waketime = RAW_WAKE_TIME;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int parse_dt_common_pdata(struct device_node *np,
|
||||
struct modem_data *pdata)
|
||||
{
|
||||
mif_dt_read_string(np, "mif,name", pdata->name);
|
||||
|
||||
mif_dt_read_enum(np, "mif,modem_net", pdata->modem_net);
|
||||
mif_dt_read_enum(np, "mif,modem_type", pdata->modem_type);
|
||||
mif_dt_read_bool(np, "mif,use_handover", pdata->use_handover);
|
||||
mif_dt_read_enum(np, "mif,ipc_version", pdata->ipc_version);
|
||||
|
||||
mif_dt_read_u32(np, "mif,link_types", pdata->link_types);
|
||||
mif_dt_read_string(np, "mif,link_name", pdata->link_name);
|
||||
mif_dt_read_u32(np, "mif,link_attrs", pdata->link_attrs);
|
||||
|
||||
mif_dt_read_u32(np, "mif,num_iodevs", pdata->num_iodevs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_SHMEM
|
||||
static int __parse_dt_mandatory_gpio_pdata(struct device_node *np,
|
||||
struct modem_data *pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* GPIO_CP_ON */
|
||||
pdata->gpio_cp_on = of_get_named_gpio(np, "mif,gpio_cp_on", 0);
|
||||
if (!gpio_is_valid(pdata->gpio_cp_on)) {
|
||||
mif_err("cp_on: Invalied gpio pins\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mif_info("gpio_cp_on: %d\n", pdata->gpio_cp_on);
|
||||
ret = gpio_request(pdata->gpio_cp_on, "CP_ON");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n", "CP_ON", ret);
|
||||
gpio_direction_output(pdata->gpio_cp_on, 0);
|
||||
|
||||
/* GPIO_CP_RESET */
|
||||
pdata->gpio_cp_reset = of_get_named_gpio(np, "mif,gpio_cp_reset", 0);
|
||||
if (!gpio_is_valid(pdata->gpio_cp_reset)) {
|
||||
mif_err("cp_rst: Invalied gpio pins\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mif_info("gpio_cp_reset: %d\n", pdata->gpio_cp_reset);
|
||||
ret = gpio_request(pdata->gpio_cp_reset, "CP_RST");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n", "CP_RST", ret);
|
||||
gpio_direction_output(pdata->gpio_cp_reset, 0);
|
||||
|
||||
/* GPIO_PDA_ACTIVE */
|
||||
pdata->gpio_pda_active = of_get_named_gpio(np,
|
||||
"mif,gpio_pda_active", 0);
|
||||
if (!gpio_is_valid(pdata->gpio_pda_active)) {
|
||||
mif_err("pda_active: Invalied gpio pins\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mif_info("gpio_pda_active: %d\n", pdata->gpio_pda_active);
|
||||
ret = gpio_request(pdata->gpio_pda_active, "PDA_ACTIVE");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n", "PDA_ACTIVE", ret);
|
||||
gpio_direction_output(pdata->gpio_pda_active, 0);
|
||||
|
||||
/* GPIO_PHONE_ACTIVE */
|
||||
pdata->gpio_phone_active = of_get_named_gpio(np,
|
||||
"mif,gpio_phone_active", 0);
|
||||
if (!gpio_is_valid(pdata->gpio_phone_active)) {
|
||||
mif_err("phone_active: Invalied gpio pins\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mif_info("gpio_phone_active: %d\n", pdata->gpio_phone_active);
|
||||
ret = gpio_request(pdata->gpio_phone_active, "PHONE_ACTIVE");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n", "PHONE_ACTIVE", ret);
|
||||
gpio_direction_input(pdata->gpio_phone_active);
|
||||
|
||||
/* GPIO_IPC_INT2CP */
|
||||
pdata->gpio_ipc_int2cp = of_get_named_gpio(np,
|
||||
"mif,gpio_ipc_int2cp", 0);
|
||||
if (gpio_is_valid(pdata->gpio_ipc_int2cp)) {
|
||||
mif_info("gpio_ipc_int2cp: %d\n", pdata->gpio_ipc_int2cp);
|
||||
ret = gpio_request(pdata->gpio_ipc_int2cp, "IPC_INT2CP");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n",
|
||||
"SEND_SIG", ret);
|
||||
else
|
||||
gpio_direction_output(pdata->gpio_ipc_int2cp, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __parse_dt_optional_gpio_pdata(struct device_node *np,
|
||||
struct modem_data *pdata)
|
||||
{
|
||||
#if !defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
int ret;
|
||||
|
||||
/* GPIO_CP_WAKEUP */
|
||||
pdata->gpio_cp_wakeup = of_get_named_gpio(np,
|
||||
"mif,gpio_cp_wakeup", 0);
|
||||
if (gpio_is_valid(pdata->gpio_cp_wakeup)) {
|
||||
mif_info("gpio_cp_wakeup: %d\n", pdata->gpio_cp_wakeup);
|
||||
ret = gpio_request(pdata->gpio_cp_wakeup, "CP_WAKEUP");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n",
|
||||
"CP_WAKEUP", ret);
|
||||
else
|
||||
gpio_direction_output(pdata->gpio_cp_wakeup, 0);
|
||||
}
|
||||
|
||||
/* GPIO_AP_STATUS */
|
||||
pdata->gpio_ap_status = of_get_named_gpio(np,
|
||||
"mif,gpio_ap_status", 0);
|
||||
if (gpio_is_valid(pdata->gpio_ap_status)) {
|
||||
mif_info("gpio_ap_status: %d\n", pdata->gpio_ap_status);
|
||||
ret = gpio_request(pdata->gpio_ap_status, "AP_STATUS");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n",
|
||||
"AP_STATUS", ret);
|
||||
else
|
||||
gpio_direction_output(pdata->gpio_ap_status, 0);
|
||||
}
|
||||
|
||||
/* GPIO_AP_WAKEUP */
|
||||
pdata->gpio_ap_wakeup = of_get_named_gpio(np,
|
||||
"mif,gpio_ap_wakeup", 0);
|
||||
if (gpio_is_valid(pdata->gpio_ap_wakeup)) {
|
||||
mif_info("gpio_ap_wakeup: %d\n", pdata->gpio_ap_wakeup);
|
||||
ret = gpio_request(pdata->gpio_ap_wakeup, "AP_WAKEUP");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n",
|
||||
"AP_WAKEUP", ret);
|
||||
else
|
||||
gpio_direction_input(pdata->gpio_ap_wakeup);
|
||||
}
|
||||
|
||||
/* GPIO_CP_STATUS */
|
||||
pdata->gpio_cp_status = of_get_named_gpio(np,
|
||||
"mif,gpio_cp_status", 0);
|
||||
if (gpio_is_valid(pdata->gpio_cp_status)) {
|
||||
mif_info("gpio_cp_status: %d\n", pdata->gpio_cp_status);
|
||||
ret = gpio_request(pdata->gpio_cp_status, "CP_STATUS");
|
||||
if (ret)
|
||||
mif_err("fail to request gpio %s:%d\n",
|
||||
"CP_STATUS", ret);
|
||||
else
|
||||
gpio_direction_input(pdata->gpio_cp_status);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int parse_dt_gpio_pdata(struct device_node *np, struct modem_data *pdata)
|
||||
{
|
||||
int err = 0;
|
||||
#if defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
struct device_node *pm_node;
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_SHMEM
|
||||
err = __parse_dt_mandatory_gpio_pdata(np, pdata);
|
||||
if (err)
|
||||
return err;
|
||||
#endif
|
||||
__parse_dt_optional_gpio_pdata(np, pdata);
|
||||
|
||||
#if defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
pm_node = of_get_child_by_name(np, PM_DT_NODE_NAME);
|
||||
if (!pm_node) {
|
||||
mif_err("ERR! No PM DT node\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
err = link_pm_parse_dt_gpio_pdata(pm_node, pdata);
|
||||
if (err)
|
||||
return err;
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static int parse_dt_mbox_pdata(struct device *dev, struct device_node *np,
|
||||
struct modem_data *pdata)
|
||||
{
|
||||
struct modem_mbox *mbox;
|
||||
int ret = 0;
|
||||
#ifndef CONFIG_ECT
|
||||
const struct property *prop;
|
||||
const __be32 *val;
|
||||
int nr;
|
||||
#endif
|
||||
|
||||
if (pdata->link_types != LINKTYPE(LINKDEV_SHMEM))
|
||||
return ret;
|
||||
|
||||
mbox = devm_kzalloc(dev, sizeof(struct modem_mbox), GFP_KERNEL);
|
||||
if (!mbox) {
|
||||
mif_err("mbox: failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pdata->mbx = mbox;
|
||||
|
||||
mif_dt_read_u32 (np, "mbx_ap2cp_msg", mbox->mbx_ap2cp_msg);
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_msg", mbox->mbx_cp2ap_msg);
|
||||
mif_dt_read_u32 (np, "mbx_ap2cp_united_status",
|
||||
mbox->mbx_ap2cp_status);
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_united_status",
|
||||
mbox->mbx_cp2ap_status);
|
||||
|
||||
mif_dt_read_u32 (np, "mif,int_ap2cp_msg", mbox->int_ap2cp_msg);
|
||||
mif_dt_read_u32 (np, "mif,int_ap2cp_status", mbox->int_ap2cp_status);
|
||||
mif_dt_read_u32 (np, "mif,int_ap2cp_active", mbox->int_ap2cp_active);
|
||||
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_msg", mbox->irq_cp2ap_msg);
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_status", mbox->irq_cp2ap_status);
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_active", mbox->irq_cp2ap_active);
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_wake_lock",
|
||||
mbox->irq_cp2ap_wake_lock);
|
||||
|
||||
/* Value for Performance Request */
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_dvfsreq", mbox->mbx_cp2ap_perf_req);
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_dvfsreq_cpu",
|
||||
mbox->mbx_cp2ap_perf_req_cpu);
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_dvfsreq_mif",
|
||||
mbox->mbx_cp2ap_perf_req_mif);
|
||||
mif_dt_read_u32 (np, "mbx_cp2ap_dvfsreq_int",
|
||||
mbox->mbx_cp2ap_perf_req_int);
|
||||
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_perf_req_cpu",
|
||||
mbox->irq_cp2ap_perf_req_cpu);
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_perf_req_mif",
|
||||
mbox->irq_cp2ap_perf_req_mif);
|
||||
mif_dt_read_u32 (np, "mif,irq_cp2ap_perf_req_int",
|
||||
mbox->irq_cp2ap_perf_req_int);
|
||||
|
||||
/* Status Bit Info */
|
||||
mif_dt_read_u32 (np, "sbi_evs_mode_mask", mbox->sbi_evs_mode_mask);
|
||||
mif_dt_read_u32 (np, "sbi_evs_mode_pos", mbox->sbi_evs_mode_pos);
|
||||
mif_dt_read_u32 (np, "sbi_lte_active_mask", mbox->sbi_lte_active_mask);
|
||||
mif_dt_read_u32 (np, "sbi_lte_active_pos", mbox->sbi_lte_active_pos);
|
||||
mif_dt_read_u32 (np, "sbi_cp_status_mask", mbox->sbi_cp_status_mask);
|
||||
mif_dt_read_u32 (np, "sbi_cp_status_pos", mbox->sbi_cp_status_pos);
|
||||
mif_dt_read_u32 (np, "sbi_pda_active_mask", mbox->sbi_pda_active_mask);
|
||||
mif_dt_read_u32 (np, "sbi_pda_active_pos", mbox->sbi_pda_active_pos);
|
||||
mif_dt_read_u32 (np, "sbi_ap_status_mask", mbox->sbi_ap_status_mask);
|
||||
mif_dt_read_u32 (np, "sbi_ap_status_pos", mbox->sbi_ap_status_pos);
|
||||
|
||||
#ifndef CONFIG_ECT
|
||||
/* get AP core clock table from DT */
|
||||
prop = of_find_property(np, "mif,ap_clk_table", NULL);
|
||||
if (!prop || !prop->value) {
|
||||
mif_err("failed to get ap_clk_table prop\n");
|
||||
mbox->ap_clk_cnt = 0;
|
||||
} else {
|
||||
nr = prop->length / sizeof(u32);
|
||||
mbox->ap_clk_cnt = nr;
|
||||
|
||||
mbox->ap_clk_table = devm_kzalloc(dev, sizeof(u32) * nr, GFP_KERNEL);
|
||||
if (!mbox->ap_clk_table) {
|
||||
mif_err("ap_clk_table: failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
val = prop->value;
|
||||
while (nr--)
|
||||
mbox->ap_clk_table[nr] = be32_to_cpup(val++);
|
||||
}
|
||||
|
||||
/* get MIF clock table from DT */
|
||||
prop = of_find_property(np, "mif,mif_clk_table", NULL);
|
||||
if (!prop || !prop->value) {
|
||||
mif_err("failed to get mif_clk_table prop\n");
|
||||
mbox->mif_clk_cnt = 0;
|
||||
} else {
|
||||
nr = prop->length / sizeof(u32);
|
||||
mbox->mif_clk_cnt = nr;
|
||||
|
||||
mbox->mif_clk_table = devm_kzalloc(dev, sizeof(u32) * nr, GFP_KERNEL);
|
||||
if (!mbox->mif_clk_table) {
|
||||
mif_err("mif_clk_table: failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
val = prop->value;
|
||||
while (nr--)
|
||||
mbox->mif_clk_table[nr] = be32_to_cpup(val++);
|
||||
}
|
||||
|
||||
/* get INT clock table from DT */
|
||||
prop = of_find_property(np, "mif,int_clk_table", NULL);
|
||||
if (!prop || !prop->value) {
|
||||
mif_err("failed to get int_clk_table prop\n");
|
||||
mbox->int_clk_cnt = 0;
|
||||
} else {
|
||||
nr = prop->length / sizeof(u32);
|
||||
mbox->int_clk_cnt = nr;
|
||||
|
||||
mbox->int_clk_table = devm_kzalloc(dev, sizeof(u32) * nr, GFP_KERNEL);
|
||||
if (!mbox->int_clk_table) {
|
||||
mif_err("int_clk_table: failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
val = prop->value;
|
||||
while (nr--)
|
||||
mbox->int_clk_table[nr] = be32_to_cpup(val++);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_dt_iodevs_pdata(struct device *dev, struct device_node *np,
|
||||
struct modem_data *pdata)
|
||||
{
|
||||
struct device_node *child = NULL;
|
||||
size_t size = sizeof(struct modem_io_t) * pdata->num_iodevs;
|
||||
int i = 0;
|
||||
|
||||
pdata->iodevs = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
if (!pdata->iodevs) {
|
||||
mif_err("iodevs: failed to alloc memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct modem_io_t *iod;
|
||||
|
||||
iod = &pdata->iodevs[i];
|
||||
|
||||
mif_dt_read_string(child, "iod,name", iod->name);
|
||||
mif_dt_read_u32(child, "iod,id", iod->id);
|
||||
mif_dt_read_enum(child, "iod,format", iod->format);
|
||||
mif_dt_read_enum(child, "iod,io_type", iod->io_type);
|
||||
mif_dt_read_u32(child, "iod,links", iod->links);
|
||||
if (count_bits(iod->links) > 1)
|
||||
mif_dt_read_enum(child, "iod,tx_link", iod->tx_link);
|
||||
mif_dt_read_u32(child, "iod,attrs", iod->attrs);
|
||||
/* mif_dt_read_string(child, "iod,app", iod->app); */
|
||||
mif_dt_read_u32_noerr(child, "iod,max_tx_size",
|
||||
iod->ul_buffer_size);
|
||||
|
||||
if (iod->attrs & IODEV_ATTR(ATTR_SBD_IPC)) {
|
||||
mif_dt_read_u32(child, "iod,ul_num_buffers",
|
||||
iod->ul_num_buffers);
|
||||
mif_dt_read_u32(child, "iod,ul_buffer_size",
|
||||
iod->ul_buffer_size);
|
||||
mif_dt_read_u32(child, "iod,dl_num_buffers",
|
||||
iod->dl_num_buffers);
|
||||
mif_dt_read_u32(child, "iod,dl_buffer_size",
|
||||
iod->dl_buffer_size);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct modem_data *modem_if_parse_dt_pdata(struct device *dev)
|
||||
{
|
||||
struct modem_data *pdata;
|
||||
struct device_node *iodevs = NULL;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(struct modem_data), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
mif_err("modem_data: alloc fail\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (parse_dt_common_pdata(dev->of_node, pdata)) {
|
||||
mif_err("DT error: failed to parse common\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (parse_dt_gpio_pdata(dev->of_node, pdata)) {
|
||||
mif_err("DT error: failed to parse gpio\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (parse_dt_mbox_pdata(dev, dev->of_node, pdata)) {
|
||||
mif_err("DT error: failed to parse mbox\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
iodevs = of_get_child_by_name(dev->of_node, "iodevs");
|
||||
if (!iodevs) {
|
||||
mif_err("DT error: failed to get child node\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (parse_dt_iodevs_pdata(dev, iodevs, pdata)) {
|
||||
mif_err("DT error: failed to parse iodevs\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev->platform_data = pdata;
|
||||
mif_info("DT parse complete!\n");
|
||||
return pdata;
|
||||
|
||||
error:
|
||||
if (pdata) {
|
||||
if (pdata->iodevs)
|
||||
devm_kfree(dev, pdata->iodevs);
|
||||
devm_kfree(dev, pdata);
|
||||
}
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static const struct of_device_id sec_modem_match[] = {
|
||||
{ .compatible = "sec_modem,modem_pdata", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sec_modem_match);
|
||||
#else
|
||||
static struct modem_data *modem_if_parse_dt_pdata(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
enum mif_sim_mode {
|
||||
MIF_SIM_NONE = 0,
|
||||
MIF_SIM_SINGLE,
|
||||
MIF_SIM_DUAL,
|
||||
MIF_SIM_TRIPLE,
|
||||
};
|
||||
|
||||
static int simslot_count(struct seq_file *m, void *v)
|
||||
{
|
||||
enum mif_sim_mode mode = (enum mif_sim_mode)m->private;
|
||||
|
||||
seq_printf(m, "%u\n", mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simslot_count_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, simslot_count, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations simslot_count_fops = {
|
||||
.open = simslot_count_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GPIO_DS_DETECT
|
||||
static enum mif_sim_mode get_sim_mode(struct device_node *of_node)
|
||||
{
|
||||
enum mif_sim_mode mode = MIF_SIM_SINGLE;
|
||||
int gpio_ds_det;
|
||||
int retval;
|
||||
|
||||
gpio_ds_det = of_get_named_gpio(of_node, "mif,gpio_ds_det", 0);
|
||||
if (!gpio_is_valid(gpio_ds_det)) {
|
||||
mif_err("DT error: failed to get sim mode\n");
|
||||
goto make_proc;
|
||||
}
|
||||
|
||||
retval = gpio_request(gpio_ds_det, "DS_DET");
|
||||
if (retval) {
|
||||
mif_err("Failed to reqeust GPIO(%d)\n", retval);
|
||||
goto make_proc;
|
||||
} else {
|
||||
gpio_direction_input(gpio_ds_det);
|
||||
}
|
||||
|
||||
retval = gpio_get_value(gpio_ds_det);
|
||||
if (retval)
|
||||
mode = MIF_SIM_DUAL;
|
||||
|
||||
mif_info("sim_mode: %d\n", mode);
|
||||
gpio_free(gpio_ds_det);
|
||||
|
||||
make_proc:
|
||||
if (!proc_create_data("simslot_count", 0, NULL, &simslot_count_fops,
|
||||
(void *)(long)mode)) {
|
||||
mif_err("Failed to create proc\n");
|
||||
mode = MIF_SIM_SINGLE;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
#else
|
||||
static enum mif_sim_mode get_sim_mode(struct device_node *of_node)
|
||||
{
|
||||
return MIF_SIM_SINGLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int modem_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct modem_data *pdata = dev->platform_data;
|
||||
struct modem_shared *msd;
|
||||
struct modem_ctl *modemctl;
|
||||
struct io_device **iod;
|
||||
unsigned size;
|
||||
struct link_device *ld;
|
||||
enum mif_sim_mode sim_mode;
|
||||
|
||||
mif_info("%s: +++\n", pdev->name);
|
||||
|
||||
if (dev->of_node) {
|
||||
pdata = modem_if_parse_dt_pdata(dev);
|
||||
if (IS_ERR(pdata)) {
|
||||
mif_err("MIF DT parse error!\n");
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
} else {
|
||||
if (!pdata) {
|
||||
mif_err("Non-DT, incorrect pdata!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
msd = create_modem_shared_data(pdev);
|
||||
if (!msd) {
|
||||
mif_err("%s: msd == NULL\n", pdata->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
modemctl = create_modemctl_device(pdev, msd);
|
||||
if (!modemctl) {
|
||||
mif_err("%s: modemctl == NULL\n", pdata->name);
|
||||
devm_kfree(dev, msd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* create link device */
|
||||
/* support multi-link device */
|
||||
for (i = 0; i < LINKDEV_MAX; i++) {
|
||||
/* find matching link type */
|
||||
if (pdata->link_types & LINKTYPE(i)) {
|
||||
ld = call_link_init_func(pdev, i);
|
||||
if (!ld)
|
||||
goto free_mc;
|
||||
|
||||
mif_info("%s: %s link created\n", pdata->name, ld->name);
|
||||
|
||||
ld->link_type = i;
|
||||
ld->mc = modemctl;
|
||||
ld->msd = msd;
|
||||
|
||||
list_add(&ld->list, &msd->link_dev_list);
|
||||
}
|
||||
}
|
||||
|
||||
sim_mode = get_sim_mode(dev->of_node);
|
||||
|
||||
/* create io deivces and connect to modemctl device */
|
||||
size = sizeof(struct io_device *) * pdata->num_iodevs;
|
||||
iod = kzalloc(size, GFP_KERNEL);
|
||||
for (i = 0; i < pdata->num_iodevs; i++) {
|
||||
if (sim_mode < MIF_SIM_DUAL &&
|
||||
pdata->iodevs[i].attrs & IODEV_ATTR(ATTR_DUALSIM))
|
||||
continue;
|
||||
|
||||
iod[i] = create_io_device(pdev, &pdata->iodevs[i], msd,
|
||||
modemctl, pdata);
|
||||
if (!iod[i]) {
|
||||
mif_err("%s: iod[%d] == NULL\n", pdata->name, i);
|
||||
goto free_iod;
|
||||
}
|
||||
|
||||
if (iod[i]->format == IPC_FMT || iod[i]->format == IPC_BOOT)
|
||||
list_add_tail(&iod[i]->list,
|
||||
&modemctl->modem_state_notify_list);
|
||||
|
||||
attach_devices(iod[i], pdata->iodevs[i].tx_link);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, modemctl);
|
||||
|
||||
kfree(iod);
|
||||
|
||||
mif_info("%s: ---\n", pdev->name);
|
||||
return 0;
|
||||
|
||||
free_iod:
|
||||
for (i = 0; i < pdata->num_iodevs; i++) {
|
||||
if (iod[i])
|
||||
devm_kfree(dev, iod[i]);
|
||||
}
|
||||
kfree(iod);
|
||||
|
||||
free_mc:
|
||||
if (modemctl)
|
||||
devm_kfree(dev, modemctl);
|
||||
|
||||
if (msd)
|
||||
devm_kfree(dev, msd);
|
||||
|
||||
mif_err("%s: xxx\n", pdev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void modem_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct modem_ctl *mc = dev_get_drvdata(dev);
|
||||
|
||||
mc->ops.modem_shutdown(mc);
|
||||
mc->phone_state = STATE_OFFLINE;
|
||||
|
||||
evt_log(0, "%s(%s)\n", mc->name, FUNC);
|
||||
}
|
||||
|
||||
static int modem_suspend(struct device *pdev)
|
||||
{
|
||||
struct modem_ctl *mc = dev_get_drvdata(pdev);
|
||||
|
||||
#if !defined(CONFIG_LINK_DEVICE_HSIC)
|
||||
if (mc->gpio_pda_active)
|
||||
gpio_set_value(mc->gpio_pda_active, 0);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_LINK_DEVICE_LLI) &&\
|
||||
!defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
mipi_lli_suspend();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
if (mc->airplane_mode == 0) {
|
||||
mif_err("%s: pda_active:0\n", mc->name);
|
||||
mbox_update_value(MCU_CP, mc->mbx_ap_status, 0,
|
||||
mc->sbi_pda_active_mask, mc->sbi_pda_active_pos);
|
||||
mbox_set_interrupt(MCU_CP, mc->int_pda_active);
|
||||
}
|
||||
#endif
|
||||
|
||||
evt_log(0, "%s: %s\n", FUNC, mc->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modem_resume(struct device *pdev)
|
||||
{
|
||||
struct modem_ctl *mc = dev_get_drvdata(pdev);
|
||||
|
||||
#if !defined(CONFIG_LINK_DEVICE_HSIC)
|
||||
if (mc->gpio_pda_active)
|
||||
gpio_set_value(mc->gpio_pda_active, 1);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_LINK_DEVICE_LLI) &&\
|
||||
!defined(CONFIG_LINK_POWER_MANAGEMENT_WITH_FSM)
|
||||
mipi_lli_resume();
|
||||
|
||||
/* CP initiated resume case */
|
||||
if (gpio_get_value(mc->gpio_ap_wakeup)) {
|
||||
mif_info("gpio_ap_wakeup high\n");
|
||||
mipi_lli_set_link_status(LLI_WAITFORMOUNT);
|
||||
gpio_set_value(mc->gpio_ap_status, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
if (mc->airplane_mode == 0) {
|
||||
mif_err("%s: pda_active:1\n", mc->name);
|
||||
mbox_update_value(MCU_CP, mc->mbx_ap_status, 1,
|
||||
mc->sbi_pda_active_mask, mc->sbi_pda_active_pos);
|
||||
mbox_set_interrupt(MCU_CP, mc->int_pda_active);
|
||||
}
|
||||
#endif
|
||||
|
||||
evt_log(0, "%s: %s\n", FUNC, mc->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops modem_pm_ops = {
|
||||
.suspend = modem_suspend,
|
||||
.resume = modem_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver modem_driver = {
|
||||
.probe = modem_probe,
|
||||
.shutdown = modem_shutdown,
|
||||
.driver = {
|
||||
.name = "mif_sipc5",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &modem_pm_ops,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(sec_modem_match),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(modem_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Samsung Modem Interface Driver");
|
||||
41
drivers/misc/modem_v1/modem_notifier.c
Executable file
41
drivers/misc/modem_v1/modem_notifier.c
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 <stdarg.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/modem_notifier.h>
|
||||
|
||||
#include "modem_prj.h"
|
||||
#include "modem_utils.h"
|
||||
|
||||
static struct raw_notifier_head modem_event_notifier;
|
||||
|
||||
int register_modem_event_notifier(struct notifier_block *nb)
|
||||
{
|
||||
if(!nb)
|
||||
return -ENOENT;
|
||||
|
||||
return raw_notifier_chain_register(&modem_event_notifier, nb);
|
||||
}
|
||||
|
||||
void modem_notify_event(enum modem_event evt)
|
||||
{
|
||||
mif_err("event notify (%d) ++\n", evt);
|
||||
raw_notifier_call_chain(&modem_event_notifier, evt, NULL);
|
||||
mif_err("event notify (%d) --\n", evt);
|
||||
}
|
||||
|
||||
337
drivers/misc/modem_v1/modem_pktlog.c
Normal file
337
drivers/misc/modem_v1/modem_pktlog.c
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* 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/poll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "modem_pktlog.h"
|
||||
|
||||
#if 0
|
||||
#define PKTLOG_MAX_LEN 256
|
||||
void pktlog_copy_buf(struct pktlog_data *pktlog, int type, void *buf)
|
||||
{
|
||||
struct sk_buff *pkt;
|
||||
int len;
|
||||
|
||||
if (!pktlog || !pktlog->qmax)
|
||||
return;
|
||||
|
||||
pkt = alloc_skb(PKTLOG_MAX_LEN,
|
||||
int_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
|
||||
if (!pkt) {
|
||||
pr_err("%s: fail to alloc skb for pktlog buf\n", __func__);
|
||||
return;
|
||||
}
|
||||
pkt->tstamp = ktime_get_real();
|
||||
pktpriv(pkt)->type = type;
|
||||
|
||||
/*TODO: log from fmt variable prameters */
|
||||
|
||||
/* skb_put(); */
|
||||
|
||||
skb_queue_tail(&pktlog->logq, pkt);
|
||||
if (pktlog->logq->qlen > qmax) {
|
||||
struct sk_buff *drop = skb_dequene(&pktlog->logq);
|
||||
if (drop)
|
||||
dev_kfree_skb_any(drop);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void pktlog_queue_skb(struct pktlog_data *pktlog, unsigned char dir,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *pkt;
|
||||
|
||||
if (!pktlog || !pktlog->qmax)
|
||||
return;
|
||||
|
||||
pkt = skb_clone(skb, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
|
||||
if (!pkt) {
|
||||
pr_err("%s: fail to skb_clone for pktlog buf\n", __func__);
|
||||
return;
|
||||
}
|
||||
pkt->tstamp = timespec_to_ktime(current_kernel_time());
|
||||
pktpriv(pkt)->dir = dir;
|
||||
|
||||
skb_queue_tail(&pktlog->logq, pkt);
|
||||
if (pktlog->logq.qlen > pktlog->qmax) {
|
||||
struct sk_buff *drop = skb_dequeue(&pktlog->logq);
|
||||
if (drop)
|
||||
dev_kfree_skb_any(drop);
|
||||
printk_ratelimited(KERN_INFO "%s: over qmax(%d)\n", __func__,
|
||||
pktlog->logq.qlen);
|
||||
}
|
||||
wake_up(&pktlog->wq);
|
||||
}
|
||||
|
||||
static int pktlog_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct pktlog_data *pktlog = filp->private_data;
|
||||
|
||||
if (!pktlog) {
|
||||
pr_err("%s: Invalid pktlog data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (atomic_inc_return(&pktlog->opened) >= 2) {
|
||||
pr_err("%s: Not support multi user oepn(%d)\n", __func__,
|
||||
atomic_read(&pktlog->opened));
|
||||
atomic_dec(&pktlog->opened);
|
||||
return -EINVAL;
|
||||
}
|
||||
pktlog->file_hdr.snaplen = pktlog->snaplen;
|
||||
pktlog->copy_file_header = true;
|
||||
|
||||
pr_info("%s: qmax = %d open by %s\n", __func__, pktlog->qmax,
|
||||
current->comm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pktlog_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct pktlog_data *pktlog = filp->private_data;
|
||||
|
||||
pr_info("%s: qmax = %d close by %s- %d\n", __func__, pktlog->qmax,
|
||||
current->comm, atomic_dec_return(&pktlog->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int pktlog_poll(struct file *filp, struct poll_table_struct *wait)
|
||||
{
|
||||
struct pktlog_data *pktlog = filp->private_data;
|
||||
|
||||
if (!pktlog) {
|
||||
pr_err("%s: Invalid pktlog data\n", __func__);
|
||||
return POLLERR;
|
||||
}
|
||||
|
||||
if (skb_queue_empty(&pktlog->logq))
|
||||
poll_wait(filp, &pktlog->wq, wait);
|
||||
|
||||
return POLLIN;
|
||||
}
|
||||
|
||||
static ssize_t pktlog_read(struct file *filp, char *buf, size_t count,
|
||||
loff_t *fpos)
|
||||
{
|
||||
struct pktlog_data *pktlog = filp->private_data;
|
||||
struct sk_buff *pkt;
|
||||
char *p = buf;
|
||||
struct pktdump_hdr *hdr = &pktlog->hdr;
|
||||
struct timeval tv;
|
||||
int ret;
|
||||
unsigned cook_hdr_len = sizeof(struct pktdump_hdr)
|
||||
- sizeof(struct pcap_hdr);
|
||||
unsigned payload_len, cplen = 0;
|
||||
|
||||
if (!pktlog) {
|
||||
pr_err("%s: Invalid pktlog data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pktlog->copy_file_header) {
|
||||
pktlog->copy_file_header = false;
|
||||
ret = copy_to_user(p, &pktlog->file_hdr,
|
||||
sizeof(struct pcap_file_header));
|
||||
if (ret < 0)
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: fileheader copy fail\n", __func__);
|
||||
cplen += sizeof(struct pcap_file_header);
|
||||
p += sizeof(struct pcap_file_header);
|
||||
}
|
||||
|
||||
pkt = skb_dequeue(&pktlog->logq);
|
||||
if (unlikely(!pkt)) {
|
||||
printk_ratelimited(KERN_ERR "%s: no log pakcets\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tv = ktime_to_timeval(pkt->tstamp);
|
||||
hdr->pcap.tv_sec = tv.tv_sec;
|
||||
hdr->pcap.tv_usec = tv.tv_usec;
|
||||
hdr->pcap.len = cook_hdr_len + pkt->len;
|
||||
hdr->pcap.caplen = min(hdr->pcap.len, pktlog->snaplen);
|
||||
|
||||
hdr->sd.dir = pktpriv(pkt)->dir;
|
||||
|
||||
ret = copy_to_user(p, hdr, sizeof(struct pktdump_hdr));
|
||||
if (ret < 0) {
|
||||
printk_ratelimited(KERN_ERR "%s: pcap header copy fail\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
cplen += sizeof(struct pktdump_hdr);
|
||||
|
||||
p += sizeof(struct pktdump_hdr);
|
||||
payload_len = min(pkt->len, pktlog->snaplen - cook_hdr_len);
|
||||
ret = copy_to_user(p, pkt->data, payload_len);
|
||||
if (ret < 0) {
|
||||
printk_ratelimited(KERN_ERR "%s: pcap data copy fail\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
cplen += payload_len;
|
||||
|
||||
exit:
|
||||
dev_kfree_skb_any(pkt);
|
||||
return cplen;
|
||||
}
|
||||
|
||||
static ssize_t show_snaplen(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pktlog_data *pktlog = dev_get_drvdata(dev);
|
||||
char *p = buf;
|
||||
|
||||
p += sprintf(buf, "packet log snaplen : %u\n", pktlog->snaplen);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
static ssize_t store_snaplen(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pktlog_data *pktlog = dev_get_drvdata(dev);
|
||||
unsigned snaplen;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &snaplen);
|
||||
if (ret)
|
||||
return count;
|
||||
|
||||
pktlog->snaplen = snaplen;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute attr_snaplen =
|
||||
__ATTR(snaplen, S_IRUGO | S_IWUSR, show_snaplen, store_snaplen);
|
||||
|
||||
static ssize_t show_qmax(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pktlog_data *pktlog = dev_get_drvdata(dev);
|
||||
char *p = buf;
|
||||
|
||||
p += sprintf(buf, "packet log queue max : %u\n", pktlog->qmax);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
static ssize_t store_qmax(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pktlog_data *pktlog = dev_get_drvdata(dev);
|
||||
unsigned qmax;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &qmax);
|
||||
if (ret)
|
||||
return count;
|
||||
|
||||
pktlog->qmax = qmax;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute attr_qmax =
|
||||
__ATTR(qmax, S_IRUGO | S_IWUSR, show_qmax, store_qmax);
|
||||
|
||||
static const struct file_operations pktlog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pktlog_open,
|
||||
.release = pktlog_release,
|
||||
.poll = pktlog_poll,
|
||||
.read = pktlog_read,
|
||||
};
|
||||
|
||||
static void init_pcap_fileheader(struct pktlog_data *pktlog)
|
||||
{
|
||||
struct pcap_file_header *hdr = &pktlog->file_hdr;
|
||||
|
||||
hdr->magic = TCPDUMP_MAGIC;
|
||||
hdr->version_major = PCAP_VERSION_MAJOR;
|
||||
hdr->version_minor = PCAP_VERSION_MINOR;
|
||||
hdr->thiszone = 0;
|
||||
hdr->sigflag = 0;
|
||||
hdr->snaplen = pktlog->snaplen;
|
||||
hdr->linktype = WTAP_ENCAP_USER0;
|
||||
}
|
||||
|
||||
struct pktlog_data *create_pktlog(char *name)
|
||||
{
|
||||
struct pktlog_data *pktlog;
|
||||
int ret;
|
||||
char node[64] = "pktlog_"; /* /dev/pktlog/{name} */
|
||||
|
||||
pktlog = kzalloc(sizeof(struct pktlog_data), GFP_KERNEL);
|
||||
if (!pktlog) {
|
||||
pr_err("%s: pktlog data alloc fail\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pktlog->misc.minor = MISC_DYNAMIC_MINOR;
|
||||
pktlog->misc.name = strncat(node, name, 50);
|
||||
pktlog->misc.fops = &pktlog_fops;
|
||||
pktlog->misc.parent = NULL;
|
||||
|
||||
init_waitqueue_head(&pktlog->wq);
|
||||
skb_queue_head_init(&pktlog->logq);
|
||||
pktlog->qmax = 0;
|
||||
pktlog->snaplen = 256;
|
||||
atomic_set(&pktlog->opened, 0);
|
||||
|
||||
ret = misc_register(&pktlog->misc);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: fail to register misc device '%s'\n", __func__,
|
||||
pktlog->misc.name);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
ret = device_create_file(pktlog->misc.this_device, &attr_qmax);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to create qmax sysfs file: %s\n", __func__,
|
||||
name);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
ret = device_create_file(pktlog->misc.this_device, &attr_snaplen);
|
||||
if (ret) {
|
||||
pr_err("%s: fail to create snaplen sysfs file: %s\n", __func__,
|
||||
name);
|
||||
goto free_exit;
|
||||
}
|
||||
init_pcap_fileheader(pktlog);
|
||||
pr_info("%s: probed - %s\n", __func__, name);
|
||||
|
||||
return pktlog;
|
||||
|
||||
free_exit:
|
||||
kfree(pktlog);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void remove_pktlog(struct pktlog_data *pktlog)
|
||||
{
|
||||
if (!pktlog)
|
||||
return;
|
||||
|
||||
device_remove_file(pktlog->misc.this_device, &attr_qmax);
|
||||
misc_deregister(&pktlog->misc);
|
||||
kfree(pktlog);
|
||||
}
|
||||
105
drivers/misc/modem_v1/modem_pktlog.h
Normal file
105
drivers/misc/modem_v1/modem_pktlog.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#ifndef __MODEM_PKTLOG_H__
|
||||
#define __MODEM_PKTLOG_H__
|
||||
|
||||
#ifdef CONFIG_DEBUG_PKTLOG
|
||||
|
||||
#include <linux/time.h>
|
||||
|
||||
struct pktbuf_private {
|
||||
unsigned char dir;
|
||||
} __packed;
|
||||
#define pktpriv(pkt) ((struct pktbuf_private *)&pkt->cb)
|
||||
|
||||
#define PCAP_VERSION_MAJOR 2
|
||||
#define PCAP_VERSION_MINOR 4
|
||||
#define TCPDUMP_MAGIC 0xa1b2c3d4
|
||||
|
||||
#define DLT_LINUX_SLL 113
|
||||
#define WTAP_ENCAP_USER0 147 /* sipc5 header */
|
||||
|
||||
struct pcap_file_header {
|
||||
unsigned magic;
|
||||
unsigned short version_major;
|
||||
unsigned short version_minor;
|
||||
int thiszone;
|
||||
unsigned sigflag;
|
||||
unsigned snaplen;
|
||||
unsigned linktype;
|
||||
} __packed;
|
||||
|
||||
struct pcap_hdr {
|
||||
unsigned tv_sec;
|
||||
unsigned tv_usec;
|
||||
unsigned caplen;
|
||||
unsigned len;
|
||||
} __packed;
|
||||
|
||||
#define SLL_ADDRLEN 8
|
||||
struct sll_hdr {
|
||||
unsigned short pkttype;
|
||||
unsigned short hatype;
|
||||
unsigned short halen;
|
||||
unsigned char addr[SLL_ADDRLEN];
|
||||
unsigned short protocol;
|
||||
} __packed;
|
||||
|
||||
struct sipc_debug {
|
||||
unsigned char dir;
|
||||
} __packed;
|
||||
|
||||
struct pktdump_hdr {
|
||||
struct pcap_hdr pcap;
|
||||
struct sipc_debug sd;
|
||||
} __packed;
|
||||
|
||||
struct pktlog_data {
|
||||
struct miscdevice misc;
|
||||
atomic_t opened;
|
||||
wait_queue_head_t wq;
|
||||
|
||||
unsigned qmax;
|
||||
unsigned snaplen;
|
||||
|
||||
struct sk_buff_head logq;
|
||||
|
||||
bool copy_file_header;
|
||||
struct pcap_file_header file_hdr;
|
||||
struct pktdump_hdr hdr;
|
||||
};
|
||||
|
||||
enum {
|
||||
PKTLOG_BOTTOM, /* to/from phy layer */
|
||||
PKTLOG_TOP, /* to/from network/ril layer */
|
||||
PKTLOG_TEXT,
|
||||
};
|
||||
|
||||
#define PKT_TX 0x10
|
||||
#define PKT_RX 0x20
|
||||
#define PKT_IP 0x40
|
||||
|
||||
#define PKT_BOT 0x0
|
||||
#define PKT_TOP 0xF
|
||||
|
||||
|
||||
extern struct pktlog_data *create_pktlog(char *name);
|
||||
extern void remove_pktlog(struct pktlog_data *pktlog);
|
||||
extern void pktlog_queue_skb(struct pktlog_data *pktlog, unsigned char dir,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#define pktlog_tx_bottom_skb(mld, skb) pktlog_queue_skb((mld)->pktlog, (PKT_TX | PKT_BOT) , skb)
|
||||
#define pktlog_tx_top_skb(mld, skb) pktlog_queue_skb((mld)->pktlog, (PKT_TX | PKT_TOP) , skb)
|
||||
#define pktlog_rx_bottom_skb(mld, skb) pktlog_queue_skb((mld)->pktlog, (PKT_RX | PKT_BOT) , skb)
|
||||
#define pktlog_rx_top_skb(mld, skb) pktlog_queue_skb((mld)->pktlog, (PKT_RX | PKT_TOP) , skb)
|
||||
#define pktlog_text_buf(mld, buf) do {} while (0)
|
||||
|
||||
#else /* else of DEBUG_PKTLOG */
|
||||
static inline struct pktlog_data *create_pktlog(char *name) { return NULL; }
|
||||
#define remove_pktlog(pkt) do {} while (0)
|
||||
#define pktlog_tx_bottom_skb(mld, skb) do {} while (0)
|
||||
#define pktlog_tx_top_skb(mld, skb) do {} while (0)
|
||||
#define pktlog_rx_bottom_skb(mld, skb) do {} while (0)
|
||||
#define pktlog_rx_top_skb(mld, skb) do {} while (0)
|
||||
#define pktlog_text_buf(mld, buf) do {} while (0)
|
||||
#endif /* end of DEBUG_PKTLOG */
|
||||
|
||||
#endif
|
||||
742
drivers/misc/modem_v1/modem_prj.h
Normal file
742
drivers/misc/modem_v1/modem_prj.h
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_PRJ_H__
|
||||
#define __MODEM_PRJ_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "modem_debug.h"
|
||||
#include "modem_v1.h"
|
||||
#include "modem_pktlog.h"
|
||||
|
||||
#include "include/circ_queue.h"
|
||||
#include "include/sipc5.h"
|
||||
|
||||
#define DEBUG_MODEM_IF
|
||||
#ifdef DEBUG_MODEM_IF
|
||||
#if 1
|
||||
#define DEBUG_MODEM_IF_LINK_TX
|
||||
#endif
|
||||
#if 1
|
||||
#define DEBUG_MODEM_IF_LINK_RX
|
||||
#endif
|
||||
#if defined(DEBUG_MODEM_IF_LINK_TX) && defined(DEBUG_MODEM_IF_LINK_RX)
|
||||
#define DEBUG_MODEM_IF_LINK_HEADER
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define DEBUG_MODEM_IF_IODEV_TX
|
||||
#endif
|
||||
#if 0
|
||||
#define DEBUG_MODEM_IF_IODEV_RX
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define DEBUG_MODEM_IF_FLOW_CTRL
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define DEBUG_MODEM_IF_PS_DATA
|
||||
#endif
|
||||
#if 0
|
||||
#define DEBUG_MODEM_IF_IP_DATA
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
IOCTL commands
|
||||
*/
|
||||
#define IOCTL_MODEM_ON _IO('o', 0x19)
|
||||
#define IOCTL_MODEM_OFF _IO('o', 0x20)
|
||||
#define IOCTL_MODEM_RESET _IO('o', 0x21)
|
||||
#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
|
||||
#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
|
||||
#define IOCTL_MODEM_BOOT_DONE _IO('o', 0x24)
|
||||
|
||||
#define IOCTL_MODEM_PROTOCOL_SUSPEND _IO('o', 0x25)
|
||||
#define IOCTL_MODEM_PROTOCOL_RESUME _IO('o', 0x26)
|
||||
|
||||
#define IOCTL_MODEM_STATUS _IO('o', 0x27)
|
||||
|
||||
#define IOCTL_MODEM_DL_START _IO('o', 0x28)
|
||||
#define IOCTL_MODEM_FW_UPDATE _IO('o', 0x29)
|
||||
|
||||
#define IOCTL_MODEM_NET_SUSPEND _IO('o', 0x30)
|
||||
#define IOCTL_MODEM_NET_RESUME _IO('o', 0x31)
|
||||
|
||||
#define IOCTL_MODEM_DUMP_START _IO('o', 0x32)
|
||||
#define IOCTL_MODEM_DUMP_UPDATE _IO('o', 0x33)
|
||||
#define IOCTL_MODEM_FORCE_CRASH_EXIT _IO('o', 0x34)
|
||||
#define IOCTL_MODEM_CP_UPLOAD _IO('o', 0x35)
|
||||
|
||||
#define IOCTL_LINK_CONNECTED _IO('o', 0x33)
|
||||
#define IOCTL_MODEM_SET_TX_LINK _IO('o', 0x37)
|
||||
|
||||
#define IOCTL_MODEM_RAMDUMP_START _IO('o', 0xCE)
|
||||
#define IOCTL_MODEM_RAMDUMP_STOP _IO('o', 0xCF)
|
||||
|
||||
#define IOCTL_MODEM_XMIT_BOOT _IO('o', 0x40)
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
#define IOCTL_MODEM_GET_SHMEM_INFO _IO('o', 0x41)
|
||||
#endif
|
||||
#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43)
|
||||
|
||||
#define IOCTL_LINK_DEVICE_RESET _IO('o', 0x44)
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
#define IOCTL_MODEM_GET_SHMEM_SRINFO _IO('o', 0x45)
|
||||
#define IOCTL_MODEM_SET_SHMEM_SRINFO _IO('o', 0x46)
|
||||
#endif
|
||||
/* ioctl command for IPC Logger */
|
||||
#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51)
|
||||
#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52)
|
||||
|
||||
#define IOCTL_SECURITY_REQ _IO('o', 0x53) /* Request smc_call */
|
||||
#define IOCTL_SHMEM_FULL_DUMP _IO('o', 0x54) /* For shmem dump */
|
||||
#define IOCTL_MODEM_CRASH_REASON _IO('o', 0x55) /* Get Crash Reason */
|
||||
#define IOCTL_MODEM_AIRPLANE_MODE _IO('o', 0x56) /* Set Airplane mode on/off */
|
||||
|
||||
/*
|
||||
Definitions for IO devices
|
||||
*/
|
||||
#define MAX_IOD_RXQ_LEN 2048
|
||||
|
||||
#define CP_CRASH_INFO_SIZE 512
|
||||
#define CP_CRASH_TAG "CP Crash "
|
||||
|
||||
#define IPv6 6
|
||||
#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
|
||||
|
||||
/* Loopback */
|
||||
#define CP2AP_LOOPBACK_CHANNEL 30
|
||||
#define DATA_LOOPBACK_CHANNEL 31
|
||||
|
||||
#define DATA_DRAIN_CHANNEL 30 /* Drain channel to drop RX packets */
|
||||
|
||||
/* Debugging features */
|
||||
#define MIF_LOG_DIR "/sdcard/log"
|
||||
#define MIF_MAX_PATH_LEN 256
|
||||
|
||||
/* Does modem ctl structure will use state ? or status defined below ?*/
|
||||
enum modem_state {
|
||||
STATE_OFFLINE,
|
||||
STATE_CRASH_RESET, /* silent reset */
|
||||
STATE_CRASH_EXIT, /* cp ramdump */
|
||||
STATE_BOOTING,
|
||||
STATE_ONLINE,
|
||||
STATE_NV_REBUILDING, /* <= rebuilding start */
|
||||
STATE_LOADER_DONE,
|
||||
STATE_SIM_ATTACH,
|
||||
STATE_SIM_DETACH,
|
||||
STATE_CRASH_WATCHDOG, /* cp watchdog crash */
|
||||
};
|
||||
|
||||
enum link_state {
|
||||
LINK_STATE_OFFLINE = 0,
|
||||
LINK_STATE_IPC,
|
||||
LINK_STATE_CP_CRASH
|
||||
};
|
||||
|
||||
struct sim_state {
|
||||
bool online; /* SIM is online? */
|
||||
bool changed; /* online is changed? */
|
||||
};
|
||||
|
||||
struct modem_firmware {
|
||||
unsigned long long binary;
|
||||
u32 size;
|
||||
u32 m_offset;
|
||||
u32 b_offset;
|
||||
u32 mode;
|
||||
u32 len;
|
||||
} __packed;
|
||||
|
||||
struct modem_sec_req {
|
||||
u32 mode;
|
||||
u32 size_boot;
|
||||
u32 size_main;
|
||||
u32 dummy;
|
||||
} __packed;
|
||||
|
||||
struct modem_mem_info {
|
||||
u32 magic;
|
||||
u32 m_offset;
|
||||
u32 cp_mem_size;
|
||||
u32 shmem_msm_size;
|
||||
} __packed;
|
||||
|
||||
enum cp_boot_mode {
|
||||
CP_BOOT_MODE_NORMAL,
|
||||
CP_BOOT_MODE_DUMP,
|
||||
CP_BOOT_RE_INIT,
|
||||
MAX_CP_BOOT_MODE
|
||||
};
|
||||
|
||||
struct sec_info {
|
||||
enum cp_boot_mode mode;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
enum mem_info {
|
||||
MAGIC_CODE=16,
|
||||
CP_MEM,
|
||||
SHM_MEM
|
||||
};
|
||||
#define SIPC_MULTI_FRAME_MORE_BIT (0b10000000) /* 0x80 */
|
||||
#define SIPC_MULTI_FRAME_ID_MASK (0b01111111) /* 0x7F */
|
||||
#define SIPC_MULTI_FRAME_ID_BITS 7
|
||||
#define NUM_SIPC_MULTI_FRAME_IDS (2 ^ SIPC_MULTI_FRAME_ID_BITS)
|
||||
#define MAX_SIPC_MULTI_FRAME_ID (NUM_SIPC_MULTI_FRAME_IDS - 1)
|
||||
|
||||
struct __packed sipc_fmt_hdr {
|
||||
u16 len;
|
||||
u8 msg_seq;
|
||||
u8 ack_seq;
|
||||
u8 main_cmd;
|
||||
u8 sub_cmd;
|
||||
u8 cmd_type;
|
||||
};
|
||||
|
||||
static inline bool sipc_ps_ch(u8 ch)
|
||||
{
|
||||
return (ch >= SIPC_CH_ID_PDP_0 && ch <= SIPC_CH_ID_PDP_14) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
/* Channel 0, 5, 6, 27, 255 are reserved in SIPC5.
|
||||
* see SIPC5 spec: 2.2.2 Channel Identification (Ch ID) Field.
|
||||
* They do not need to store in `iodevs_tree_fmt'
|
||||
*/
|
||||
#define sipc5_is_not_reserved_channel(ch) \
|
||||
((ch) != 0 && (ch) != 5 && (ch) != 6 && (ch) != 27 && (ch) != 255)
|
||||
|
||||
struct vnet {
|
||||
struct io_device *iod;
|
||||
struct link_device *ld;
|
||||
};
|
||||
|
||||
/* for fragmented data from link devices */
|
||||
struct fragmented_data {
|
||||
struct sk_buff *skb_recv;
|
||||
struct sipc5_frame_data f_data;
|
||||
/* page alloc fail retry*/
|
||||
unsigned int realloc_offset;
|
||||
};
|
||||
#define fragdata(iod, ld) (&(iod)->fragments[(ld)->link_type])
|
||||
|
||||
/** struct skbuff_priv - private data of struct sk_buff
|
||||
* this is matched to char cb[48] of struct sk_buff
|
||||
*/
|
||||
struct skbuff_private {
|
||||
struct io_device *iod;
|
||||
struct link_device *ld;
|
||||
|
||||
/* for time-stamping */
|
||||
struct timespec ts;
|
||||
|
||||
u32 sipc_ch:8, /* SIPC Channel Number */
|
||||
frm_ctrl:8, /* Multi-framing control */
|
||||
reserved:15,
|
||||
lnk_hdr:1; /* Existence of a link-layer header */
|
||||
} __packed;
|
||||
|
||||
static inline struct skbuff_private *skbpriv(struct sk_buff *skb)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct skbuff_private) > sizeof(skb->cb));
|
||||
return (struct skbuff_private *)&skb->cb;
|
||||
}
|
||||
|
||||
enum iod_rx_state {
|
||||
IOD_RX_ON_STANDBY = 0,
|
||||
IOD_RX_HEADER,
|
||||
IOD_RX_PAYLOAD,
|
||||
IOD_RX_PADDING,
|
||||
MAX_IOD_RX_STATE
|
||||
};
|
||||
|
||||
static const char const *rx_state_string[] = {
|
||||
[IOD_RX_ON_STANDBY] = "RX_ON_STANDBY",
|
||||
[IOD_RX_HEADER] = "RX_HEADER",
|
||||
[IOD_RX_PAYLOAD] = "RX_PAYLOAD",
|
||||
[IOD_RX_PADDING] = "RX_PADDING",
|
||||
};
|
||||
|
||||
static const inline char *rx_state(enum iod_rx_state state)
|
||||
{
|
||||
if (unlikely(state >= MAX_IOD_RX_STATE))
|
||||
return "INVALID_STATE";
|
||||
else
|
||||
return rx_state_string[state];
|
||||
}
|
||||
|
||||
struct io_device {
|
||||
struct list_head list;
|
||||
|
||||
/* rb_tree node for an io device */
|
||||
struct rb_node node_chan;
|
||||
struct rb_node node_fmt;
|
||||
|
||||
/* Name of the IO device */
|
||||
char *name;
|
||||
|
||||
/* Reference count */
|
||||
atomic_t opened;
|
||||
|
||||
/* Wait queue for the IO device */
|
||||
wait_queue_head_t wq;
|
||||
|
||||
/* Misc and net device structures for the IO device */
|
||||
struct miscdevice miscdev;
|
||||
struct net_device *ndev;
|
||||
struct list_head node_ndev;
|
||||
struct napi_struct napi;
|
||||
|
||||
/* ID and Format for channel on the link */
|
||||
unsigned int id;
|
||||
enum modem_link link_types;
|
||||
enum dev_format format;
|
||||
enum modem_io io_typ;
|
||||
enum modem_network net_typ;
|
||||
|
||||
/* Attributes of an IO device */
|
||||
u32 attrs;
|
||||
|
||||
/* The name of the application that will use this IO device */
|
||||
char *app;
|
||||
|
||||
/* The size of maximum Tx packet */
|
||||
unsigned int max_tx_size;
|
||||
|
||||
/* Whether or not handover among 2+ link devices */
|
||||
bool use_handover;
|
||||
|
||||
/* SIPC version */
|
||||
enum sipc_ver ipc_version;
|
||||
|
||||
/* Whether or not IPC is over SBD-based link device */
|
||||
bool sbd_ipc;
|
||||
|
||||
/* Whether or not link-layer header is required */
|
||||
bool link_header;
|
||||
|
||||
/* Rx queue of sk_buff */
|
||||
struct sk_buff_head sk_rx_q;
|
||||
|
||||
/* For keeping multi-frame packets temporarily */
|
||||
struct sk_buff_head sk_multi_q[NUM_SIPC_MULTI_FRAME_IDS];
|
||||
|
||||
/* RX state used in RX FSM */
|
||||
enum iod_rx_state curr_rx_state;
|
||||
enum iod_rx_state next_rx_state;
|
||||
|
||||
/*
|
||||
** work for each io device, when delayed work needed
|
||||
** use this for private io device rx action
|
||||
*/
|
||||
struct delayed_work rx_work;
|
||||
|
||||
/* Information ID for supporting 'Multi FMT'
|
||||
* reference SIPC Spec. 2.2.4
|
||||
*/
|
||||
u8 info_id;
|
||||
spinlock_t info_id_lock;
|
||||
|
||||
struct fragmented_data fragments[LINKDEV_MAX];
|
||||
|
||||
int (*recv_skb_single)(struct io_device *iod, struct link_device *ld,
|
||||
struct sk_buff *skb);
|
||||
|
||||
int (*recv_net_skb)(struct io_device *iod, struct link_device *ld,
|
||||
struct sk_buff *skb);
|
||||
|
||||
/* inform the IO device that the modem is now online or offline or
|
||||
* crashing or whatever...
|
||||
*/
|
||||
void (*modem_state_changed)(struct io_device *iod, enum modem_state);
|
||||
|
||||
/* inform the IO device that the SIM is not inserting or removing */
|
||||
void (*sim_state_changed)(struct io_device *iod, bool sim_online);
|
||||
|
||||
struct modem_ctl *mc;
|
||||
struct modem_shared *msd;
|
||||
|
||||
struct wake_lock wakelock;
|
||||
long waketime;
|
||||
|
||||
/* DO NOT use __current_link directly
|
||||
* you MUST use skbpriv(skb)->ld in mc, link, etc..
|
||||
*/
|
||||
struct link_device *__current_link;
|
||||
};
|
||||
#define to_io_device(misc) container_of(misc, struct io_device, miscdev)
|
||||
|
||||
/* get_current_link, set_current_link don't need to use locks.
|
||||
* In ARM, set_current_link and get_current_link are compiled to
|
||||
* each one instruction (str, ldr) as atomic_set, atomic_read.
|
||||
* And, the order of set_current_link and get_current_link is not important.
|
||||
*/
|
||||
#define get_current_link(iod) ((iod)->__current_link)
|
||||
#define set_current_link(iod, ld) ((iod)->__current_link = (ld))
|
||||
|
||||
struct link_device {
|
||||
struct list_head list;
|
||||
enum modem_link link_type;
|
||||
struct modem_ctl *mc;
|
||||
struct modem_shared *msd;
|
||||
|
||||
char *name;
|
||||
bool sbd_ipc;
|
||||
bool aligned;
|
||||
|
||||
/* SIPC version */
|
||||
enum sipc_ver ipc_version;
|
||||
|
||||
/* Modem data */
|
||||
struct modem_data *mdm_data;
|
||||
|
||||
/* TX queue of socket buffers */
|
||||
struct sk_buff_head sk_fmt_tx_q;
|
||||
struct sk_buff_head sk_raw_tx_q;
|
||||
struct sk_buff_head *skb_txq[MAX_SIPC_DEVICES];
|
||||
|
||||
/* RX queue of socket buffers */
|
||||
struct sk_buff_head sk_fmt_rx_q;
|
||||
struct sk_buff_head sk_raw_rx_q;
|
||||
struct sk_buff_head *skb_rxq[MAX_SIPC_DEVICES];
|
||||
|
||||
/* Stop/resume control for network ifaces */
|
||||
spinlock_t netif_lock;
|
||||
|
||||
/* bit mask for stopped channel */
|
||||
unsigned long netif_stop_mask;
|
||||
unsigned long tx_flowctrl_mask;
|
||||
|
||||
struct completion raw_tx_resumed;
|
||||
|
||||
/* flag of stopped state for all channels */
|
||||
atomic_t netif_stopped;
|
||||
|
||||
struct workqueue_struct *rx_wq;
|
||||
struct delayed_work rx_delayed_work;
|
||||
|
||||
int (*init_comm)(struct link_device *ld, struct io_device *iod);
|
||||
void (*terminate_comm)(struct link_device *ld, struct io_device *iod);
|
||||
|
||||
/* called by an io_device when it has a packet to send over link
|
||||
* - the io device is passed so the link device can look at id and
|
||||
* format fields to determine how to route/format the packet
|
||||
*/
|
||||
int (*send)(struct link_device *ld, struct io_device *iod,
|
||||
struct sk_buff *skb);
|
||||
|
||||
/* method for CP booting */
|
||||
void (*boot_on)(struct link_device *ld, struct io_device *iod);
|
||||
int (*xmit_boot)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
int (*dload_start)(struct link_device *ld, struct io_device *iod);
|
||||
int (*firm_update)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
|
||||
/* methods for CP crash dump */
|
||||
int (*shmem_dump)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
int (*force_dump)(struct link_device *ld, struct io_device *iod);
|
||||
int (*dump_start)(struct link_device *ld, struct io_device *iod);
|
||||
int (*dump_update)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
int (*dump_finish)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
|
||||
/* IOCTL extension */
|
||||
int (*ioctl)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
|
||||
/* Close (stop) TX with physical link (on CP crash, etc.) */
|
||||
void (*close_tx)(struct link_device *ld);
|
||||
|
||||
/* Change secure mode, Call SMC API */
|
||||
int (*security_req)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
|
||||
/* Get crash reason form modem_if driver */
|
||||
int (*crash_reason)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
|
||||
/* Set airplane mode for power saving */
|
||||
int (*airplane_mode)(struct link_device *ld, struct io_device *iod,
|
||||
unsigned long arg);
|
||||
};
|
||||
|
||||
#define pm_to_link_device(pm) container_of(pm, struct link_device, pm)
|
||||
|
||||
static inline struct sk_buff *rx_alloc_skb(unsigned int length,
|
||||
struct io_device *iod, struct link_device *ld)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = dev_alloc_skb(length);
|
||||
if (likely(skb)) {
|
||||
skbpriv(skb)->iod = iod;
|
||||
skbpriv(skb)->ld = ld;
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
struct modemctl_ops {
|
||||
int (*modem_on)(struct modem_ctl *);
|
||||
int (*modem_off)(struct modem_ctl *);
|
||||
int (*modem_shutdown)(struct modem_ctl *);
|
||||
int (*modem_reset)(struct modem_ctl *);
|
||||
int (*modem_boot_on)(struct modem_ctl *);
|
||||
int (*modem_boot_off)(struct modem_ctl *);
|
||||
int (*modem_boot_done)(struct modem_ctl *);
|
||||
int (*modem_force_crash_exit)(struct modem_ctl *);
|
||||
int (*modem_dump_start)(struct modem_ctl *);
|
||||
void (*modem_boot_confirm)(struct modem_ctl *);
|
||||
};
|
||||
|
||||
/* for IPC Logger */
|
||||
struct mif_storage {
|
||||
char *addr;
|
||||
unsigned int cnt;
|
||||
};
|
||||
|
||||
/* modem_shared - shared data for all io/link devices and a modem ctl
|
||||
* msd : mc : iod : ld = 1 : 1 : M : N
|
||||
*/
|
||||
struct modem_shared {
|
||||
/* list of link devices */
|
||||
struct list_head link_dev_list;
|
||||
|
||||
/* list of activated ndev */
|
||||
struct list_head activated_ndev_list;
|
||||
|
||||
/* Array of pointers to IO devices corresponding to ch[n] */
|
||||
struct io_device *ch2iod[256];
|
||||
|
||||
/* Array of active channels */
|
||||
u8 ch[256];
|
||||
|
||||
/* The number of active channels in the array @ch[] */
|
||||
unsigned int num_channels;
|
||||
|
||||
/* rb_tree root of io devices. */
|
||||
struct rb_root iodevs_tree_fmt; /* group by dev_format */
|
||||
|
||||
/* for IPC Logger */
|
||||
struct mif_storage storage;
|
||||
spinlock_t lock;
|
||||
|
||||
/* CP crash information */
|
||||
char cp_crash_info[530];
|
||||
|
||||
/* loopbacked IP address
|
||||
* default is 0.0.0.0 (disabled)
|
||||
* after you setted this, you can use IP packet loopback using this IP.
|
||||
* exam: echo 1.2.3.4 > /sys/devices/virtual/misc/umts_multipdp/loopback
|
||||
*/
|
||||
__be32 loopback_ipaddr;
|
||||
};
|
||||
|
||||
struct modem_ctl {
|
||||
struct device *dev;
|
||||
char *name;
|
||||
struct modem_data *mdm_data;
|
||||
|
||||
struct modem_shared *msd;
|
||||
void __iomem *sysram_alive;
|
||||
|
||||
enum modem_state phone_state;
|
||||
struct sim_state sim_state;
|
||||
|
||||
/* spin lock for each modem_ctl instance */
|
||||
spinlock_t lock;
|
||||
|
||||
/* list for notify to opened iod when changed modem state */
|
||||
struct list_head modem_state_notify_list;
|
||||
|
||||
/* completion for waiting for CP initialization */
|
||||
struct completion init_cmpl;
|
||||
|
||||
/* completion for waiting for CP power-off */
|
||||
struct completion off_cmpl;
|
||||
|
||||
unsigned int gpio_cp_on;
|
||||
unsigned int gpio_cp_off;
|
||||
unsigned int gpio_reset_req_n;
|
||||
unsigned int gpio_cp_reset;
|
||||
|
||||
/* for broadcasting AP's PM state (active or sleep) */
|
||||
unsigned int gpio_pda_active;
|
||||
unsigned int int_pda_active;
|
||||
|
||||
/* for checking aliveness of CP */
|
||||
unsigned int gpio_phone_active;
|
||||
unsigned int irq_phone_active;
|
||||
struct modem_irq irq_cp_active;
|
||||
|
||||
/* for AP-CP power management (PM) handshaking */
|
||||
unsigned int gpio_ap_wakeup;
|
||||
unsigned int irq_ap_wakeup;
|
||||
|
||||
unsigned int gpio_ap_status;
|
||||
unsigned int int_ap_status;
|
||||
|
||||
unsigned int gpio_cp_wakeup;
|
||||
unsigned int int_cp_wakeup;
|
||||
|
||||
unsigned int gpio_cp_status;
|
||||
unsigned int irq_cp_status;
|
||||
|
||||
/* for performance tuning */
|
||||
unsigned int gpio_perf_req;
|
||||
unsigned int irq_perf_req;
|
||||
|
||||
/* for USB/HSIC PM */
|
||||
unsigned int gpio_host_wakeup;
|
||||
unsigned int irq_host_wakeup;
|
||||
unsigned int gpio_host_active;
|
||||
unsigned int gpio_slave_wakeup;
|
||||
|
||||
unsigned int gpio_cp_dump_int;
|
||||
unsigned int gpio_ap_dump_int;
|
||||
unsigned int gpio_flm_uart_sel;
|
||||
unsigned int gpio_cp_warm_reset;
|
||||
|
||||
unsigned int gpio_sim_detect;
|
||||
unsigned int irq_sim_detect;
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
unsigned int mbx_pda_active;
|
||||
unsigned int mbx_phone_active;
|
||||
unsigned int mbx_ap_wakeup;
|
||||
unsigned int mbx_ap_status;
|
||||
unsigned int mbx_cp_wakeup;
|
||||
unsigned int mbx_cp_status;
|
||||
unsigned int mbx_perf_req;
|
||||
|
||||
/* for notify uart connection with direction*/
|
||||
unsigned int mbx_uart_noti;
|
||||
unsigned int int_uart_noti;
|
||||
|
||||
/* for checking aliveness of CP */
|
||||
struct modem_irq irq_cp_wdt; /* watchdog timer */
|
||||
struct modem_irq irq_cp_fail;
|
||||
|
||||
/* Status Bit Info */
|
||||
unsigned int sbi_evs_mode_mask;
|
||||
unsigned int sbi_evs_mode_pos;
|
||||
unsigned int sbi_lte_active_mask;
|
||||
unsigned int sbi_lte_active_pos;
|
||||
unsigned int sbi_cp_status_mask;
|
||||
unsigned int sbi_cp_status_pos;
|
||||
|
||||
unsigned int sbi_pda_active_mask;
|
||||
unsigned int sbi_pda_active_pos;
|
||||
unsigned int sbi_ap_status_mask;
|
||||
unsigned int sbi_ap_status_pos;
|
||||
|
||||
unsigned int sbi_uart_noti_mask;
|
||||
unsigned int sbi_uart_noti_pos;
|
||||
|
||||
unsigned int airplane_mode;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXYNOS_BUSMONITOR
|
||||
struct notifier_block busmon_nfb;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MUIC_NOTIFIER)
|
||||
struct notifier_block uart_notifier;
|
||||
#endif
|
||||
bool uart_connect;
|
||||
bool uart_dir;
|
||||
|
||||
struct work_struct pm_qos_work;
|
||||
|
||||
/* Switch with 2 links in a modem */
|
||||
unsigned int gpio_link_switch;
|
||||
|
||||
const struct attribute_group *group;
|
||||
|
||||
struct delayed_work dwork;
|
||||
struct work_struct work;
|
||||
|
||||
struct modemctl_ops ops;
|
||||
struct io_device *iod;
|
||||
struct io_device *bootd;
|
||||
|
||||
void (*gpio_revers_bias_clear)(void);
|
||||
void (*gpio_revers_bias_restore)(void);
|
||||
void (*modem_complete)(struct modem_ctl *mc);
|
||||
};
|
||||
|
||||
static inline bool cp_offline(struct modem_ctl *mc)
|
||||
{
|
||||
if (!mc)
|
||||
return true;
|
||||
return (mc->phone_state == STATE_OFFLINE);
|
||||
}
|
||||
|
||||
static inline bool cp_online(struct modem_ctl *mc)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return (mc->phone_state == STATE_ONLINE);
|
||||
}
|
||||
|
||||
static inline bool cp_booting(struct modem_ctl *mc)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return (mc->phone_state == STATE_BOOTING);
|
||||
}
|
||||
|
||||
static inline bool cp_crashed(struct modem_ctl *mc)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return (mc->phone_state == STATE_CRASH_EXIT
|
||||
|| mc->phone_state == STATE_CRASH_WATCHDOG);
|
||||
}
|
||||
|
||||
static inline bool rx_possible(struct modem_ctl *mc)
|
||||
{
|
||||
if (likely(cp_online(mc)))
|
||||
return true;
|
||||
|
||||
if (cp_booting(mc) || cp_crashed(mc))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int sipc5_init_io_device(struct io_device *iod);
|
||||
|
||||
#endif
|
||||
1359
drivers/misc/modem_v1/modem_utils.c
Normal file
1359
drivers/misc/modem_v1/modem_utils.c
Normal file
File diff suppressed because it is too large
Load diff
515
drivers/misc/modem_v1/modem_utils.h
Normal file
515
drivers/misc/modem_v1/modem_utils.h
Normal file
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_UTILS_H__
|
||||
#define __MODEM_UTILS_H__
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include "modem_prj.h"
|
||||
|
||||
#define MIF_TAG "mif"
|
||||
|
||||
#define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type))
|
||||
|
||||
#define MAX_MIF_BUFF_SIZE 0x80000 /* 512kb */
|
||||
#define MAX_MIF_SEPA_SIZE 32
|
||||
#define MIF_SEPARATOR "IPC_LOGGER(VER1.1)"
|
||||
#define MIF_SEPARATOR_DPRAM "DPRAM_LOGGER(VER1.1)"
|
||||
#define MAX_IPC_SKB_SIZE 4096
|
||||
#define MAX_LOG_SIZE 64
|
||||
|
||||
#define MIF_TAG "mif"
|
||||
|
||||
#define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE)
|
||||
#define MIF_ID_SIZE sizeof(enum mif_log_id)
|
||||
|
||||
#define MAX_IPC_LOG_SIZE \
|
||||
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
|
||||
- sizeof(unsigned long long) - sizeof(size_t))
|
||||
#define MAX_IRQ_LOG_SIZE \
|
||||
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
|
||||
- sizeof(unsigned long long) - sizeof(struct mif_irq_map))
|
||||
#define MAX_COM_LOG_SIZE \
|
||||
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
|
||||
- sizeof(unsigned long long))
|
||||
#define MAX_TIM_LOG_SIZE \
|
||||
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
|
||||
- sizeof(unsigned long long) - sizeof(struct timespec))
|
||||
|
||||
enum mif_log_id {
|
||||
MIF_IPC_RL2AP = 1,
|
||||
MIF_IPC_AP2CP,
|
||||
MIF_IPC_CP2AP,
|
||||
MIF_IPC_AP2RL,
|
||||
MIF_IRQ,
|
||||
MIF_COM,
|
||||
MIF_TIME
|
||||
};
|
||||
|
||||
struct mif_irq_map {
|
||||
u16 magic;
|
||||
u16 access;
|
||||
|
||||
u16 fmt_tx_in;
|
||||
u16 fmt_tx_out;
|
||||
u16 fmt_rx_in;
|
||||
u16 fmt_rx_out;
|
||||
|
||||
u16 raw_tx_in;
|
||||
u16 raw_tx_out;
|
||||
u16 raw_rx_in;
|
||||
u16 raw_rx_out;
|
||||
|
||||
u16 cp2ap;
|
||||
};
|
||||
|
||||
struct mif_ipc_block {
|
||||
enum mif_log_id id;
|
||||
unsigned long long time;
|
||||
size_t len;
|
||||
char buff[MAX_IPC_LOG_SIZE];
|
||||
};
|
||||
|
||||
struct mif_irq_block {
|
||||
enum mif_log_id id;
|
||||
unsigned long long time;
|
||||
struct mif_irq_map map;
|
||||
char buff[MAX_IRQ_LOG_SIZE];
|
||||
};
|
||||
|
||||
struct mif_common_block {
|
||||
enum mif_log_id id;
|
||||
unsigned long long time;
|
||||
char buff[MAX_COM_LOG_SIZE];
|
||||
};
|
||||
|
||||
struct mif_time_block {
|
||||
enum mif_log_id id;
|
||||
unsigned long long time;
|
||||
struct timespec epoch;
|
||||
char buff[MAX_TIM_LOG_SIZE];
|
||||
};
|
||||
|
||||
enum ipc_layer {
|
||||
LINK,
|
||||
IODEV,
|
||||
APP,
|
||||
MAX_SIPC_LAYER
|
||||
};
|
||||
|
||||
static const char const *sipc_layer_string[] = {
|
||||
[LINK] = "LNK",
|
||||
[IODEV] = "IOD",
|
||||
[APP] = "APP",
|
||||
[MAX_SIPC_LAYER] = "INVALID"
|
||||
};
|
||||
|
||||
static const inline char *layer_str(enum ipc_layer layer)
|
||||
{
|
||||
if (unlikely(layer >= MAX_SIPC_LAYER))
|
||||
return "INVALID";
|
||||
else
|
||||
return sipc_layer_string[layer];
|
||||
}
|
||||
|
||||
static const char const *dev_format_string[] = {
|
||||
[IPC_FMT] = "FMT",
|
||||
[IPC_RAW] = "RAW",
|
||||
[IPC_RFS] = "RFS",
|
||||
[IPC_MULTI_RAW] = "MULTI_RAW",
|
||||
[IPC_BOOT] = "BOOT",
|
||||
[IPC_DUMP] = "DUMP",
|
||||
[IPC_CMD] = "CMD",
|
||||
[IPC_DEBUG] = "DEBUG",
|
||||
};
|
||||
|
||||
static const inline char *dev_str(enum dev_format dev)
|
||||
{
|
||||
if (unlikely(dev >= MAX_DEV_FORMAT))
|
||||
return "INVALID";
|
||||
else
|
||||
return dev_format_string[dev];
|
||||
}
|
||||
|
||||
static inline enum direction opposite(enum direction dir)
|
||||
{
|
||||
return (dir == TX) ? RX : TX;
|
||||
}
|
||||
|
||||
static const char const *direction_string[] = {
|
||||
[TX] = "TX",
|
||||
[RX] = "RX"
|
||||
};
|
||||
|
||||
static const inline char *dir_str(enum direction dir)
|
||||
{
|
||||
if (unlikely(dir >= MAX_DIR))
|
||||
return "INVALID";
|
||||
else
|
||||
return direction_string[dir];
|
||||
}
|
||||
|
||||
static const char const *udl_string[] = {
|
||||
[UL] = "UL",
|
||||
[DL] = "DL"
|
||||
};
|
||||
|
||||
static const inline char *udl_str(enum direction dir)
|
||||
{
|
||||
if (unlikely(dir >= ULDL))
|
||||
return "INVALID";
|
||||
else
|
||||
return udl_string[dir];
|
||||
}
|
||||
|
||||
static const char const *q_direction_string[] = {
|
||||
[TX] = "TXQ",
|
||||
[RX] = "RXQ"
|
||||
};
|
||||
|
||||
static const inline char *q_dir(enum direction dir)
|
||||
{
|
||||
if (unlikely(dir >= MAX_DIR))
|
||||
return "INVALID";
|
||||
else
|
||||
return q_direction_string[dir];
|
||||
}
|
||||
|
||||
static const char const *ipc_direction_string[] = {
|
||||
[TX] = "AP->CP",
|
||||
[RX] = "AP<-CP"
|
||||
};
|
||||
|
||||
static const inline char *ipc_dir(enum direction dir)
|
||||
{
|
||||
if (unlikely(dir >= MAX_DIR))
|
||||
return "INVALID";
|
||||
else
|
||||
return ipc_direction_string[dir];
|
||||
}
|
||||
|
||||
static const char const *arrow_direction[] = {
|
||||
[TX] = "->",
|
||||
[RX] = "<-"
|
||||
};
|
||||
|
||||
static const inline char *arrow(enum direction dir)
|
||||
{
|
||||
if (unlikely(dir >= MAX_DIR))
|
||||
return "><";
|
||||
else
|
||||
return arrow_direction[dir];
|
||||
}
|
||||
|
||||
static const char const *modem_state_string[] = {
|
||||
[STATE_OFFLINE] = "OFFLINE",
|
||||
[STATE_CRASH_RESET] = "CRASH_RESET",
|
||||
[STATE_CRASH_EXIT] = "CRASH_EXIT",
|
||||
[STATE_BOOTING] = "BOOTING",
|
||||
[STATE_ONLINE] = "ONLINE",
|
||||
[STATE_NV_REBUILDING] = "NV_REBUILDING",
|
||||
[STATE_LOADER_DONE] = "LOADER_DONE",
|
||||
[STATE_SIM_ATTACH] = "SIM_ATTACH",
|
||||
[STATE_SIM_DETACH] = "SIM_DETACH",
|
||||
[STATE_CRASH_WATCHDOG] = "WDT_RESET",
|
||||
};
|
||||
|
||||
static const inline char *cp_state_str(enum modem_state state)
|
||||
{
|
||||
return modem_state_string[state];
|
||||
}
|
||||
|
||||
static const inline char *mc_state(struct modem_ctl *mc)
|
||||
{
|
||||
return cp_state_str(mc->phone_state);
|
||||
}
|
||||
|
||||
struct __packed utc_time {
|
||||
u32 year:18,
|
||||
mon:4,
|
||||
day:5,
|
||||
hour:5;
|
||||
u32 min:6,
|
||||
sec:6,
|
||||
us:20;
|
||||
};
|
||||
|
||||
/* {Hour, Minute, Second, U(micro)-second} format */
|
||||
#define HMSU_FMT "[%02d:%02d:%02d.%06d]"
|
||||
|
||||
static inline unsigned long ns2us(unsigned long ns)
|
||||
{
|
||||
return (ns > 0) ? (ns / 1000) : 0;
|
||||
}
|
||||
|
||||
static inline unsigned long ns2ms(unsigned long ns)
|
||||
{
|
||||
return (ns > 0) ? (ns / 1000000) : 0;
|
||||
}
|
||||
|
||||
static inline unsigned long us2ms(unsigned long us)
|
||||
{
|
||||
return (us > 0) ? (us / 1000) : 0;
|
||||
}
|
||||
|
||||
static inline unsigned long ms2us(unsigned long ms)
|
||||
{
|
||||
return ms * 1E3L;
|
||||
}
|
||||
|
||||
static inline unsigned long ms2ns(unsigned long ms)
|
||||
{
|
||||
return ms * 1E6L;
|
||||
}
|
||||
|
||||
void get_utc_time(struct utc_time *utc);
|
||||
|
||||
static inline unsigned int calc_offset(void *target, void *base)
|
||||
{
|
||||
return (unsigned long)target - (unsigned long)base;
|
||||
}
|
||||
|
||||
int mif_dump_log(struct modem_shared *, struct io_device *);
|
||||
|
||||
#define mif_irq_log(msd, map, data, len) \
|
||||
_mif_irq_log(MIF_IRQ, msd, map, data, len)
|
||||
#define mif_com_log(msd, format, ...) \
|
||||
_mif_com_log(MIF_COM, msd, pr_fmt(format), ##__VA_ARGS__)
|
||||
#define mif_time_log(msd, epoch, data, len) \
|
||||
_mif_time_log(MIF_TIME, msd, epoch, data, len)
|
||||
|
||||
void mif_ipc_log(enum mif_log_id,
|
||||
struct modem_shared *, const char *, size_t);
|
||||
void _mif_irq_log(enum mif_log_id,
|
||||
struct modem_shared *, struct mif_irq_map, const char *, size_t);
|
||||
void _mif_com_log(enum mif_log_id,
|
||||
struct modem_shared *, const char *, ...);
|
||||
void _mif_time_log(enum mif_log_id,
|
||||
struct modem_shared *, struct timespec, const char *, size_t);
|
||||
|
||||
static inline struct link_device *find_linkdev(struct modem_shared *msd,
|
||||
enum modem_link link_type)
|
||||
{
|
||||
struct link_device *ld;
|
||||
list_for_each_entry(ld, &msd->link_dev_list, list) {
|
||||
if (ld->link_type == link_type)
|
||||
return ld;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline unsigned int count_bits(unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; n != 0; i++)
|
||||
n &= (n - 1);
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline bool count_flood(int cnt, int mask)
|
||||
{
|
||||
return (cnt > 0 && (cnt & mask) == 0) ? true : false;
|
||||
}
|
||||
|
||||
void log_ipc_pkt(u8 ch, enum ipc_layer layer, enum direction dir,
|
||||
struct sk_buff *skb, u8 *hdr);
|
||||
|
||||
/* print buffer as hex string */
|
||||
int pr_buffer(const char *tag, const char *data, size_t data_len,
|
||||
size_t max_len);
|
||||
|
||||
/* print a sk_buff as hex string */
|
||||
#define pr_skb(tag, skb) \
|
||||
pr_buffer(tag, (char *)((skb)->data), (size_t)((skb)->len), (size_t)16)
|
||||
|
||||
/* print a urb as hex string */
|
||||
#define pr_urb(tag, urb) \
|
||||
pr_buffer(tag, (char *)((urb)->transfer_buffer), \
|
||||
(size_t)((urb)->actual_length), (size_t)16)
|
||||
|
||||
/* Stop/wake all TX queues in network interfaces */
|
||||
void stop_net_iface(struct link_device *ld, unsigned int channel);
|
||||
void resume_net_iface(struct link_device *ld, unsigned int channel);
|
||||
void stop_net_ifaces(struct link_device *ld);
|
||||
void resume_net_ifaces(struct link_device *ld);
|
||||
|
||||
/* flow control CMD from CP, it use in serial devices */
|
||||
int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len);
|
||||
|
||||
/* Get an IO device */
|
||||
struct io_device *get_iod_with_format(struct modem_shared *msd,
|
||||
enum dev_format format);
|
||||
|
||||
static inline struct io_device *link_get_iod_with_format(
|
||||
struct link_device *ld, enum dev_format format)
|
||||
{
|
||||
struct io_device *iod = get_iod_with_format(ld->msd, format);
|
||||
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
|
||||
}
|
||||
|
||||
static inline struct io_device *get_iod_with_channel(
|
||||
struct modem_shared *msd, unsigned int channel)
|
||||
{
|
||||
return msd->ch2iod[channel];
|
||||
}
|
||||
|
||||
static inline struct io_device *link_get_iod_with_channel(
|
||||
struct link_device *ld, unsigned int channel)
|
||||
{
|
||||
struct io_device *iod = get_iod_with_channel(ld->msd, channel);
|
||||
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
|
||||
}
|
||||
|
||||
/* insert iod to tree functions */
|
||||
struct io_device *insert_iod_with_format(struct modem_shared *msd,
|
||||
enum dev_format format, struct io_device *iod);
|
||||
void insert_iod_with_channel(struct modem_shared *msd, unsigned int channel,
|
||||
struct io_device *iod);
|
||||
|
||||
/* iodev for each */
|
||||
typedef void (*action_fn)(struct io_device *iod, void *args);
|
||||
void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args);
|
||||
|
||||
/* netif wake/stop queue of iod */
|
||||
void iodev_netif_wake(struct io_device *iod, void *args);
|
||||
void iodev_netif_stop(struct io_device *iod, void *args);
|
||||
|
||||
/* netif wake/stop queue of iod having activated ndev */
|
||||
void netif_tx_flowctl(struct modem_shared *msd, bool tx_stop);
|
||||
|
||||
/* change tx_link of raw devices */
|
||||
void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type);
|
||||
|
||||
__be32 ipv4str_to_be32(const char *ipv4str, size_t count);
|
||||
|
||||
void mif_add_timer(struct timer_list *timer, unsigned long expire,
|
||||
void (*function)(unsigned long), unsigned long data);
|
||||
|
||||
/* debug helper functions for sipc4, sipc5 */
|
||||
void mif_print_data(const u8 *data, int len);
|
||||
|
||||
void mif_dump2format16(const u8 *data, int len, char *buff, char *tag);
|
||||
void mif_dump2format4(const u8 *data, int len, char *buff, char *tag);
|
||||
void mif_print_dump(const u8 *data, int len, int width);
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
|
||||
IPv4 Header Format
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Version| IHL |Type of Service| Total Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Identification |C|D|M| Fragment Offset |
|
||||
| |E|F|F| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Time to Live | Protocol | Header Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Destination Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Options | Padding |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
IHL - Header Length
|
||||
Flags - Consist of 3 bits
|
||||
The 1st bit is "Congestion" bit.
|
||||
The 2nd bit is "Dont Fragment" bit.
|
||||
The 3rd bit is "More Fragments" bit.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#define IPV4_HDR_SIZE 20
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
||||
TCP Header Format
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Port | Destination Port |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Sequence Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Acknowledgment Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Data | |C|E|U|A|P|R|S|F| |
|
||||
| Offset| Rsvd |W|C|R|C|S|S|Y|I| Window |
|
||||
| | |R|E|G|K|H|T|N|N| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Checksum | Urgent Pointer |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Options | Padding |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| data |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
-------------------------------------------------------------------------*/
|
||||
#define TCP_HDR_SIZE 20
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
||||
UDP Header Format
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Port | Destination Port |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| data |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
-------------------------------------------------------------------------*/
|
||||
#define UDP_HDR_SIZE 8
|
||||
|
||||
void print_ipv4_packet(const u8 *ip_pkt, enum direction dir);
|
||||
bool is_dns_packet(const u8 *ip_pkt);
|
||||
bool is_syn_packet(const u8 *ip_pkt);
|
||||
|
||||
void mif_init_irq(struct modem_irq *irq, unsigned int num, const char *name,
|
||||
unsigned long flags);
|
||||
int mif_request_irq(struct modem_irq *irq, irq_handler_t isr, void *data);
|
||||
void mif_enable_irq(struct modem_irq *irq);
|
||||
void mif_disable_irq(struct modem_irq *irq);
|
||||
|
||||
struct file *mif_open_file(const char *path);
|
||||
void mif_save_file(struct file *fp, const char *buff, size_t size);
|
||||
void mif_close_file(struct file *fp);
|
||||
|
||||
int board_gpio_export(struct device *dev,
|
||||
unsigned gpio, bool dir, const char *name);
|
||||
|
||||
void make_gpio_floating(unsigned int gpio, bool floating);
|
||||
|
||||
#ifdef CONFIG_ARGOS
|
||||
/* kernel team needs to provide argos header file. !!!
|
||||
* As of now, there's nothing to use. */
|
||||
#ifdef CONFIG_SCHED_HMP
|
||||
extern struct cpumask hmp_slow_cpu_mask;
|
||||
extern struct cpumask hmp_fast_cpu_mask;
|
||||
#endif
|
||||
|
||||
int argos_irq_affinity_setup_label(unsigned int irq, const char *label,
|
||||
struct cpumask *affinity_cpu_mask,
|
||||
struct cpumask *default_cpu_mask);
|
||||
int argos_task_affinity_setup_label(struct task_struct *p, const char *label,
|
||||
struct cpumask *affinity_cpu_mask,
|
||||
struct cpumask *default_cpu_mask);
|
||||
#endif
|
||||
|
||||
void mif_set_snapshot(bool enable);
|
||||
#endif/*__MODEM_UTILS_H__*/
|
||||
|
||||
561
drivers/misc/modem_v1/modem_v1.h
Normal file
561
drivers/misc/modem_v1/modem_v1.h
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_V1_H__
|
||||
#define __MODEM_V1_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
#include <linux/shm_ipc.h>
|
||||
#endif
|
||||
|
||||
#define MAX_STR_LEN 256
|
||||
#define MAX_NAME_LEN 64
|
||||
#define MAX_DUMP_LEN 20
|
||||
|
||||
#define SMC_ID_CLK 0x82001011
|
||||
#define SSS_CLK_ENABLE 0
|
||||
#define SSS_CLK_DISABLE 1
|
||||
|
||||
enum modem_t {
|
||||
IMC_XMM6260,
|
||||
IMC_XMM6262,
|
||||
VIA_CBP71,
|
||||
VIA_CBP72,
|
||||
VIA_CBP82,
|
||||
SEC_CMC220,
|
||||
SEC_CMC221,
|
||||
SEC_SS222,
|
||||
SEC_SS300,
|
||||
SEC_SH222AP,
|
||||
SEC_SS310AP,
|
||||
QC_MDM6600,
|
||||
QC_ESC6270,
|
||||
QC_QSC6085,
|
||||
SPRD_SC8803,
|
||||
IMC_XMM7260,
|
||||
DUMMY,
|
||||
MAX_MODEM_TYPE
|
||||
};
|
||||
|
||||
enum dev_format {
|
||||
IPC_FMT,
|
||||
IPC_RAW,
|
||||
IPC_RFS,
|
||||
IPC_MULTI_RAW,
|
||||
IPC_BOOT,
|
||||
IPC_DUMP,
|
||||
IPC_CMD,
|
||||
IPC_DEBUG,
|
||||
MAX_DEV_FORMAT,
|
||||
};
|
||||
|
||||
#define MAX_SIPC_DEVICES (IPC_RFS + 1) /* FMT, RAW, RFS */
|
||||
#define MAX_SIPC5_DEVICES (IPC_RAW + 1) /* FMT, RAW */
|
||||
|
||||
#define MAX_SIPC_CHANNELS 256 /* 2 ^ 8 */
|
||||
#define MAX_LINK_CHANNELS 32 /* up to 32 channels */
|
||||
|
||||
enum modem_io {
|
||||
IODEV_MISC,
|
||||
IODEV_NET,
|
||||
IODEV_DUMMY,
|
||||
};
|
||||
|
||||
/* Caution!!
|
||||
* Please make sure that below sequence is exactly matched with cbd.
|
||||
* If you want one more link type, append it to the end of list.
|
||||
*/
|
||||
enum modem_link {
|
||||
LINKDEV_UNDEFINED,
|
||||
LINKDEV_MIPI,
|
||||
LINKDEV_DPRAM,
|
||||
LINKDEV_SPI,
|
||||
LINKDEV_USB,
|
||||
LINKDEV_HSIC,
|
||||
LINKDEV_C2C,
|
||||
LINKDEV_UART,
|
||||
LINKDEV_PLD,
|
||||
LINKDEV_SHMEM,
|
||||
LINKDEV_LLI,
|
||||
LINKDEV_MAX
|
||||
};
|
||||
#define LINKTYPE(modem_link) (1u << (modem_link))
|
||||
|
||||
enum modem_network {
|
||||
UMTS_NETWORK,
|
||||
CDMA_NETWORK,
|
||||
TDSCDMA_NETWORK,
|
||||
LTE_NETWORK,
|
||||
MAX_MODEM_NETWORK
|
||||
};
|
||||
|
||||
enum sipc_ver {
|
||||
NO_SIPC_VER = 0,
|
||||
SIPC_VER_40 = 40,
|
||||
SIPC_VER_41 = 41,
|
||||
SIPC_VER_42 = 42,
|
||||
SIPC_VER_50 = 50,
|
||||
MAX_SIPC_VER
|
||||
};
|
||||
|
||||
enum sipc_ch_id {
|
||||
SIPC_CH_ID_RAW_0 = 0, /*reserved*/
|
||||
SIPC_CH_ID_CS_VT_DATA,
|
||||
SIPC_CH_ID_CS_VT_CONTROL,
|
||||
SIPC_CH_ID_CS_VT_AUDIO,
|
||||
SIPC_CH_ID_CS_VT_VIDEO,
|
||||
SIPC_CH_ID_RAW_5, /*reserved*/
|
||||
SIPC_CH_ID_RAW_6, /*reserved*/
|
||||
SIPC_CH_ID_CDMA_DATA,
|
||||
SIPC_CH_ID_PCM_DATA,
|
||||
SIPC_CH_ID_TRANSFER_SCREEN,
|
||||
|
||||
SIPC_CH_ID_PDP_0 = 10, /*ID:10*/
|
||||
SIPC_CH_ID_PDP_1,
|
||||
SIPC_CH_ID_PDP_2,
|
||||
SIPC_CH_ID_PDP_3,
|
||||
SIPC_CH_ID_PDP_4,
|
||||
SIPC_CH_ID_PDP_5,
|
||||
SIPC_CH_ID_PDP_6,
|
||||
SIPC_CH_ID_PDP_7,
|
||||
SIPC_CH_ID_PDP_8,
|
||||
SIPC_CH_ID_PDP_9,
|
||||
SIPC_CH_ID_PDP_10,
|
||||
SIPC_CH_ID_PDP_11,
|
||||
SIPC_CH_ID_PDP_12,
|
||||
SIPC_CH_ID_PDP_13,
|
||||
SIPC_CH_ID_PDP_14,
|
||||
SIPC_CH_ID_BT_DUN, /*ID:25*/
|
||||
SIPC_CH_ID_CIQ_DATA,
|
||||
SIPC_CH_ID_PDP_17, /*reserved*/
|
||||
SIPC_CH_ID_CPLOG1, /*ID:28*/
|
||||
SIPC_CH_ID_CPLOG2, /*ID:29*/
|
||||
SIPC_CH_ID_LOOPBACK1, /*ID:30*/
|
||||
SIPC_CH_ID_LOOPBACK2, /*ID:31*/
|
||||
|
||||
/* 32 ~ 214 are reserved */
|
||||
|
||||
SIPC5_CH_ID_BOOT_0 = 215,
|
||||
SIPC5_CH_ID_BOOT_1,
|
||||
SIPC5_CH_ID_BOOT_2,
|
||||
SIPC5_CH_ID_BOOT_3,
|
||||
SIPC5_CH_ID_BOOT_4,
|
||||
SIPC5_CH_ID_BOOT_5,
|
||||
SIPC5_CH_ID_BOOT_6,
|
||||
SIPC5_CH_ID_BOOT_7,
|
||||
SIPC5_CH_ID_BOOT_8,
|
||||
SIPC5_CH_ID_BOOT_9,
|
||||
|
||||
SIPC5_CH_ID_DUMP_0 = 225,
|
||||
SIPC5_CH_ID_DUMP_1,
|
||||
SIPC5_CH_ID_DUMP_2,
|
||||
SIPC5_CH_ID_DUMP_3,
|
||||
SIPC5_CH_ID_DUMP_4,
|
||||
SIPC5_CH_ID_DUMP_5,
|
||||
SIPC5_CH_ID_DUMP_6,
|
||||
SIPC5_CH_ID_DUMP_7,
|
||||
SIPC5_CH_ID_DUMP_8,
|
||||
SIPC5_CH_ID_DUMP_9,
|
||||
|
||||
SIPC5_CH_ID_FMT_0 = 235,
|
||||
SIPC5_CH_ID_FMT_1,
|
||||
SIPC5_CH_ID_FMT_2,
|
||||
SIPC5_CH_ID_FMT_3,
|
||||
SIPC5_CH_ID_FMT_4,
|
||||
SIPC5_CH_ID_FMT_5,
|
||||
SIPC5_CH_ID_FMT_6,
|
||||
SIPC5_CH_ID_FMT_7,
|
||||
SIPC5_CH_ID_FMT_8,
|
||||
SIPC5_CH_ID_FMT_9,
|
||||
|
||||
SIPC5_CH_ID_RFS_0 = 245,
|
||||
SIPC5_CH_ID_RFS_1,
|
||||
SIPC5_CH_ID_RFS_2,
|
||||
SIPC5_CH_ID_RFS_3,
|
||||
SIPC5_CH_ID_RFS_4,
|
||||
SIPC5_CH_ID_RFS_5,
|
||||
SIPC5_CH_ID_RFS_6,
|
||||
SIPC5_CH_ID_RFS_7,
|
||||
SIPC5_CH_ID_RFS_8,
|
||||
SIPC5_CH_ID_RFS_9,
|
||||
|
||||
SIPC5_CH_ID_MAX = 255
|
||||
};
|
||||
|
||||
struct __packed multi_frame_control {
|
||||
u8 id:7,
|
||||
more:1;
|
||||
};
|
||||
|
||||
enum io_mode {
|
||||
PIO,
|
||||
DMA
|
||||
};
|
||||
|
||||
enum direction {
|
||||
TX = 0,
|
||||
UL = 0,
|
||||
AP2CP = 0,
|
||||
RX = 1,
|
||||
DL = 1,
|
||||
CP2AP = 1,
|
||||
TXRX = 2,
|
||||
ULDL = 2,
|
||||
MAX_DIR = 2
|
||||
};
|
||||
|
||||
enum read_write {
|
||||
RD = 0,
|
||||
WR = 1,
|
||||
RDWR = 2
|
||||
};
|
||||
|
||||
#define STR_CP_FAIL "cp_fail"
|
||||
#define STR_CP_WDT "cp_wdt" /* CP watchdog timer */
|
||||
|
||||
/* You can define modem specific attribute here.
|
||||
* It could be all the different behaviour between many modem vendor.
|
||||
*/
|
||||
enum link_attr_bit {
|
||||
LINK_ATTR_SBD_IPC, /* IPC over SBD (from MIPI-LLI) */
|
||||
LINK_ATTR_IPC_ALIGNED, /* IPC with 4-bytes alignment */
|
||||
LINK_ATTR_IOSM_MESSAGE, /* IOSM message */
|
||||
LINK_ATTR_DPRAM_MAGIC, /* DPRAM-style magic code validation */
|
||||
/*--------------------------------------------------------------*/
|
||||
LINK_ATTR_SBD_BOOT, /* BOOT over SBD */
|
||||
LINK_ATTR_SBD_DUMP, /* DUMP over SBD */
|
||||
LINK_ATTR_MEM_BOOT, /* BOOT over legacy memory-type I/F */
|
||||
LINK_ATTR_MEM_DUMP, /* DUMP over legacy memory-type I/F */
|
||||
LINK_ATTR_BOOT_ALIGNED, /* BOOT with 4-bytes alignment */
|
||||
LINK_ATTR_DUMP_ALIGNED, /* DUMP with 4-bytes alignment */
|
||||
/*--------------------------------------------------------------*/
|
||||
LINK_ATTR_XMIT_BTDLR, /* Used to download CP bootloader */
|
||||
};
|
||||
#define LINK_ATTR(b) (0x1 << b)
|
||||
|
||||
enum iodev_attr_bit {
|
||||
ATTR_SIPC4,
|
||||
ATTR_SIPC5,
|
||||
ATTR_CDC_NCM,
|
||||
ATTR_MULTIFMT,
|
||||
ATTR_HANDOVER,
|
||||
ATTR_LEGACY_RFS,
|
||||
ATTR_RX_FRAGMENT,
|
||||
ATTR_SBD_IPC, /* IPC using SBD designed from MIPI-LLI */
|
||||
ATTR_NO_LINK_HEADER, /* Link-layer header is not needed */
|
||||
ATTR_NO_CHECK_MAXQ, /* no need to check rxq overflow condition */
|
||||
ATTR_DUALSIM, /* support Dual SIM */
|
||||
};
|
||||
#define IODEV_ATTR(b) (0x1 << b)
|
||||
|
||||
/**
|
||||
* struct modem_io_t - declaration for io_device
|
||||
* @name: device name
|
||||
* @id: for SIPC4, contains format & channel information
|
||||
* (id & 11100000b)>>5 = format (eg, 0=FMT, 1=RAW, 2=RFS)
|
||||
* (id & 00011111b) = channel (valid only if format is RAW)
|
||||
* for SIPC5, contains only 8-bit channel ID
|
||||
* @format: device format
|
||||
* @io_type: type of this io_device
|
||||
* @links: list of link_devices to use this io_device
|
||||
* for example, if you want to use DPRAM and USB in an io_device.
|
||||
* .links = LINKTYPE(LINKDEV_DPRAM) | LINKTYPE(LINKDEV_USB)
|
||||
* @tx_link: when you use 2+ link_devices, set the link for TX.
|
||||
* If define multiple link_devices in @links,
|
||||
* you can receive data from them. But, cannot send data to all.
|
||||
* TX is only one link_device.
|
||||
* @app: the name of the application that will use this IO device
|
||||
*
|
||||
*/
|
||||
struct modem_io_t {
|
||||
char *name;
|
||||
u32 id;
|
||||
enum dev_format format;
|
||||
enum modem_io io_type;
|
||||
u32 links;
|
||||
enum modem_link tx_link;
|
||||
u32 attrs;
|
||||
char *app;
|
||||
#if 1/*defined(CONFIG_LINK_DEVICE_MEMORY_SBD)*/
|
||||
unsigned int ul_num_buffers;
|
||||
unsigned int ul_buffer_size;
|
||||
unsigned int dl_num_buffers;
|
||||
unsigned int dl_buffer_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct modemlink_pm_data {
|
||||
char *name;
|
||||
struct device *dev;
|
||||
/* link power contol 2 types : pin & regulator control */
|
||||
int (*link_ldo_enable)(bool);
|
||||
unsigned int gpio_link_enable;
|
||||
unsigned int gpio_link_active;
|
||||
unsigned int gpio_link_hostwake;
|
||||
unsigned int gpio_link_slavewake;
|
||||
int (*link_reconnect)(void);
|
||||
|
||||
/* usb hub only */
|
||||
int (*port_enable)(int, int);
|
||||
int (*hub_standby)(void *);
|
||||
void *hub_pm_data;
|
||||
bool has_usbhub;
|
||||
|
||||
/* cpu/bus frequency lock */
|
||||
atomic_t freqlock;
|
||||
int (*freq_lock)(struct device *dev);
|
||||
int (*freq_unlock)(struct device *dev);
|
||||
|
||||
int autosuspend_delay_ms; /* if zero, the default value is used */
|
||||
void (*ehci_reg_dump)(struct device *);
|
||||
};
|
||||
|
||||
struct modemlink_pm_link_activectl {
|
||||
int gpio_initialized;
|
||||
int gpio_request_host_active;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
enum shmem_type {
|
||||
REAL_SHMEM,
|
||||
C2C_SHMEM,
|
||||
MAX_SHMEM_TYPE
|
||||
};
|
||||
|
||||
struct modem_mbox {
|
||||
unsigned int mbx_ap2cp_msg;
|
||||
unsigned int mbx_cp2ap_msg;
|
||||
unsigned int mbx_ap2cp_status; /* AP_STATUS */
|
||||
unsigned int mbx_cp2ap_status; /* CP_STATUS */
|
||||
|
||||
unsigned int int_ap2cp_msg;
|
||||
unsigned int int_ap2cp_status;
|
||||
unsigned int int_ap2cp_active;
|
||||
unsigned int int_ap2cp_uart_noti;
|
||||
|
||||
unsigned int irq_cp2ap_msg;
|
||||
unsigned int irq_cp2ap_status;
|
||||
unsigned int irq_cp2ap_active;
|
||||
unsigned int irq_cp2ap_wake_lock;
|
||||
|
||||
/* Performance request */
|
||||
unsigned int mbx_ap2cp_perf_req;
|
||||
unsigned int mbx_cp2ap_perf_req;
|
||||
unsigned int mbx_cp2ap_perf_req_cpu;
|
||||
unsigned int mbx_cp2ap_perf_req_mif;
|
||||
unsigned int mbx_cp2ap_perf_req_int;
|
||||
|
||||
unsigned int int_ap2cp_perf_req;
|
||||
unsigned int irq_cp2ap_perf_req_cpu;
|
||||
unsigned int irq_cp2ap_perf_req_mif;
|
||||
unsigned int irq_cp2ap_perf_req_int;
|
||||
|
||||
/* Status Bit Info */
|
||||
unsigned int sbi_evs_mode_mask;
|
||||
unsigned int sbi_evs_mode_pos;
|
||||
unsigned int sbi_lte_active_mask;
|
||||
unsigned int sbi_lte_active_pos;
|
||||
unsigned int sbi_cp_status_mask;
|
||||
unsigned int sbi_cp_status_pos;
|
||||
|
||||
unsigned int sbi_pda_active_mask;
|
||||
unsigned int sbi_pda_active_pos;
|
||||
unsigned int sbi_ap_status_mask;
|
||||
unsigned int sbi_ap_status_pos;
|
||||
|
||||
unsigned int sbi_uart_noti_mask;
|
||||
unsigned int sbi_uart_noti_pos;
|
||||
|
||||
/* Clk table Info */
|
||||
unsigned int *ap_clk_table;
|
||||
unsigned int ap_clk_cnt;
|
||||
|
||||
unsigned int *mif_clk_table;
|
||||
unsigned int mif_clk_cnt;
|
||||
|
||||
unsigned int *int_clk_table;
|
||||
unsigned int int_clk_cnt;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* platform data */
|
||||
struct modem_data {
|
||||
char *name;
|
||||
|
||||
unsigned int gpio_cp_on;
|
||||
unsigned int gpio_cp_off;
|
||||
unsigned int gpio_reset_req_n;
|
||||
unsigned int gpio_cp_reset;
|
||||
|
||||
/* for broadcasting AP's PM state (active or sleep) */
|
||||
unsigned int gpio_pda_active;
|
||||
|
||||
/* for checking aliveness of CP */
|
||||
unsigned int gpio_phone_active;
|
||||
unsigned int irq_phone_active;
|
||||
|
||||
/* for AP-CP IPC interrupt */
|
||||
unsigned int gpio_ipc_int2ap;
|
||||
unsigned int irq_ipc_int2ap;
|
||||
unsigned long irqf_ipc_int2ap; /* IRQ flags */
|
||||
unsigned int gpio_ipc_int2cp;
|
||||
|
||||
/* for AP-CP power management (PM) handshaking */
|
||||
unsigned int gpio_ap_wakeup;
|
||||
unsigned int irq_ap_wakeup;
|
||||
unsigned int gpio_ap_status;
|
||||
unsigned int gpio_cp_wakeup;
|
||||
unsigned int gpio_cp_status;
|
||||
unsigned int irq_cp_status;
|
||||
|
||||
/* for USB/HSIC PM */
|
||||
unsigned int gpio_host_wakeup;
|
||||
unsigned int irq_host_wakeup;
|
||||
unsigned int gpio_host_active;
|
||||
unsigned int gpio_slave_wakeup;
|
||||
|
||||
unsigned int gpio_cp_dump_int;
|
||||
unsigned int gpio_ap_dump_int;
|
||||
unsigned int gpio_flm_uart_sel;
|
||||
unsigned int gpio_cp_warm_reset;
|
||||
|
||||
unsigned int gpio_sim_detect;
|
||||
unsigned int irq_sim_detect;
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
struct modem_mbox *mbx;
|
||||
#endif
|
||||
|
||||
/* Switch with 2 links in a modem */
|
||||
unsigned int gpio_link_switch;
|
||||
|
||||
/* Modem component */
|
||||
enum modem_network modem_net;
|
||||
enum modem_t modem_type;
|
||||
|
||||
u32 link_types;
|
||||
char *link_name;
|
||||
unsigned long link_attrs; /* Set of link_attr_bit flags */
|
||||
|
||||
/* SIPC version */
|
||||
enum sipc_ver ipc_version;
|
||||
|
||||
/* the number of real IPC devices -> (IPC_RAW + 1) or (IPC_RFS + 1) */
|
||||
unsigned int max_ipc_dev;
|
||||
|
||||
/* Information of IO devices */
|
||||
unsigned int num_iodevs;
|
||||
struct modem_io_t *iodevs;
|
||||
|
||||
/* Modem link PM support */
|
||||
struct modemlink_pm_data *link_pm_data;
|
||||
|
||||
/* Handover with 2+ modems */
|
||||
bool use_handover;
|
||||
|
||||
/* SIM Detect polarity */
|
||||
bool sim_polarity;
|
||||
|
||||
u8 __iomem *ipc_base;
|
||||
|
||||
void (*gpio_revers_bias_clear)(void);
|
||||
void (*gpio_revers_bias_restore)(void);
|
||||
};
|
||||
|
||||
struct modem_irq {
|
||||
spinlock_t lock;
|
||||
unsigned int num;
|
||||
char name[MAX_NAME_LEN];
|
||||
unsigned long flags;
|
||||
bool active;
|
||||
};
|
||||
|
||||
#define MODEM_BOOT_DEV_SPI "spi_boot_link"
|
||||
|
||||
struct modem_boot_spi_platform_data {
|
||||
const char *name;
|
||||
unsigned int gpio_cp_status;
|
||||
};
|
||||
|
||||
struct modem_boot_spi {
|
||||
struct miscdevice misc_dev;
|
||||
struct spi_device *spi_dev;
|
||||
struct mutex lock;
|
||||
unsigned int gpio_cp_status;
|
||||
};
|
||||
#define to_modem_boot_spi(misc) \
|
||||
container_of(misc, struct modem_boot_spi, misc_dev);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
#define mif_dt_read_enum(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = (__typeof__(dest))(val); \
|
||||
} while (0)
|
||||
|
||||
#define mif_dt_read_bool(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = val ? true : false; \
|
||||
} while (0)
|
||||
|
||||
#define mif_dt_read_string(np, prop, dest) \
|
||||
do { \
|
||||
if (of_property_read_string(np, prop, \
|
||||
(const char **)&dest)) \
|
||||
return -EINVAL; \
|
||||
} while (0)
|
||||
|
||||
#define mif_dt_read_u32(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (of_property_read_u32(np, prop, &val)) \
|
||||
return -EINVAL; \
|
||||
dest = val; \
|
||||
} while (0)
|
||||
|
||||
#define mif_dt_read_u32_noerr(np, prop, dest) \
|
||||
do { \
|
||||
u32 val; \
|
||||
if (!of_property_read_u32(np, prop, &val)) \
|
||||
dest = val; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "mif: "
|
||||
#define FUNC (__func__)
|
||||
#define CALLER (__builtin_return_address(0))
|
||||
|
||||
#define mif_err_limited(fmt, ...) \
|
||||
printk_ratelimited(KERN_ERR LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
|
||||
#define mif_err(fmt, ...) \
|
||||
pr_err(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
|
||||
#define mif_debug(fmt, ...) \
|
||||
pr_debug(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
|
||||
#define mif_info(fmt, ...) \
|
||||
pr_info(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
|
||||
#define mif_trace(fmt, ...) \
|
||||
printk(KERN_DEBUG "mif: %s: %d: called(%pF): " fmt, \
|
||||
__func__, __LINE__, __builtin_return_address(0), ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
173
drivers/misc/modem_v1/modem_variation.c
Normal file
173
drivers/misc/modem_v1/modem_variation.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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 "modem_variation.h"
|
||||
|
||||
/* add declaration of modem & link type */
|
||||
/* modem device support */
|
||||
DECLARE_MODEM_INIT_DUMMY(dummy)
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_XMM6260
|
||||
DECLARE_MODEM_INIT_DUMMY(xmm6260)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_XMM6262
|
||||
DECLARE_MODEM_INIT_DUMMY(xmm6262)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LTE_MODEM_XMM7260
|
||||
DECLARE_MODEM_INIT_DUMMY(xmm7260)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_CDMA_MODEM_CBP71
|
||||
DECLARE_MODEM_INIT_DUMMY(cbp71)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_CDMA_MODEM_CBP72
|
||||
DECLARE_MODEM_INIT_DUMMY(cbp72)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_CDMA_MODEM_CBP82
|
||||
DECLARE_MODEM_INIT_DUMMY(cbp82)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LTE_MODEM_CMC220
|
||||
DECLARE_MODEM_INIT_DUMMY(cmc220)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LTE_MODEM_CMC221
|
||||
DECLARE_MODEM_INIT_DUMMY(cmc221)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_SS222
|
||||
DECLARE_MODEM_INIT_DUMMY(ss222)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_SH222AP
|
||||
DECLARE_MODEM_INIT_DUMMY(sh222ap)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_SS310AP
|
||||
DECLARE_MODEM_INIT_DUMMY(ss310ap)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_UMTS_MODEM_SS300
|
||||
DECLARE_MODEM_INIT_DUMMY(ss300)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_CDMA_MODEM_MDM6600
|
||||
DECLARE_MODEM_INIT_DUMMY(mdm6600)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GSM_MODEM_ESC6270
|
||||
DECLARE_MODEM_INIT_DUMMY(esc6270)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_CDMA_MODEM_QSC6085
|
||||
DECLARE_MODEM_INIT_DUMMY(qsc6085)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TDSCDMA_MODEM_SPRD8803
|
||||
DECLARE_MODEM_INIT_DUMMY(sprd8803)
|
||||
#endif
|
||||
|
||||
/* link device support */
|
||||
DECLARE_LINK_INIT_DUMMY(undefined)
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_MIPI
|
||||
DECLARE_LINK_INIT_DUMMY(mipi)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_USB
|
||||
DECLARE_LINK_INIT_DUMMY(usb)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_HSIC
|
||||
DECLARE_LINK_INIT_DUMMY(hsic)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_DPRAM
|
||||
DECLARE_LINK_INIT_DUMMY(dpram)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_PLD
|
||||
DECLARE_LINK_INIT_DUMMY(pld)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_C2C
|
||||
DECLARE_LINK_INIT_DUMMY(c2c)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_LLI
|
||||
DECLARE_LINK_INIT_DUMMY(lli)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_SHMEM
|
||||
DECLARE_LINK_INIT_DUMMY(shmem)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LINK_DEVICE_SPI
|
||||
DECLARE_LINK_INIT_DUMMY(spi)
|
||||
#endif
|
||||
|
||||
static modem_init_call modem_init_func[MAX_MODEM_TYPE] = {
|
||||
[IMC_XMM6260] = MODEM_INIT_CALL(xmm6260),
|
||||
[IMC_XMM6262] = MODEM_INIT_CALL(xmm6262),
|
||||
[VIA_CBP71] = MODEM_INIT_CALL(cbp71),
|
||||
[VIA_CBP72] = MODEM_INIT_CALL(cbp72),
|
||||
[VIA_CBP82] = MODEM_INIT_CALL(cbp82),
|
||||
[SEC_CMC220] = MODEM_INIT_CALL(cmc220),
|
||||
[SEC_CMC221] = MODEM_INIT_CALL(cmc221),
|
||||
[SEC_SS222] = MODEM_INIT_CALL(ss222),
|
||||
[SEC_SS300] = MODEM_INIT_CALL(ss300),
|
||||
[SEC_SH222AP] = MODEM_INIT_CALL(sh222ap),
|
||||
[SEC_SS310AP] = MODEM_INIT_CALL(ss310ap),
|
||||
[QC_MDM6600] = MODEM_INIT_CALL(mdm6600),
|
||||
[QC_ESC6270] = MODEM_INIT_CALL(esc6270),
|
||||
[QC_QSC6085] = MODEM_INIT_CALL(qsc6085),
|
||||
[SPRD_SC8803] = MODEM_INIT_CALL(sprd8803),
|
||||
[IMC_XMM7260] = MODEM_INIT_CALL(xmm7260),
|
||||
[DUMMY] = MODEM_INIT_CALL(dummy),
|
||||
};
|
||||
|
||||
static link_init_call link_init_func[LINKDEV_MAX] = {
|
||||
[LINKDEV_UNDEFINED] = LINK_INIT_CALL(undefined),
|
||||
[LINKDEV_MIPI] = LINK_INIT_CALL(mipi),
|
||||
[LINKDEV_USB] = LINK_INIT_CALL(usb),
|
||||
[LINKDEV_HSIC] = LINK_INIT_CALL(hsic),
|
||||
[LINKDEV_DPRAM] = LINK_INIT_CALL(dpram),
|
||||
[LINKDEV_PLD] = LINK_INIT_CALL(pld),
|
||||
[LINKDEV_C2C] = LINK_INIT_CALL(c2c),
|
||||
[LINKDEV_LLI] = LINK_INIT_CALL(lli),
|
||||
[LINKDEV_SHMEM] = LINK_INIT_CALL(shmem),
|
||||
[LINKDEV_SPI] = LINK_INIT_CALL(spi),
|
||||
};
|
||||
|
||||
int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
|
||||
{
|
||||
if (modem_init_func[pdata->modem_type])
|
||||
return modem_init_func[pdata->modem_type](mc, pdata);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
struct link_device *call_link_init_func(struct platform_device *pdev,
|
||||
enum modem_link link_type)
|
||||
{
|
||||
if (link_init_func[link_type])
|
||||
return link_init_func[link_type](pdev);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
155
drivers/misc/modem_v1/modem_variation.h
Normal file
155
drivers/misc/modem_v1/modem_variation.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MODEM_VARIATION_H__
|
||||
#define __MODEM_VARIATION_H__
|
||||
|
||||
#include "modem_prj.h"
|
||||
|
||||
#define DECLARE_MODEM_INIT(type) \
|
||||
int type ## _init_modemctl_device( \
|
||||
struct modem_ctl *mc, \
|
||||
struct modem_data *pdata)
|
||||
|
||||
#define DECLARE_MODEM_INIT_DUMMY(type) \
|
||||
DECLARE_MODEM_INIT(type) { return 0; }
|
||||
|
||||
#define DECLARE_LINK_INIT(type) \
|
||||
struct link_device *type ## _create_link_device( \
|
||||
struct platform_device *pdev)
|
||||
|
||||
#define DECLARE_LINK_INIT_DUMMY(type) \
|
||||
DECLARE_LINK_INIT(type) { return NULL; }
|
||||
|
||||
#define MODEM_INIT_CALL(type) type ## _init_modemctl_device
|
||||
|
||||
#define LINK_INIT_CALL(type) type ## _create_link_device
|
||||
|
||||
/**
|
||||
* Add extern declaration of modem & link type
|
||||
* (CAUTION!!! Every DUMMY function must be declared in modem_variation.c)
|
||||
*/
|
||||
|
||||
/* modem device support */
|
||||
#ifdef CONFIG_UMTS_MODEM_XMM6260
|
||||
DECLARE_MODEM_INIT(xmm6260);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UMTS_MODEM_XMM6262
|
||||
DECLARE_MODEM_INIT(xmm6262);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LTE_MODEM_XMM7260
|
||||
DECLARE_MODEM_INIT(xmm7260);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDMA_MODEM_CBP71
|
||||
DECLARE_MODEM_INIT(cbp71);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDMA_MODEM_CBP72
|
||||
DECLARE_MODEM_INIT(cbp72);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDMA_MODEM_CBP82
|
||||
DECLARE_MODEM_INIT(cbp82);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LTE_MODEM_CMC220
|
||||
DECLARE_MODEM_INIT(cmc220);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LTE_MODEM_CMC221
|
||||
DECLARE_MODEM_INIT(cmc221);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UMTS_MODEM_SH222AP
|
||||
DECLARE_MODEM_INIT(sh222ap);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UMTS_MODEM_SS310AP
|
||||
DECLARE_MODEM_INIT(ss310ap);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UMTS_MODEM_SS222
|
||||
DECLARE_MODEM_INIT(ss222);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_UMTS_MODEM_SS300
|
||||
DECLARE_MODEM_INIT(ss300);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDMA_MODEM_MDM6600
|
||||
DECLARE_MODEM_INIT(mdm6600);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GSM_MODEM_ESC6270
|
||||
DECLARE_MODEM_INIT(esc6270);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CDMA_MODEM_QSC6085
|
||||
DECLARE_MODEM_INIT(qsc6085);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803
|
||||
DECLARE_MODEM_INIT(sprd8803);
|
||||
#endif
|
||||
|
||||
/* link device support */
|
||||
#ifdef CONFIG_LINK_DEVICE_MIPI
|
||||
DECLARE_LINK_INIT(mipi);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_USB
|
||||
DECLARE_LINK_INIT(usb);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_HSIC
|
||||
DECLARE_LINK_INIT(hsic);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_DPRAM
|
||||
DECLARE_LINK_INIT(dpram);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_PLD
|
||||
DECLARE_LINK_INIT(pld);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_C2C
|
||||
DECLARE_LINK_INIT(c2c);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_LLI
|
||||
DECLARE_LINK_INIT(lli);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SHMEM
|
||||
DECLARE_LINK_INIT(shmem);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LINK_DEVICE_SPI
|
||||
DECLARE_LINK_INIT(spi);
|
||||
#endif
|
||||
|
||||
typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *);
|
||||
typedef struct link_device *(*link_init_call)(struct platform_device *);
|
||||
|
||||
int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata);
|
||||
|
||||
struct link_device *call_link_init_func(struct platform_device *pdev,
|
||||
enum modem_link link_type);
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue