Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

File diff suppressed because it is too large Load diff

View 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;
}

View 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

File diff suppressed because it is too large Load diff

View 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");

View 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);
}

View 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);
}

View 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

View 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

File diff suppressed because it is too large Load diff

View 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__*/

View 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

View 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;
}

View 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