/*
* ssi_protocol.c
*
* Implementation of the SSI McSAAB improved protocol.
*
* Copyright (C) 2010 Nokia Corporation. All rights reserved.
* Copyright (C) 2013 Sebastian Reichel <sre@kernel.org>
*
* Contact: Carlos Chinea <carlos.chinea@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/if_phonet.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/hsi/hsi.h>
#include <linux/hsi/ssi_protocol.h>
void ssi_waketest(struct hsi_client *cl, unsigned int enable);
#define SSIP_TXQUEUE_LEN 100
#define SSIP_MAX_MTU 65535
#define SSIP_DEFAULT_MTU 4000
#define PN_MEDIA_SOS 21
#define SSIP_MIN_PN_HDR 6 /* FIXME: Revisit */
#define SSIP_WDTOUT 2000 /* FIXME: has to be 500 msecs */
#define SSIP_KATOUT 15 /* 15 msecs */
#define SSIP_MAX_CMDS 5 /* Number of pre-allocated commands buffers */
#define SSIP_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
#define SSIP_CMT_LOADER_SYNC 0x11223344
/*
* SSI protocol command definitions
*/
#define SSIP_COMMAND(data) ((data) >> 28)
#define SSIP_PAYLOAD(data) ((data) & 0xfffffff)
/* Commands */
#define SSIP_SW_BREAK 0
#define SSIP_BOOTINFO_REQ 1
#define SSIP_BOOTINFO_RESP 2
#define SSIP_WAKETEST_RESULT 3
#define SSIP_START_TRANS 4
#define SSIP_READY 5
/* Payloads */
#define SSIP_DATA_VERSION(data) ((data) & 0xff)
#define SSIP_LOCAL_VERID 1
#define SSIP_WAKETEST_OK 0
#define SSIP_WAKETEST_FAILED 1
#define SSIP_PDU_LENGTH(data) (((data) >> 8) & 0xffff)
#define SSIP_MSG_ID(data) ((data) & 0xff)
/* Generic Command */
#define SSIP_CMD(cmd, payload) (((cmd) << 28) | ((payload) & 0xfffffff))
/* Commands for the control channel */
#define SSIP_BOOTINFO_REQ_CMD(ver) \
SSIP_CMD(SSIP_BOOTINFO_REQ, SSIP_DATA_VERSION(ver))
#define SSIP_BOOTINFO_RESP_CMD(ver) \
SSIP_CMD(SSIP_BOOTINFO_RESP, SSIP_DATA_VERSION(ver))
#define SSIP_START_TRANS_CMD(pdulen, id) \
SSIP_CMD(SSIP_START_TRANS, (((pdulen) << 8) | SSIP_MSG_ID(id)))
#define SSIP_READY_CMD SSIP_CMD(SSIP_READY, 0)
#define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0)
/* Main state machine states */
enum {
INIT,
HANDSHAKE,
ACTIVE,
};
/* Send state machine states */
enum {
SEND_IDLE,
WAIT4READY,
SEND_READY,
SENDING,
SENDING_SWBREAK,
};
/* Receive state machine states */
enum {
RECV_IDLE,
RECV_READY,
RECEIVING,
};
/**
* struct ssi_protocol - SSI protocol (McSAAB) data
* @main_state: Main state machine
* @send_state: TX state machine
* @recv_state: RX state machine
* @waketest: Flag to follow wake line test
* @rxid: RX data id
* @txid: TX data id
* @txqueue_len: TX queue length
* @tx_wd: TX watchdog
* @rx_wd: RX watchdog
* @keep_alive: Workaround for SSI HW bug
* @lock: To serialize access to this struct
* @netdev: Phonet network device
* @txqueue: TX data queue
* @cmdqueue: Queue of free commands
* @cl: HSI client own reference
* @link: Link for ssip_list
* @tx_usecount: Refcount to keep track the slaves that use the wake line
* @channel_id_cmd: HSI channel id for command stream
* @channel_id_data: HSI channel id for data stream
*/
struct ssi_protocol {
unsigned int main_state;
unsigned int send_state;
unsigned int recv_state;
unsigned int waketest:1;
u8 rxid;
u8 txid;
unsigned int txqueue_len;
struct timer_list tx_wd;
struct timer_list rx_wd;
struct timer_list keep_alive; /* wake-up workaround */
spinlock_t lock;
struct net_device *netdev;
struct list_head txqueue;
struct list_head cmdqueue;
struct hsi_client *cl;
struct list_head link;
atomic_t tx_usecnt;
int channel_id_cmd;
int channel_id_data;
};
/* List of ssi protocol instances */
static LIST_HEAD(ssip_list);
static void ssip_rxcmd_complete(struct hsi_msg *msg);
static inline void ssip_set_cmd(struct hsi_msg *msg, u32 cmd)
{
u32 *data;
data = sg_virt(msg->sgt.sgl);
*data = cmd;
}
static inline u32 ssip_get_cmd(struct hsi_msg *msg)
{
u32 *data;
data = sg_virt(msg