M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
#
# msm network device configuration
#
config MSM_RMNET
tristate "MSM RMNET Virtual Network Device"
depends on ARCH_MSM
default y
help
Virtual ethernet interface for MSM RMNET transport.
config MSM_RMNET_SDIO
bool "RMNET SDIO Driver"
depends on MSM_SDIO_DMUX
default n
help
Implements RMNET over SDIO interface.
config MSM_RMNET_BAM
bool "RMNET BAM Driver"
depends on (MSM_BAM_DMUX && NET_SCHED && NET_SCH_HTB && NET_SCH_PRIO && NET_CLS_FW)
default n
help
Implements RMNET over BAM interface.
RMNET provides a virtual ethernet interface
for routing IP packets within the MSM using
BAM as a physical transport.
config MSM_RMNET_SMUX
bool "RMNET SMUX Driver"
depends on N_SMUX
help
Implements RMNET over SMUX interface.
RMNET provides a virtual ethernet interface
for routing IP packets within the MSM using
HSUART as a physical transport.
config MSM_RMNET_DEBUG
bool "MSM RMNET debug interface"
depends on MSM_RMNET
default n
help
Debug stats on wakeup counts.
config MSM_RMNET_WWAN
tristate "MSM RMNET WWAN Network Device"
depends on IPA
default n
help
WWAN Network Driver
Provides an API to embedded
applications to send and receive
the data to/from A2
config QFEC
tristate "QFEC ethernet driver"
select MII
depends on ARM
help
This driver supports Ethernet in the FSM9xxx.
To compile this driver as a module, choose M here: the
module will be called qfec.
config ECM_IPA
tristate "STD ECM LAN Driver support"
depends on IPA
help
Allows LAN between Apps and tethered HOST on STD ECM
+11
View File
@@ -0,0 +1,11 @@
#
# Makefile for the msm networking support.
#
obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o
obj-$(CONFIG_MSM_RMNET_WWAN) += msm_rmnet_wwan.o
obj-$(CONFIG_MSM_RMNET_SDIO) += msm_rmnet_sdio.o
obj-$(CONFIG_MSM_RMNET_BAM) += msm_rmnet_bam.o
obj-$(CONFIG_MSM_RMNET_SMUX) += msm_rmnet_smux.o
obj-$(CONFIG_QFEC) += qfec.o
obj-$(CONFIG_ECM_IPA) += ecm_ipa.o
File diff suppressed because it is too large Load Diff
+839
View File
@@ -0,0 +1,839 @@
/* linux/drivers/net/msm_rmnet.c
*
* Virtual Ethernet Interface for MSM7K Networking
*
* Copyright (C) 2007 Google, Inc.
* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
#include <linux/platform_device.h>
#include <linux/if_arp.h>
#include <linux/msm_rmnet.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <mach/msm_smd.h>
#include <mach/subsystem_restart.h>
/* Debug message support */
static int msm_rmnet_debug_mask;
module_param_named(debug_enable, msm_rmnet_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
#define DEBUG_MASK_LVL0 (1U << 0)
#define DEBUG_MASK_LVL1 (1U << 1)
#define DEBUG_MASK_LVL2 (1U << 2)
#define DBG(m, x...) do { \
if (msm_rmnet_debug_mask & m) \
pr_info(x); \
} while (0)
#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
/* Configure device instances */
#define RMNET_DEVICE_COUNT (8)
static const char *ch_name[RMNET_DEVICE_COUNT] = {
"DATA5",
"DATA6",
"DATA7",
"DATA8",
"DATA9",
"DATA12",
"DATA13",
"DATA14",
};
/* XXX should come from smd headers */
#define SMD_PORT_ETHER0 11
/* allow larger frames */
#define RMNET_DATA_LEN 2000
#define HEADROOM_FOR_QOS 8
static struct completion *port_complete[RMNET_DEVICE_COUNT];
struct rmnet_private
{
smd_channel_t *ch;
struct net_device_stats stats;
const char *chname;
struct wake_lock wake_lock;
#ifdef CONFIG_MSM_RMNET_DEBUG
ktime_t last_packet;
unsigned long wakeups_xmit;
unsigned long wakeups_rcv;
unsigned long timeout_us;
#endif
struct sk_buff *skb;
spinlock_t lock;
struct tasklet_struct tsklt;
struct tasklet_struct rx_tasklet;
u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */
struct platform_driver pdrv;
struct completion complete;
void *pil;
struct mutex pil_lock;
};
static uint msm_rmnet_modem_wait;
module_param_named(modem_wait, msm_rmnet_modem_wait,
uint, S_IRUGO | S_IWUSR | S_IWGRP);
/* Forward declaration */
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int count_this_packet(void *_hdr, int len)
{
struct ethhdr *hdr = _hdr;
if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
return 0;
return 1;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
static unsigned long timeout_us;
#ifdef CONFIG_HAS_EARLYSUSPEND
/*
* If early suspend is enabled then we specify two timeout values,
* screen on (default), and screen is off.
*/
static unsigned long timeout_suspend_us;
static struct device *rmnet0;
/* Set timeout in us when the screen is off. */
static ssize_t timeout_suspend_store(struct device *d, struct device_attribute *attr, const char *buf, size_t n)
{
timeout_suspend_us = simple_strtoul(buf, NULL, 10);
return n;
}
static ssize_t timeout_suspend_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
}
static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show, timeout_suspend_store);
static void rmnet_early_suspend(struct early_suspend *handler) {
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_suspend_us;
}
}
static void rmnet_late_resume(struct early_suspend *handler) {
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_us;
}
}
static struct early_suspend rmnet_power_suspend = {
.suspend = rmnet_early_suspend,
.resume = rmnet_late_resume,
};
static int __init rmnet_late_init(void)
{
register_early_suspend(&rmnet_power_suspend);
return 0;
}
late_initcall(rmnet_late_init);
#endif
/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
static int rmnet_cause_wakeup(struct rmnet_private *p) {
int ret = 0;
ktime_t now;
if (p->timeout_us == 0) /* Check if disabled */
return 0;
/* Use real (wall) time. */
now = ktime_get_real();
if (ktime_us_delta(now, p->last_packet) > p->timeout_us) {
ret = 1;
}
p->last_packet = now;
return ret;
}
static ssize_t wakeups_xmit_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_xmit);
}
DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_rcv);
}
DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
/* Set timeout in us. */
static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t n)
{
#ifndef CONFIG_HAS_EARLYSUSPEND
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p->timeout_us = timeout_us = simple_strtoul(buf, NULL, 10);
#else
/* If using early suspend/resume hooks do not write the value on store. */
timeout_us = simple_strtoul(buf, NULL, 10);
#endif
return n;
}
static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", timeout_us);
}
DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
#endif
static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
{
__be16 protocol = 0;
skb->dev = dev;
/* Determine L3 protocol */
switch (skb->data[0] & 0xf0) {
case 0x40:
protocol = htons(ETH_P_IP);
break;
case 0x60:
protocol = htons(ETH_P_IPV6);
break;
default:
pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
dev->name, skb->data[0] & 0xf0);
/* skb will be dropped in uppder layer for unknown protocol */
}
return protocol;
}
static void smd_net_data_handler(unsigned long arg);
/* Called in soft-irq context */
static void smd_net_data_handler(unsigned long arg)
{
struct net_device *dev = (struct net_device *) arg;
struct rmnet_private *p = netdev_priv(dev);
struct sk_buff *skb;
void *ptr = 0;
int sz;
u32 opmode = p->operation_mode;
unsigned long flags;
for (;;) {
sz = smd_cur_packet_size(p->ch);
if (sz == 0) break;
if (smd_read_avail(p->ch) < sz) break;
skb = dev_alloc_skb(sz + NET_IP_ALIGN);
if (skb == NULL) {
pr_err("[%s] rmnet_recv() cannot allocate skb\n",
dev->name);
/* out of memory, reschedule a later attempt */
p->rx_tasklet.data = (unsigned long)dev;
tasklet_schedule(&p->rx_tasklet);
break;
} else {
skb->dev = dev;
skb_reserve(skb, NET_IP_ALIGN);
ptr = skb_put(skb, sz);
wake_lock_timeout(&p->wake_lock, HZ / 2);
if (smd_read(p->ch, ptr, sz) != sz) {
pr_err("[%s] rmnet_recv() smd lied about avail?!",
dev->name);
ptr = 0;
dev_kfree_skb_irq(skb);
} else {
/* Handle Rx frame format */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_IP(opmode)) {
/* Driver in IP mode */
skb->protocol =
rmnet_ip_type_trans(skb, dev);
} else {
/* Driver in Ethernet mode */
skb->protocol =
eth_type_trans(skb, dev);
}
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(ptr, skb->len)) {
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_rcv +=
rmnet_cause_wakeup(p);
#endif
p->stats.rx_packets++;
p->stats.rx_bytes += skb->len;
}
DBG1("[%s] Rx packet #%lu len=%d\n",
dev->name, p->stats.rx_packets,
skb->len);
/* Deliver to network stack */
netif_rx(skb);
}
continue;
}
if (smd_read(p->ch, ptr, sz) != sz)
pr_err("[%s] rmnet_recv() smd lied about avail?!",
dev->name);
}
}
static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
smd_channel_t *ch = p->ch;
int smd_ret;
struct QMI_QOS_HDR_S *qmih;
u32 opmode;
unsigned long flags;
/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_QOS(opmode)) {
qmih = (struct QMI_QOS_HDR_S *)
skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
qmih->version = 1;
qmih->flags = 0;
qmih->flow_id = skb->mark;
}
dev->trans_start = jiffies;
smd_ret = smd_write(ch, skb->data, skb->len);
if (smd_ret != skb->len) {
pr_err("[%s] %s: smd_write returned error %d",
dev->name, __func__, smd_ret);
p->stats.tx_errors++;
goto xmit_out;
}
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
p->stats.tx_packets++;
p->stats.tx_bytes += skb->len;
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_xmit += rmnet_cause_wakeup(p);
#endif
}
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
dev->name, p->stats.tx_packets, skb->len, skb->mark);
xmit_out:
/* data xmited, safe to release skb */
dev_kfree_skb_irq(skb);
return 0;
}
static void _rmnet_resume_flow(unsigned long param)
{
struct net_device *dev = (struct net_device *)param;
struct rmnet_private *p = netdev_priv(dev);
struct sk_buff *skb = NULL;
unsigned long flags;
/* xmit and enable the flow only once even if
multiple tasklets were scheduled by smd_net_notify */
spin_lock_irqsave(&p->lock, flags);
if (p->skb && (smd_write_avail(p->ch) >= p->skb->len)) {
skb = p->skb;
p->skb = NULL;
spin_unlock_irqrestore(&p->lock, flags);
_rmnet_xmit(skb, dev);
netif_wake_queue(dev);
} else
spin_unlock_irqrestore(&p->lock, flags);
}
static void msm_rmnet_unload_modem(void *pil)
{
if (pil)
subsystem_put(pil);
}
static void *msm_rmnet_load_modem(struct net_device *dev)
{
void *pil;
int rc;
struct rmnet_private *p = netdev_priv(dev);
pil = subsystem_get("modem");
if (IS_ERR(pil))
pr_err("[%s] %s: modem load failed\n",
dev->name, __func__);
else if (msm_rmnet_modem_wait) {
rc = wait_for_completion_interruptible_timeout(
&p->complete,
msecs_to_jiffies(msm_rmnet_modem_wait * 1000));
if (!rc)
rc = -ETIMEDOUT;
if (rc < 0) {
pr_err("[%s] %s: wait for rmnet port failed %d\n",
dev->name, __func__, rc);
msm_rmnet_unload_modem(pil);
pil = ERR_PTR(rc);
}
}
return pil;
}
static void smd_net_notify(void *_dev, unsigned event)
{
struct rmnet_private *p = netdev_priv((struct net_device *)_dev);
switch (event) {
case SMD_EVENT_DATA:
spin_lock(&p->lock);
if (p->skb && (smd_write_avail(p->ch) >= p->skb->len)) {
smd_disable_read_intr(p->ch);
tasklet_hi_schedule(&p->tsklt);
}
spin_unlock(&p->lock);
if (smd_read_avail(p->ch) &&
(smd_read_avail(p->ch) >= smd_cur_packet_size(p->ch))) {
p->rx_tasklet.data = (unsigned long) _dev;
tasklet_schedule(&p->rx_tasklet);
}
break;
case SMD_EVENT_OPEN:
DBG0("%s: opening SMD port\n", __func__);
netif_carrier_on(_dev);
if (netif_queue_stopped(_dev)) {
DBG0("%s: re-starting if queue\n", __func__);
netif_wake_queue(_dev);
}
break;
case SMD_EVENT_CLOSE:
DBG0("%s: closing SMD port\n", __func__);
netif_carrier_off(_dev);
break;
}
}
static int __rmnet_open(struct net_device *dev)
{
int r;
void *pil;
struct rmnet_private *p = netdev_priv(dev);
mutex_lock(&p->pil_lock);
if (!p->pil) {
pil = msm_rmnet_load_modem(dev);
if (IS_ERR(pil)) {
mutex_unlock(&p->pil_lock);
return PTR_ERR(pil);
}
p->pil = pil;
}
mutex_unlock(&p->pil_lock);
if (!p->ch) {
r = smd_open(p->chname, &p->ch, dev, smd_net_notify);
if (r < 0)
return -ENODEV;
}
smd_disable_read_intr(p->ch);
return 0;
}
static int __rmnet_close(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
if (p->ch)
return 0;
else
return -EBADF;
}
static int rmnet_open(struct net_device *dev)
{
int rc = 0;
DBG0("[%s] rmnet_open()\n", dev->name);
rc = __rmnet_open(dev);
if (rc == 0)
netif_start_queue(dev);
return rc;
}
static int rmnet_stop(struct net_device *dev)
{
DBG0("[%s] rmnet_stop()\n", dev->name);
netif_stop_queue(dev);
/* TODO: unload modem safely,
currently, this causes unnecessary unloads */
/*
mutex_lock(&p->pil_lock);
msm_rmnet_unload_modem(p->pil);
p->pil = NULL;
mutex_unlock(&p->pil_lock);
*/
return 0;
}
static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
{
if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
return -EINVAL;
DBG0("[%s] MTU change: old=%d new=%d\n",
dev->name, dev->mtu, new_mtu);
dev->mtu = new_mtu;
return 0;
}
static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
smd_channel_t *ch = p->ch;
unsigned long flags;
if (netif_queue_stopped(dev)) {
pr_err("[%s] fatal: rmnet_xmit called when netif_queue is stopped",
dev->name);
return 0;
}
spin_lock_irqsave(&p->lock, flags);
smd_enable_read_intr(ch);
if (smd_write_avail(ch) < skb->len) {
netif_stop_queue(dev);
p->skb = skb;
spin_unlock_irqrestore(&p->lock, flags);
return 0;
}
smd_disable_read_intr(ch);
spin_unlock_irqrestore(&p->lock, flags);
_rmnet_xmit(skb, dev);
return 0;
}
static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
return &p->stats;
}
static void rmnet_set_multicast_list(struct net_device *dev)
{
}
static void rmnet_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
}
static const struct net_device_ops rmnet_ops_ether = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_set_rx_mode = rmnet_set_multicast_list,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static const struct net_device_ops rmnet_ops_ip = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_set_rx_mode = rmnet_set_multicast_list,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = 0,
.ndo_validate_addr = 0,
};
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct rmnet_private *p = netdev_priv(dev);
u32 old_opmode = p->operation_mode;
unsigned long flags;
int prev_mtu = dev->mtu;
int rc = 0;
/* Process IOCTL command */
switch (cmd) {
case RMNET_IOCTL_SET_LLP_ETHERNET: /* Set Ethernet protocol */
/* Perform Ethernet config only if in IP mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_IP) {
ether_setup(dev);
random_ether_addr(dev->dev_addr);
dev->mtu = prev_mtu;
dev->netdev_ops = &rmnet_ops_ether;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_IP;
p->operation_mode |= RMNET_MODE_LLP_ETH;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set Ethernet protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
/* Perform IP config only if in Ethernet mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_ETH) {
/* Undo config done in ether_setup() */
dev->header_ops = 0; /* No header */
dev->type = ARPHRD_RAWIP;
dev->hard_header_len = 0;
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST|
IFF_MULTICAST);
dev->netdev_ops = &rmnet_ops_ip;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_ETH;
p->operation_mode |= RMNET_MODE_LLP_IP;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set IP protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode &
(RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
break;
case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode |= RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
dev->name);
break;
case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
dev->name);
break;
case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode & RMNET_MODE_QOS);
break;
case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
break;
case RMNET_IOCTL_OPEN: /* Open transport port */
rc = __rmnet_open(dev);
DBG0("[%s] rmnet_ioctl(): open transport port\n",
dev->name);
break;
case RMNET_IOCTL_CLOSE: /* Close transport port */
rc = __rmnet_close(dev);
DBG0("[%s] rmnet_ioctl(): close transport port\n",
dev->name);
break;
default:
pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
dev->name, cmd);
return -EINVAL;
}
DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
dev->name, __func__, cmd, old_opmode, p->operation_mode);
return rc;
}
static void __init rmnet_setup(struct net_device *dev)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_ops_ether;
ether_setup(dev);
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
dev->needed_headroom = HEADROOM_FOR_QOS;
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
static int msm_rmnet_smd_probe(struct platform_device *pdev)
{
int i;
for (i = 0; i < RMNET_DEVICE_COUNT; i++)
if (!strcmp(pdev->name, ch_name[i])) {
complete_all(port_complete[i]);
break;
}
return 0;
}
static int __init rmnet_init(void)
{
int ret;
struct device *d;
struct net_device *dev;
struct rmnet_private *p;
unsigned n;
pr_info("%s: SMD devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
#ifdef CONFIG_MSM_RMNET_DEBUG
timeout_us = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
timeout_suspend_us = 0;
#endif
#endif
for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct rmnet_private),
"rmnet%d", rmnet_setup);
if (!dev)
return -ENOMEM;
d = &(dev->dev);
p = netdev_priv(dev);
p->chname = ch_name[n];
/* Initial config uses Ethernet */
p->operation_mode = RMNET_MODE_LLP_ETH;
p->skb = NULL;
spin_lock_init(&p->lock);
tasklet_init(&p->tsklt, _rmnet_resume_flow,
(unsigned long)dev);
tasklet_init(&p->rx_tasklet, smd_net_data_handler,
(unsigned long)dev);
wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]);
#ifdef CONFIG_MSM_RMNET_DEBUG
p->timeout_us = timeout_us;
p->wakeups_xmit = p->wakeups_rcv = 0;
#endif
init_completion(&p->complete);
port_complete[n] = &p->complete;
mutex_init(&p->pil_lock);
p->pdrv.probe = msm_rmnet_smd_probe;
p->pdrv.driver.name = ch_name[n];
p->pdrv.driver.owner = THIS_MODULE;
ret = platform_driver_register(&p->pdrv);
if (ret) {
free_netdev(dev);
return ret;
}
ret = register_netdev(dev);
if (ret) {
platform_driver_unregister(&p->pdrv);
free_netdev(dev);
return ret;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
if (device_create_file(d, &dev_attr_timeout))
continue;
if (device_create_file(d, &dev_attr_wakeups_xmit))
continue;
if (device_create_file(d, &dev_attr_wakeups_rcv))
continue;
#ifdef CONFIG_HAS_EARLYSUSPEND
if (device_create_file(d, &dev_attr_timeout_suspend))
continue;
/* Only care about rmnet0 for suspend/resume tiemout hooks. */
if (n == 0)
rmnet0 = d;
#endif
#endif
}
return 0;
}
module_init(rmnet_init);
@@ -0,0 +1,990 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*
*/
/*
* RMNET BAM Module.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
#include <linux/if_arp.h>
#include <linux/msm_rmnet.h>
#include <linux/platform_device.h>
#include <net/pkt_sched.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <mach/bam_dmux.h>
/* Debug message support */
static int msm_rmnet_bam_debug_mask;
module_param_named(debug_enable, msm_rmnet_bam_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
#define DEBUG_MASK_LVL0 (1U << 0)
#define DEBUG_MASK_LVL1 (1U << 1)
#define DEBUG_MASK_LVL2 (1U << 2)
#define DBG(m, x...) do { \
if (msm_rmnet_bam_debug_mask & m) \
pr_info(x); \
} while (0)
#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
/* Configure device instances */
#define RMNET_DEVICE_COUNT (8)
/* allow larger frames */
#define RMNET_DATA_LEN 2000
#define DEVICE_ID_INVALID -1
#define DEVICE_INACTIVE 0
#define DEVICE_ACTIVE 1
#define HEADROOM_FOR_BAM 8 /* for mux header */
#define HEADROOM_FOR_QOS 8
#define TAILROOM 8 /* for padding by mux layer */
struct rmnet_private {
struct net_device_stats stats;
uint32_t ch_id;
#ifdef CONFIG_MSM_RMNET_DEBUG
ktime_t last_packet;
unsigned long wakeups_xmit;
unsigned long wakeups_rcv;
unsigned long timeout_us;
#endif
struct sk_buff *waiting_for_ul_skb;
spinlock_t lock;
spinlock_t tx_queue_lock;
struct tasklet_struct tsklt;
u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */
uint8_t device_up;
uint8_t in_reset;
};
#ifdef CONFIG_MSM_RMNET_DEBUG
static unsigned long timeout_us;
#ifdef CONFIG_HAS_EARLYSUSPEND
/*
* If early suspend is enabled then we specify two timeout values,
* screen on (default), and screen is off.
*/
static unsigned long timeout_suspend_us;
static struct device *rmnet0;
/* Set timeout in us when the screen is off. */
static ssize_t timeout_suspend_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t n)
{
timeout_suspend_us = strict_strtoul(buf, NULL, 10);
return n;
}
static ssize_t timeout_suspend_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
}
static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
timeout_suspend_store);
static void rmnet_early_suspend(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_suspend_us;
}
}
static void rmnet_late_resume(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_us;
}
}
static struct early_suspend rmnet_power_suspend = {
.suspend = rmnet_early_suspend,
.resume = rmnet_late_resume,
};
static int __init rmnet_late_init(void)
{
register_early_suspend(&rmnet_power_suspend);
return 0;
}
late_initcall(rmnet_late_init);
#endif
/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
static int rmnet_cause_wakeup(struct rmnet_private *p)
{
int ret = 0;
ktime_t now;
if (p->timeout_us == 0) /* Check if disabled */
return 0;
/* Use real (wall) time. */
now = ktime_get_real();
if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
ret = 1;
p->last_packet = now;
return ret;
}
static ssize_t wakeups_xmit_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_xmit);
}
DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_rcv);
}
DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
/* Set timeout in us. */
static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t n)
{
#ifndef CONFIG_HAS_EARLYSUSPEND
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
#else
/* If using early suspend/resume hooks do not write the value on store. */
timeout_us = strict_strtoul(buf, NULL, 10);
#endif
return n;
}
static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", timeout_us);
}
DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
#endif
/* Forward declaration */
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
{
__be16 protocol = 0;
skb->dev = dev;
/* Determine L3 protocol */
switch (skb->data[0] & 0xf0) {
case 0x40:
protocol = htons(ETH_P_IP);
break;
case 0x60:
protocol = htons(ETH_P_IPV6);
break;
default:
pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
dev->name, skb->data[0] & 0xf0);
/* skb will be dropped in upper layer for unknown protocol */
}
return protocol;
}
static int count_this_packet(void *_hdr, int len)
{
struct ethhdr *hdr = _hdr;
if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
return 0;
return 1;
}
/* Rx Callback, Called in Work Queue context */
static void bam_recv_notify(void *dev, struct sk_buff *skb)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
u32 opmode;
if (skb) {
skb->dev = dev;
/* Handle Rx frame format */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_IP(opmode)) {
/* Driver in IP mode */
skb->protocol = rmnet_ip_type_trans(skb, dev);
} else {
/* Driver in Ethernet mode */
skb->protocol = eth_type_trans(skb, dev);
}
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_rcv += rmnet_cause_wakeup(p);
#endif
p->stats.rx_packets++;
p->stats.rx_bytes += skb->len;
}
DBG1("[%s] Rx packet #%lu len=%d\n",
((struct net_device *)dev)->name,
p->stats.rx_packets, skb->len);
/* Deliver to network stack */
netif_rx(skb);
} else
pr_err("[%s] %s: No skb received",
((struct net_device *)dev)->name, __func__);
}
static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
int bam_ret;
struct QMI_QOS_HDR_S *qmih;
u32 opmode;
unsigned long flags;
/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_QOS(opmode)) {
qmih = (struct QMI_QOS_HDR_S *)
skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
qmih->version = 1;
qmih->flags = 0;
qmih->flow_id = skb->mark;
}
dev->trans_start = jiffies;
/* if write() succeeds, skb access is unsafe in this process */
bam_ret = msm_bam_dmux_write(p->ch_id, skb);
if (bam_ret != 0 && bam_ret != -EAGAIN && bam_ret != -EFAULT) {
pr_err("[%s] %s: write returned error %d",
dev->name, __func__, bam_ret);
return -EPERM;
}
return bam_ret;
}
static void bam_write_done(void *dev, struct sk_buff *skb)
{
struct rmnet_private *p = netdev_priv(dev);
u32 opmode = p->operation_mode;
unsigned long flags;
DBG1("%s: write complete\n", __func__);
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
p->stats.tx_packets++;
p->stats.tx_bytes += skb->len;
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_xmit += rmnet_cause_wakeup(p);
#endif
}
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
((struct net_device *)(dev))->name, p->stats.tx_packets,
skb->len, skb->mark);
dev_kfree_skb_any(skb);
spin_lock_irqsave(&p->tx_queue_lock, flags);
if (netif_queue_stopped(dev) &&
msm_bam_dmux_is_ch_low(p->ch_id)) {
DBG0("%s: Low WM hit, waking queue=%p\n",
__func__, skb);
netif_wake_queue(dev);
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
}
static void bam_notify(void *dev, int event, unsigned long data)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
switch (event) {
case BAM_DMUX_RECEIVE:
bam_recv_notify(dev, (struct sk_buff *)(data));
break;
case BAM_DMUX_WRITE_DONE:
bam_write_done(dev, (struct sk_buff *)(data));
break;
case BAM_DMUX_UL_CONNECTED:
spin_lock_irqsave(&p->lock, flags);
if (p->waiting_for_ul_skb != NULL) {
struct sk_buff *skb;
int ret;
skb = p->waiting_for_ul_skb;
p->waiting_for_ul_skb = NULL;
spin_unlock_irqrestore(&p->lock, flags);
ret = _rmnet_xmit(skb, dev);
if (ret) {
pr_err("%s: error %d dropping delayed TX SKB %p\n",
__func__, ret, skb);
dev_kfree_skb_any(skb);
}
netif_wake_queue(dev);
} else {
spin_unlock_irqrestore(&p->lock, flags);
}
break;
case BAM_DMUX_UL_DISCONNECTED:
break;
}
}
static int __rmnet_open(struct net_device *dev)
{
int r;
struct rmnet_private *p = netdev_priv(dev);
DBG0("[%s] __rmnet_open()\n", dev->name);
if (!p->device_up) {
r = msm_bam_dmux_open(p->ch_id, dev, bam_notify);
if (r < 0) {
DBG0("%s: ch=%d failed with rc %d\n",
__func__, p->ch_id, r);
return -ENODEV;
}
}
p->device_up = DEVICE_ACTIVE;
return 0;
}
static int rmnet_open(struct net_device *dev)
{
int rc = 0;
DBG0("[%s] rmnet_open()\n", dev->name);
rc = __rmnet_open(dev);
if (rc == 0)
netif_start_queue(dev);
return rc;
}
static int __rmnet_close(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
int rc = 0;
if (p->device_up) {
/* do not close rmnet port once up, this causes
remote side to hang if tried to open again */
p->device_up = DEVICE_INACTIVE;
return rc;
} else
return -EBADF;
}
static int rmnet_stop(struct net_device *dev)
{
DBG0("[%s] rmnet_stop()\n", dev->name);
__rmnet_close(dev);
netif_stop_queue(dev);
return 0;
}
static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
{
if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
return -EINVAL;
DBG0("[%s] MTU change: old=%d new=%d\n",
dev->name, dev->mtu, new_mtu);
dev->mtu = new_mtu;
return 0;
}
static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
int awake;
int ret = 0;
if (netif_queue_stopped(dev)) {
pr_err("[%s]fatal: rmnet_xmit called when "
"netif_queue is stopped", dev->name);
return 0;
}
spin_lock_irqsave(&p->lock, flags);
awake = msm_bam_dmux_ul_power_vote();
if (!awake) {
/* send SKB once wakeup is complete */
netif_stop_queue(dev);
p->waiting_for_ul_skb = skb;
spin_unlock_irqrestore(&p->lock, flags);
ret = 0;
goto exit;
}
spin_unlock_irqrestore(&p->lock, flags);
ret = _rmnet_xmit(skb, dev);
if (ret == -EPERM) {
ret = NETDEV_TX_BUSY;
goto exit;
}
/*
* detected SSR a bit early. shut some things down now, and leave
* the rest to the main ssr handling code when that happens later
*/
if (ret == -EFAULT) {
netif_carrier_off(dev);
dev_kfree_skb_any(skb);
ret = 0;
goto exit;
}
if (ret == -EAGAIN) {
/*
* This should not happen
* EAGAIN means we attempted to overflow the high watermark
* Clearly the queue is not stopped like it should be, so
* stop it and return BUSY to the TCP/IP framework. It will
* retry this packet with the queue is restarted which happens
* in the write_done callback when the low watermark is hit.
*/
netif_stop_queue(dev);
ret = NETDEV_TX_BUSY;
goto exit;
}
spin_lock_irqsave(&p->tx_queue_lock, flags);
if (msm_bam_dmux_is_ch_full(p->ch_id)) {
netif_stop_queue(dev);
DBG0("%s: High WM hit, stopping queue=%p\n", __func__, skb);
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
exit:
msm_bam_dmux_ul_power_unvote();
return ret;
}
static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
return &p->stats;
}
static void rmnet_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
}
static const struct net_device_ops rmnet_ops_ether = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static const struct net_device_ops rmnet_ops_ip = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = 0,
.ndo_validate_addr = 0,
};
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct rmnet_private *p = netdev_priv(dev);
u32 old_opmode = p->operation_mode;
unsigned long flags;
int prev_mtu = dev->mtu;
int rc = 0;
/* Process IOCTL command */
switch (cmd) {
case RMNET_IOCTL_SET_LLP_ETHERNET: /* Set Ethernet protocol */
/* Perform Ethernet config only if in IP mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_IP) {
ether_setup(dev);
random_ether_addr(dev->dev_addr);
dev->mtu = prev_mtu;
dev->netdev_ops = &rmnet_ops_ether;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_IP;
p->operation_mode |= RMNET_MODE_LLP_ETH;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set Ethernet protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
/* Perform IP config only if in Ethernet mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_ETH) {
/* Undo config done in ether_setup() */
dev->header_ops = 0; /* No header */
dev->type = ARPHRD_RAWIP;
dev->hard_header_len = 0;
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST|
IFF_MULTICAST);
dev->needed_headroom = HEADROOM_FOR_BAM +
HEADROOM_FOR_QOS;
dev->needed_tailroom = TAILROOM;
dev->netdev_ops = &rmnet_ops_ip;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_ETH;
p->operation_mode |= RMNET_MODE_LLP_IP;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set IP protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode &
(RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
break;
case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode |= RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
dev->name);
break;
case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
dev->name);
break;
case RMNET_IOCTL_FLOW_ENABLE:
tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 1);
DBG0("[%s] rmnet_ioctl(): enabled flow", dev->name);
break;
case RMNET_IOCTL_FLOW_DISABLE:
tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 0);
DBG0("[%s] rmnet_ioctl(): disabled flow", dev->name);
break;
case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode & RMNET_MODE_QOS);
break;
case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
break;
case RMNET_IOCTL_OPEN: /* Open transport port */
rc = __rmnet_open(dev);
DBG0("[%s] rmnet_ioctl(): open transport port\n",
dev->name);
break;
case RMNET_IOCTL_CLOSE: /* Close transport port */
rc = __rmnet_close(dev);
DBG0("[%s] rmnet_ioctl(): close transport port\n",
dev->name);
break;
default:
pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
dev->name, cmd);
return -EINVAL;
}
DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
dev->name, __func__, cmd, old_opmode, p->operation_mode);
return rc;
}
static void __init rmnet_setup(struct net_device *dev)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_ops_ether;
ether_setup(dev);
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
dev->needed_headroom = HEADROOM_FOR_BAM + HEADROOM_FOR_QOS ;
dev->needed_tailroom = TAILROOM;
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
static struct net_device *netdevs[RMNET_DEVICE_COUNT];
static struct platform_driver bam_rmnet_drivers[RMNET_DEVICE_COUNT];
static int bam_rmnet_probe(struct platform_device *pdev)
{
int i;
char name[BAM_DMUX_CH_NAME_MAX_LEN];
struct rmnet_private *p;
for (i = 0; i < RMNET_DEVICE_COUNT; ++i) {
scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d", i);
if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
break;
}
p = netdev_priv(netdevs[i]);
if (p->in_reset) {
p->in_reset = 0;
msm_bam_dmux_open(p->ch_id, netdevs[i], bam_notify);
netif_carrier_on(netdevs[i]);
netif_start_queue(netdevs[i]);
}
return 0;
}
static int bam_rmnet_remove(struct platform_device *pdev)
{
int i;
char name[BAM_DMUX_CH_NAME_MAX_LEN];
struct rmnet_private *p;
for (i = 0; i < RMNET_DEVICE_COUNT; ++i) {
scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d", i);
if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
break;
}
p = netdev_priv(netdevs[i]);
p->in_reset = 1;
if (p->waiting_for_ul_skb != NULL) {
dev_kfree_skb_any(p->waiting_for_ul_skb);
p->waiting_for_ul_skb = NULL;
}
msm_bam_dmux_close(p->ch_id);
netif_carrier_off(netdevs[i]);
netif_stop_queue(netdevs[i]);
return 0;
}
/* support for 9 new rmnet ports */
#define RMNET_REV_DEVICE_COUNT (9)
static struct net_device *netdevs_rev[RMNET_REV_DEVICE_COUNT];
static struct platform_driver bam_rmnet_rev_drivers[RMNET_REV_DEVICE_COUNT];
static int bam_rmnet_rev_probe(struct platform_device *pdev)
{
int i;
char name[BAM_DMUX_CH_NAME_MAX_LEN];
struct rmnet_private *p;
for (i = 0; i < RMNET_REV_DEVICE_COUNT; ++i) {
scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
(i+BAM_DMUX_DATA_REV_RMNET_0));
if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
break;
}
if (i >= RMNET_REV_DEVICE_COUNT) {
pr_err("%s: wrong netdev %s\n", __func__, pdev->name);
return 0;
}
p = netdev_priv(netdevs_rev[i]);
if (p->in_reset) {
p->in_reset = 0;
msm_bam_dmux_open(p->ch_id, netdevs_rev[i], bam_notify);
netif_carrier_on(netdevs_rev[i]);
netif_start_queue(netdevs_rev[i]);
}
return 0;
}
static int bam_rmnet_rev_remove(struct platform_device *pdev)
{
int i;
char name[BAM_DMUX_CH_NAME_MAX_LEN];
struct rmnet_private *p;
for (i = 0; i < RMNET_REV_DEVICE_COUNT; ++i) {
scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
(i+BAM_DMUX_DATA_REV_RMNET_0));
if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
break;
}
if (i >= RMNET_REV_DEVICE_COUNT) {
pr_err("%s: wrong netdev %s\n", __func__, pdev->name);
return 0;
}
p = netdev_priv(netdevs_rev[i]);
p->in_reset = 1;
if (p->waiting_for_ul_skb != NULL) {
dev_kfree_skb_any(p->waiting_for_ul_skb);
p->waiting_for_ul_skb = NULL;
}
msm_bam_dmux_close(p->ch_id);
netif_carrier_off(netdevs_rev[i]);
netif_stop_queue(netdevs_rev[i]);
return 0;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
#ifdef CONFIG_HAS_EARLYSUSPEND
static int rmnet_debug_init_timeout_suspend(struct net_device *dev)
{
struct device *d;
d = &(dev->dev);
return device_create_file(d, &dev_attr_timeout_suspend);
}
#else
static int rmnet_debug_init_timeout_suspend(struct net_device *dev)
{
return 0;
}
#endif
static int rmnet_debug_init(struct net_device *dev)
{
struct device *d;
struct rmnet_private *p;
int err = 0;
d = &(dev->dev);
p = netdev_priv(dev);
p->timeout_us = 0;
p->wakeups_xmit = p->wakeups_rcv = 0;
err = device_create_file(d, &dev_attr_timeout);
if (err)
return err;
err = device_create_file(d, &dev_attr_wakeups_xmit);
if (err)
return err;
err = device_create_file(d, &dev_attr_wakeups_rcv);
if (err)
return err;
err = rmnet_debug_init_timeout_suspend(dev);
return err;
}
#else
static int rmnet_debug_init(struct net_device *dev)
{
return 0;
}
#endif
static int __init rmnet_init(void)
{
int ret;
struct device *d;
struct net_device *dev;
struct rmnet_private *p;
unsigned n;
char *tempname;
pr_info("%s: BAM devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
#ifdef CONFIG_MSM_RMNET_DEBUG
timeout_us = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
timeout_suspend_us = 0;
#endif
#endif
for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct rmnet_private),
"rmnet%d", rmnet_setup);
if (!dev) {
pr_err("%s: no memory for netdev %d\n", __func__, n);
return -ENOMEM;
}
netdevs[n] = dev;
d = &(dev->dev);
p = netdev_priv(dev);
/* Initial config uses Ethernet */
p->operation_mode = RMNET_MODE_LLP_ETH;
p->ch_id = n;
p->waiting_for_ul_skb = NULL;
p->in_reset = 0;
spin_lock_init(&p->lock);
spin_lock_init(&p->tx_queue_lock);
#ifdef CONFIG_MSM_RMNET_DEBUG
p->timeout_us = timeout_us;
p->wakeups_xmit = p->wakeups_rcv = 0;
#endif
ret = register_netdev(dev);
if (ret) {
pr_err("%s: unable to register netdev"
" %d rc=%d\n", __func__, n, ret);
free_netdev(dev);
return ret;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
if (device_create_file(d, &dev_attr_timeout))
continue;
if (device_create_file(d, &dev_attr_wakeups_xmit))
continue;
if (device_create_file(d, &dev_attr_wakeups_rcv))
continue;
#ifdef CONFIG_HAS_EARLYSUSPEND
if (device_create_file(d, &dev_attr_timeout_suspend))
continue;
/* Only care about rmnet0 for suspend/resume tiemout hooks. */
if (n == 0)
rmnet0 = d;
#endif
#endif
bam_rmnet_drivers[n].probe = bam_rmnet_probe;
bam_rmnet_drivers[n].remove = bam_rmnet_remove;
tempname = kmalloc(BAM_DMUX_CH_NAME_MAX_LEN, GFP_KERNEL);
if (tempname == NULL)
return -ENOMEM;
scnprintf(tempname, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
n);
bam_rmnet_drivers[n].driver.name = tempname;
bam_rmnet_drivers[n].driver.owner = THIS_MODULE;
ret = platform_driver_register(&bam_rmnet_drivers[n]);
if (ret) {
pr_err("%s: registration failed n=%d rc=%d\n",
__func__, n, ret);
return ret;
}
}
/*Support for new rmnet ports */
for (n = 0; n < RMNET_REV_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct rmnet_private),
"rev_rmnet%d", rmnet_setup);
if (!dev) {
pr_err("%s: no memory for rev netdev %d\n",
__func__, n);
return -ENOMEM;
}
netdevs_rev[n] = dev;
d = &(dev->dev);
p = netdev_priv(dev);
/* Initial config uses Ethernet */
p->operation_mode = RMNET_MODE_LLP_ETH;
p->ch_id = n+BAM_DMUX_DATA_REV_RMNET_0;
p->waiting_for_ul_skb = NULL;
p->in_reset = 0;
spin_lock_init(&p->lock);
spin_lock_init(&p->tx_queue_lock);
ret = register_netdev(dev);
if (ret) {
pr_err("%s: unable to register rev netdev %d rc=%d\n",
__func__, n, ret);
free_netdev(dev);
return ret;
}
if (rmnet_debug_init(dev))
continue;
bam_rmnet_rev_drivers[n].probe = bam_rmnet_rev_probe;
bam_rmnet_rev_drivers[n].remove = bam_rmnet_rev_remove;
tempname = kmalloc(BAM_DMUX_CH_NAME_MAX_LEN, GFP_KERNEL);
if (tempname == NULL)
return -ENOMEM;
scnprintf(tempname, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
(n+BAM_DMUX_DATA_REV_RMNET_0));
bam_rmnet_rev_drivers[n].driver.name = tempname;
bam_rmnet_rev_drivers[n].driver.owner = THIS_MODULE;
ret = platform_driver_register(&bam_rmnet_rev_drivers[n]);
if (ret) {
pr_err("%s: new rev driver registration failed n=%d rc=%d\n",
__func__, n, ret);
return ret;
}
}
return 0;
}
module_init(rmnet_init);
MODULE_DESCRIPTION("MSM RMNET BAM TRANSPORT");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,712 @@
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*
*/
/*
* RMNET SDIO Module.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
#include <linux/if_arp.h>
#include <linux/msm_rmnet.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <mach/sdio_dmux.h>
/* Debug message support */
static int msm_rmnet_sdio_debug_mask;
module_param_named(debug_enable, msm_rmnet_sdio_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
#define DEBUG_MASK_LVL0 (1U << 0)
#define DEBUG_MASK_LVL1 (1U << 1)
#define DEBUG_MASK_LVL2 (1U << 2)
#define DBG(m, x...) do { \
if (msm_rmnet_sdio_debug_mask & m) \
pr_info(x); \
} while (0)
#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
/* Configure device instances */
#define RMNET_DEVICE_COUNT (8)
/* allow larger frames */
#define RMNET_DATA_LEN 2000
#define DEVICE_ID_INVALID -1
#define DEVICE_INACTIVE 0
#define DEVICE_ACTIVE 1
#define HEADROOM_FOR_SDIO 8 /* for mux header */
#define HEADROOM_FOR_QOS 8
#define TAILROOM 8 /* for padding by mux layer */
struct rmnet_private {
struct net_device_stats stats;
uint32_t ch_id;
#ifdef CONFIG_MSM_RMNET_DEBUG
ktime_t last_packet;
unsigned long wakeups_xmit;
unsigned long wakeups_rcv;
unsigned long timeout_us;
#endif
struct sk_buff *skb;
spinlock_t lock;
spinlock_t tx_queue_lock;
struct tasklet_struct tsklt;
u32 operation_mode; /* IOCTL specified mode (protocol, QoS header) */
uint8_t device_up;
uint8_t in_reset;
};
#ifdef CONFIG_MSM_RMNET_DEBUG
static unsigned long timeout_us;
#ifdef CONFIG_HAS_EARLYSUSPEND
/*
* If early suspend is enabled then we specify two timeout values,
* screen on (default), and screen is off.
*/
static unsigned long timeout_suspend_us;
static struct device *rmnet0;
/* Set timeout in us when the screen is off. */
static ssize_t timeout_suspend_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t n)
{
timeout_suspend_us = strict_strtoul(buf, NULL, 10);
return n;
}
static ssize_t timeout_suspend_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us);
}
static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
timeout_suspend_store);
static void rmnet_early_suspend(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_suspend_us;
}
}
static void rmnet_late_resume(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_us;
}
}
static struct early_suspend rmnet_power_suspend = {
.suspend = rmnet_early_suspend,
.resume = rmnet_late_resume,
};
static int __init rmnet_late_init(void)
{
register_early_suspend(&rmnet_power_suspend);
return 0;
}
late_initcall(rmnet_late_init);
#endif
/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
static int rmnet_cause_wakeup(struct rmnet_private *p)
{
int ret = 0;
ktime_t now;
if (p->timeout_us == 0) /* Check if disabled */
return 0;
/* Use real (wall) time. */
now = ktime_get_real();
if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
ret = 1;
p->last_packet = now;
return ret;
}
static ssize_t wakeups_xmit_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_xmit);
}
DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", p->wakeups_rcv);
}
DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
/* Set timeout in us. */
static ssize_t timeout_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t n)
{
#ifndef CONFIG_HAS_EARLYSUSPEND
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
#else
/* If using early suspend/resume hooks do not write the value on store. */
timeout_us = strict_strtoul(buf, NULL, 10);
#endif
return n;
}
static ssize_t timeout_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p = netdev_priv(to_net_dev(d));
return sprintf(buf, "%lu\n", timeout_us);
}
DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
#endif
/* Forward declaration */
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
{
__be16 protocol = 0;
skb->dev = dev;
/* Determine L3 protocol */
switch (skb->data[0] & 0xf0) {
case 0x40:
protocol = htons(ETH_P_IP);
break;
case 0x60:
protocol = htons(ETH_P_IPV6);
break;
default:
pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
dev->name, skb->data[0] & 0xf0);
/* skb will be dropped in upper layer for unknown protocol */
}
return protocol;
}
static int count_this_packet(void *_hdr, int len)
{
struct ethhdr *hdr = _hdr;
if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
return 0;
return 1;
}
static int sdio_update_reset_state(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
int new_state;
new_state = msm_sdio_is_channel_in_reset(p->ch_id);
if (p->in_reset != new_state) {
p->in_reset = (uint8_t)new_state;
if (p->in_reset)
netif_carrier_off(dev);
else
netif_carrier_on(dev);
return 1;
}
return 0;
}
/* Rx Callback, Called in Work Queue context */
static void sdio_recv_notify(void *dev, struct sk_buff *skb)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
u32 opmode;
if (skb) {
skb->dev = dev;
/* Handle Rx frame format */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_IP(opmode)) {
/* Driver in IP mode */
skb->protocol = rmnet_ip_type_trans(skb, dev);
} else {
/* Driver in Ethernet mode */
skb->protocol = eth_type_trans(skb, dev);
}
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_rcv += rmnet_cause_wakeup(p);
#endif
p->stats.rx_packets++;
p->stats.rx_bytes += skb->len;
}
DBG1("[%s] Rx packet #%lu len=%d\n",
((struct net_device *)dev)->name,
p->stats.rx_packets, skb->len);
/* Deliver to network stack */
netif_rx(skb);
} else {
spin_lock_irqsave(&p->lock, flags);
if (!sdio_update_reset_state((struct net_device *)dev))
pr_err("[%s] %s: No skb received",
((struct net_device *)dev)->name, __func__);
spin_unlock_irqrestore(&p->lock, flags);
}
}
static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
int sdio_ret;
struct QMI_QOS_HDR_S *qmih;
u32 opmode;
unsigned long flags;
if (!netif_carrier_ok(dev)) {
pr_err("[%s] %s: channel in reset",
dev->name, __func__);
goto xmit_out;
}
/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_QOS(opmode)) {
qmih = (struct QMI_QOS_HDR_S *)
skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
qmih->version = 1;
qmih->flags = 0;
qmih->flow_id = skb->mark;
}
dev->trans_start = jiffies;
sdio_ret = msm_sdio_dmux_write(p->ch_id, skb);
if (sdio_ret != 0) {
pr_err("[%s] %s: write returned error %d",
dev->name, __func__, sdio_ret);
goto xmit_out;
}
if (count_this_packet(skb->data, skb->len)) {
p->stats.tx_packets++;
p->stats.tx_bytes += skb->len;
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_xmit += rmnet_cause_wakeup(p);
#endif
}
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
dev->name, p->stats.tx_packets, skb->len, skb->mark);
return 0;
xmit_out:
dev_kfree_skb_any(skb);
p->stats.tx_errors++;
return 0;
}
static void sdio_write_done(void *dev, struct sk_buff *skb)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
if (skb)
dev_kfree_skb_any(skb);
if (!p->in_reset) {
DBG1("%s: write complete skb=%p\n", __func__, skb);
spin_lock_irqsave(&p->tx_queue_lock, flags);
if (netif_queue_stopped(dev) &&
msm_sdio_dmux_is_ch_low(p->ch_id)) {
DBG0("%s: Low WM hit, waking queue=%p\n",
__func__, skb);
netif_wake_queue(dev);
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
} else {
DBG1("%s: write in reset skb=%p\n", __func__, skb);
}
}
static int __rmnet_open(struct net_device *dev)
{
int r;
struct rmnet_private *p = netdev_priv(dev);
DBG0("[%s] __rmnet_open()\n", dev->name);
if (!p->device_up) {
r = msm_sdio_dmux_open(p->ch_id, dev,
sdio_recv_notify, sdio_write_done);
if (r < 0)
return -ENODEV;
}
p->device_up = DEVICE_ACTIVE;
return 0;
}
static int rmnet_open(struct net_device *dev)
{
int rc = 0;
DBG0("[%s] rmnet_open()\n", dev->name);
rc = __rmnet_open(dev);
if (rc == 0)
netif_start_queue(dev);
return rc;
}
static int __rmnet_close(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
int rc = 0;
if (p->device_up) {
/* do not close rmnet port once up, this causes
remote side to hang if tried to open again */
/* rc = msm_sdio_dmux_close(p->ch_id); */
p->device_up = DEVICE_INACTIVE;
return rc;
} else
return -EBADF;
}
static int rmnet_stop(struct net_device *dev)
{
DBG0("[%s] rmnet_stop()\n", dev->name);
__rmnet_close(dev);
netif_stop_queue(dev);
return 0;
}
static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
{
if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
return -EINVAL;
DBG0("[%s] MTU change: old=%d new=%d\n",
dev->name, dev->mtu, new_mtu);
dev->mtu = new_mtu;
return 0;
}
static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
if (netif_queue_stopped(dev)) {
pr_err("[%s]fatal: rmnet_xmit called when "
"netif_queue is stopped", dev->name);
return 0;
}
_rmnet_xmit(skb, dev);
spin_lock_irqsave(&p->tx_queue_lock, flags);
if (msm_sdio_dmux_is_ch_full(p->ch_id)) {
netif_stop_queue(dev);
DBG0("%s: High WM hit, stopping queue=%p\n", __func__, skb);
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
return 0;
}
static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
return &p->stats;
}
static void rmnet_set_multicast_list(struct net_device *dev)
{
}
static void rmnet_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
}
static const struct net_device_ops rmnet_ops_ether = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_set_rx_mode = rmnet_set_multicast_list,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static const struct net_device_ops rmnet_ops_ip = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_set_rx_mode = rmnet_set_multicast_list,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = 0,
.ndo_validate_addr = 0,
};
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct rmnet_private *p = netdev_priv(dev);
u32 old_opmode = p->operation_mode;
unsigned long flags;
int prev_mtu = dev->mtu;
int rc = 0;
/* Process IOCTL command */
switch (cmd) {
case RMNET_IOCTL_SET_LLP_ETHERNET: /* Set Ethernet protocol */
/* Perform Ethernet config only if in IP mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_IP) {
ether_setup(dev);
random_ether_addr(dev->dev_addr);
dev->mtu = prev_mtu;
dev->netdev_ops = &rmnet_ops_ether;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_IP;
p->operation_mode |= RMNET_MODE_LLP_ETH;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set Ethernet protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
/* Perform IP config only if in Ethernet mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_ETH) {
/* Undo config done in ether_setup() */
dev->header_ops = 0; /* No header */
dev->type = ARPHRD_RAWIP;
dev->hard_header_len = 0;
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST|
IFF_MULTICAST);
dev->needed_headroom = HEADROOM_FOR_SDIO +
HEADROOM_FOR_QOS;
dev->needed_tailroom = TAILROOM;
dev->netdev_ops = &rmnet_ops_ip;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_ETH;
p->operation_mode |= RMNET_MODE_LLP_IP;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set IP protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode &
(RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
break;
case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode |= RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
dev->name);
break;
case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
dev->name);
break;
case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode & RMNET_MODE_QOS);
break;
case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
break;
case RMNET_IOCTL_OPEN: /* Open transport port */
rc = __rmnet_open(dev);
DBG0("[%s] rmnet_ioctl(): open transport port\n",
dev->name);
break;
case RMNET_IOCTL_CLOSE: /* Close transport port */
rc = __rmnet_close(dev);
DBG0("[%s] rmnet_ioctl(): close transport port\n",
dev->name);
break;
default:
pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
dev->name, cmd);
return -EINVAL;
}
DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
dev->name, __func__, cmd, old_opmode, p->operation_mode);
return rc;
}
static void __init rmnet_setup(struct net_device *dev)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_ops_ether;
ether_setup(dev);
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
dev->needed_headroom = HEADROOM_FOR_SDIO + HEADROOM_FOR_QOS ;
dev->needed_tailroom = TAILROOM;
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
static int __init rmnet_init(void)
{
int ret;
struct device *d;
struct net_device *dev;
struct rmnet_private *p;
unsigned n;
pr_info("%s: SDIO devices[%d]\n", __func__, RMNET_DEVICE_COUNT);
#ifdef CONFIG_MSM_RMNET_DEBUG
timeout_us = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
timeout_suspend_us = 0;
#endif
#endif
for (n = 0; n < RMNET_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct rmnet_private),
"rmnet_sdio%d", rmnet_setup);
if (!dev)
return -ENOMEM;
d = &(dev->dev);
p = netdev_priv(dev);
/* Initial config uses Ethernet */
p->operation_mode = RMNET_MODE_LLP_ETH;
p->ch_id = n;
spin_lock_init(&p->lock);
spin_lock_init(&p->tx_queue_lock);
#ifdef CONFIG_MSM_RMNET_DEBUG
p->timeout_us = timeout_us;
p->wakeups_xmit = p->wakeups_rcv = 0;
#endif
ret = register_netdev(dev);
if (ret) {
free_netdev(dev);
return ret;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
if (device_create_file(d, &dev_attr_timeout))
continue;
if (device_create_file(d, &dev_attr_wakeups_xmit))
continue;
if (device_create_file(d, &dev_attr_wakeups_rcv))
continue;
#ifdef CONFIG_HAS_EARLYSUSPEND
if (device_create_file(d, &dev_attr_timeout_suspend))
continue;
/* Only care about rmnet0 for suspend/resume tiemout hooks. */
if (n == 0)
rmnet0 = d;
#endif
#endif
}
return 0;
}
module_init(rmnet_init);
MODULE_DESCRIPTION("MSM RMNET SDIO TRANSPORT");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,933 @@
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*
*/
/*
* RMNET SMUX Module.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
#include <linux/if_arp.h>
#include <linux/msm_rmnet.h>
#include <linux/platform_device.h>
#include <linux/smux.h>
#include <linux/ip.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
/* Debug message support */
static int msm_rmnet_smux_debug_mask;
module_param_named(debug_enable, msm_rmnet_smux_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
#define DEBUG_MASK_LVL0 (1U << 0)
#define DEBUG_MASK_LVL1 (1U << 1)
#define DEBUG_MASK_LVL2 (1U << 2)
#define DBG(m, x...) do { \
if (msm_rmnet_smux_debug_mask & m) \
pr_info(x); \
} while (0)
#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
/* Configure device instances */
#define RMNET_SMUX_DEVICE_COUNT (2)
/* allow larger frames */
#define RMNET_DATA_LEN 2000
#define DEVICE_ID_INVALID -1
#define DEVICE_INACTIVE 0x00
#define DEVICE_ACTIVE 0x01
#define HEADROOM_FOR_SMUX 8 /* for mux header */
#define HEADROOM_FOR_QOS 8
#define TAILROOM 8 /* for padding by mux layer */
struct rmnet_private {
struct net_device_stats stats;
uint32_t ch_id;
#ifdef CONFIG_MSM_RMNET_DEBUG
ktime_t last_packet;
unsigned long wakeups_xmit;
unsigned long wakeups_rcv;
unsigned long timeout_us;
#endif
spinlock_t lock;
spinlock_t tx_queue_lock;
struct tasklet_struct tsklt;
/* IOCTL specified mode (protocol, QoS header) */
u32 operation_mode;
uint8_t device_state;
uint8_t in_reset;
};
static struct net_device *netdevs[RMNET_SMUX_DEVICE_COUNT];
#ifdef CONFIG_MSM_RMNET_DEBUG
static unsigned long timeout_us;
#ifdef CONFIG_HAS_EARLYSUSPEND
/*
* If early suspend is enabled then we specify two timeout values,
* screen on (default), and screen is off.
*/
static unsigned long timeout_suspend_us;
static struct device *rmnet0;
/* Set timeout in us when the screen is off. */
static ssize_t timeout_suspend_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t n)
{
timeout_suspend_us = strict_strtoul(buf, NULL, 10);
return n;
}
static ssize_t timeout_suspend_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%lu\n",
(unsigned long) timeout_suspend_us);
}
static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
timeout_suspend_store);
static void rmnet_early_suspend(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_suspend_us;
}
}
static void rmnet_late_resume(struct early_suspend *handler)
{
if (rmnet0) {
struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
p->timeout_us = timeout_us;
}
}
static struct early_suspend rmnet_power_suspend = {
.suspend = rmnet_early_suspend,
.resume = rmnet_late_resume,
};
static int __init rmnet_late_init(void)
{
register_early_suspend(&rmnet_power_suspend);
return 0;
}
late_initcall(rmnet_late_init);
#endif /* CONFIG_HAS_EARLYSUSPEND */
/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
static int rmnet_cause_wakeup(struct rmnet_private *p)
{
int ret = 0;
ktime_t now;
if (p->timeout_us == 0) /* Check if disabled */
return 0;
/* Use real (wall) time. */
now = ktime_get_real();
if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
ret = 1;
p->last_packet = now;
return ret;
}
static ssize_t wakeups_xmit_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_xmit);
}
DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
static ssize_t wakeups_rcv_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_rcv);
}
DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
/* Set timeout in us. */
static ssize_t timeout_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t n)
{
#ifndef CONFIG_HAS_EARLYSUSPEND
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
#else
/* If using early suspend/resume hooks do not write the value on store. */
timeout_us = strict_strtoul(buf, NULL, 10);
#endif /* CONFIG_HAS_EARLYSUSPEND */
return n;
}
static ssize_t timeout_show(struct device *d,
struct device_attribute *attr,
char *buf)
{
struct rmnet_private *p = netdev_priv(to_net_dev(d));
p = netdev_priv(to_net_dev(d));
return snprintf(buf, PAGE_SIZE, "%lu\n", timeout_us);
}
DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
#endif /* CONFIG_MSM_RMNET_DEBUG */
/* Forward declaration */
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int count_this_packet(void *_hdr, int len)
{
struct ethhdr *hdr = _hdr;
if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
return 0;
return 1;
}
static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
{
__be16 protocol = 0;
skb->dev = dev;
/* Determine L3 protocol */
switch (skb->data[0] & 0xf0) {
case 0x40:
protocol = htons(ETH_P_IP);
break;
case 0x60:
protocol = htons(ETH_P_IPV6);
break;
default:
pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
dev->name, skb->data[0] & 0xf0);
/* skb will be dropped in upper layer for unknown protocol */
}
return protocol;
}
static void smux_read_done(void *rcv_dev, const void *meta_data)
{
struct rmnet_private *p;
struct net_device *dev = rcv_dev;
u32 opmode;
unsigned long flags;
struct sk_buff *skb = NULL;
const struct smux_meta_read *read_meta_info = meta_data;
if (!dev || !read_meta_info) {
DBG1("%s:invalid read_done callback recieved", __func__);
return;
}
p = netdev_priv(dev);
skb = (struct sk_buff *) read_meta_info->pkt_priv;
if (!skb || skb->dev != dev) {
DBG1("%s: ERR:skb pointer NULL in READ_DONE CALLBACK",
__func__);
return;
}
/* Handle Rx frame format */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_IP(opmode)) {
/* Driver in IP mode */
skb->protocol =
rmnet_ip_type_trans(skb, dev);
} else {
/* Driver in Ethernet mode */
skb->protocol =
eth_type_trans(skb, dev);
}
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_rcv +=
rmnet_cause_wakeup(p);
#endif
p->stats.rx_packets++;
p->stats.rx_bytes += skb->len;
}
DBG2("[%s] Rx packet #%lu len=%d\n",
dev->name, p->stats.rx_packets,
skb->len);
/* Deliver to network stack */
netif_rx(skb);
return;
}
static void smux_write_done(void *dev, const void *meta_data)
{
struct rmnet_private *p = netdev_priv(dev);
u32 opmode;
struct sk_buff *skb = NULL;
const struct smux_meta_write *write_meta_info = meta_data;
unsigned long flags;
if (!dev || !write_meta_info) {
DBG1("%s: ERR:invalid WRITE_DONE callback recieved", __func__);
return;
}
skb = (struct sk_buff *) write_meta_info->pkt_priv;
if (!skb) {
DBG1("%s: ERR:skb pointer NULL in WRITE_DONE"
" CALLBACK", __func__);
return;
}
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
DBG1("%s: write complete\n", __func__);
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
p->stats.tx_packets++;
p->stats.tx_bytes += skb->len;
#ifdef CONFIG_MSM_RMNET_DEBUG
p->wakeups_xmit += rmnet_cause_wakeup(p);
#endif
}
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
((struct net_device *)(dev))->name, p->stats.tx_packets,
skb->len, skb->mark);
dev_kfree_skb_any(skb);
spin_lock_irqsave(&p->tx_queue_lock, flags);
if (netif_queue_stopped(dev) &&
msm_smux_is_ch_low(p->ch_id)) {
DBG0("%s: Low WM hit, waking queue=%p\n",
__func__, skb);
netif_wake_queue(dev);
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
}
void rmnet_smux_notify(void *priv, int event_type, const void *metadata)
{
struct rmnet_private *p;
struct net_device *dev;
unsigned long flags;
struct sk_buff *skb = NULL;
u32 opmode;
const struct smux_meta_disconnected *ssr_info;
const struct smux_meta_read *read_meta_info;
const struct smux_meta_write *write_meta_info = metadata;
if (!priv)
DBG0("%s: priv(cookie) NULL, ignoring notification:"
" %d\n", __func__, event_type);
switch (event_type) {
case SMUX_CONNECTED:
p = netdev_priv(priv);
dev = priv;
DBG0("[%s] SMUX_CONNECTED event dev:%s\n", __func__, dev->name);
netif_carrier_on(dev);
netif_start_queue(dev);
spin_lock_irqsave(&p->lock, flags);
p->device_state = DEVICE_ACTIVE;
spin_unlock_irqrestore(&p->lock, flags);
break;
case SMUX_DISCONNECTED:
p = netdev_priv(priv);
dev = priv;
ssr_info = metadata;
DBG0("[%s] SMUX_DISCONNECTED event dev:%s\n",
__func__, dev->name);
if (ssr_info && ssr_info->is_ssr == 1)
DBG0("SSR detected on :%s\n", dev->name);
netif_carrier_off(dev);
netif_stop_queue(dev);
spin_lock_irqsave(&p->lock, flags);
p->device_state = DEVICE_INACTIVE;
spin_unlock_irqrestore(&p->lock, flags);
break;
case SMUX_READ_DONE:
smux_read_done(priv, metadata);
break;
case SMUX_READ_FAIL:
p = netdev_priv(priv);
dev = priv;
read_meta_info = metadata;
if (!dev || !read_meta_info) {
DBG1("%s: ERR:invalid read failed callback"
" recieved", __func__);
return;
}
skb = (struct sk_buff *) read_meta_info->pkt_priv;
if (!skb) {
DBG1("%s: ERR:skb pointer NULL in read fail"
" CALLBACK", __func__);
return;
}
DBG0("%s: read failed\n", __func__);
opmode = p->operation_mode;
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len))
p->stats.rx_dropped++;
dev_kfree_skb_any(skb);
break;
case SMUX_WRITE_DONE:
smux_write_done(priv, metadata);
break;
case SMUX_WRITE_FAIL:
p = netdev_priv(priv);
dev = priv;
write_meta_info = metadata;
if (!dev || !write_meta_info) {
DBG1("%s: ERR:invalid WRITE_DONE"
"callback recieved", __func__);
return;
}
skb = (struct sk_buff *) write_meta_info->pkt_priv;
if (!skb) {
DBG1("%s: ERR:skb pointer NULL in"
" WRITE_DONE CALLBACK", __func__);
return;
}
DBG0("%s: write failed\n", __func__);
opmode = p->operation_mode;
if (RMNET_IS_MODE_IP(opmode) ||
count_this_packet(skb->data, skb->len)) {
p->stats.tx_dropped++;
}
dev_kfree_skb_any(skb);
break;
case SMUX_LOW_WM_HIT:
dev = priv;
p = netdev_priv(priv);
DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
spin_lock_irqsave(&p->tx_queue_lock, flags);
netif_wake_queue(dev);
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
break;
case SMUX_HIGH_WM_HIT:
dev = priv;
p = netdev_priv(priv);
DBG0("[%s] High WM hit dev:%s\n", __func__, dev->name);
spin_lock_irqsave(&p->tx_queue_lock, flags);
netif_stop_queue(dev);
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
break;
default:
dev = priv;
DBG0("[%s] Invalid event:%d received on"
" dev: %s\n", __func__, event_type, dev->name);
break;
}
return;
}
int get_rx_buffers(void *priv, void **pkt_priv, void **buffer, int size)
{
struct net_device *dev = (struct net_device *) priv;
struct sk_buff *skb = NULL;
void *ptr = NULL;
DBG0("[%s] dev:%s\n", __func__, dev->name);
skb = __dev_alloc_skb(size, GFP_ATOMIC);
if (skb == NULL) {
DBG0("%s: unable to alloc skb\n", __func__);
return -ENOMEM;
}
/* TODO skb_reserve(skb, NET_IP_ALIGN); for ethernet mode */
/* Populate some params now. */
skb->dev = dev;
ptr = skb_put(skb, size);
skb_set_network_header(skb, 0);
/* done with skb setup, return the buffer pointer. */
*pkt_priv = skb;
*buffer = ptr;
return 0;
}
static int __rmnet_open(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
DBG0("[%s] __rmnet_open()\n", dev->name);
if (p->device_state == DEVICE_ACTIVE) {
return 0;
} else {
DBG0("[%s] Platform inactive\n", dev->name);
return -ENODEV;
}
}
static int rmnet_open(struct net_device *dev)
{
int rc = 0;
DBG0("[%s] rmnet_open()\n", dev->name);
rc = __rmnet_open(dev);
if (rc == 0)
netif_start_queue(dev);
return rc;
}
static int rmnet_stop(struct net_device *dev)
{
DBG0("[%s] rmnet_stop()\n", dev->name);
netif_stop_queue(dev);
return 0;
}
static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
{
if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
return -EINVAL;
DBG0("[%s] MTU change: old=%d new=%d\n",
dev->name, dev->mtu, new_mtu);
dev->mtu = new_mtu;
return 0;
}
static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
struct QMI_QOS_HDR_S *qmih;
u32 opmode;
unsigned long flags;
/* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
spin_lock_irqsave(&p->lock, flags);
opmode = p->operation_mode;
spin_unlock_irqrestore(&p->lock, flags);
if (RMNET_IS_MODE_QOS(opmode)) {
qmih = (struct QMI_QOS_HDR_S *)
skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
qmih->version = 1;
qmih->flags = 0;
qmih->flow_id = skb->mark;
}
dev->trans_start = jiffies;
return msm_smux_write(p->ch_id, skb, skb->data, skb->len);
}
static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
unsigned long flags;
int ret = 0;
if (netif_queue_stopped(dev) || (p->device_state == DEVICE_INACTIVE)) {
pr_err("[%s]fatal: rmnet_xmit called when "
"netif_queue is stopped", dev->name);
return 0;
}
spin_lock_irqsave(&p->tx_queue_lock, flags);
ret = _rmnet_xmit(skb, dev);
if (ret == -EAGAIN) {
/*
* EAGAIN means we attempted to overflow the high watermark
* Clearly the queue is not stopped like it should be, so
* stop it and return BUSY to the TCP/IP framework. It will
* retry this packet with the queue is restarted which happens
* low watermark is called.
*/
netif_stop_queue(dev);
ret = NETDEV_TX_BUSY;
}
spin_unlock_irqrestore(&p->tx_queue_lock, flags);
return ret;
}
static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
{
struct rmnet_private *p = netdev_priv(dev);
return &p->stats;
}
static void rmnet_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
}
static const struct net_device_ops rmnet_ops_ether = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
static const struct net_device_ops rmnet_ops_ip = {
.ndo_open = rmnet_open,
.ndo_stop = rmnet_stop,
.ndo_start_xmit = rmnet_xmit,
.ndo_get_stats = rmnet_get_stats,
.ndo_tx_timeout = rmnet_tx_timeout,
.ndo_do_ioctl = rmnet_ioctl,
.ndo_change_mtu = rmnet_change_mtu,
.ndo_set_mac_address = 0,
.ndo_validate_addr = 0,
};
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct rmnet_private *p = netdev_priv(dev);
u32 old_opmode = p->operation_mode;
unsigned long flags;
int prev_mtu = dev->mtu;
int rc = 0;
/* Process IOCTL command */
switch (cmd) {
case RMNET_IOCTL_SET_LLP_ETHERNET: /* Set Ethernet protocol */
/* Perform Ethernet config only if in IP mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_IP) {
ether_setup(dev);
random_ether_addr(dev->dev_addr);
dev->mtu = prev_mtu;
dev->netdev_ops = &rmnet_ops_ether;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_IP;
p->operation_mode |= RMNET_MODE_LLP_ETH;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set Ethernet protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
/* Perform IP config only if in Ethernet mode currently*/
if (p->operation_mode & RMNET_MODE_LLP_ETH) {
/* Undo config done in ether_setup() */
dev->header_ops = 0; /* No header */
dev->type = ARPHRD_RAWIP;
dev->hard_header_len = 0;
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST |
IFF_MULTICAST);
dev->needed_headroom = HEADROOM_FOR_SMUX +
HEADROOM_FOR_QOS;
dev->needed_tailroom = TAILROOM;
dev->netdev_ops = &rmnet_ops_ip;
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_LLP_ETH;
p->operation_mode |= RMNET_MODE_LLP_IP;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): "
"set IP protocol mode\n",
dev->name);
}
break;
case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode &
(RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
break;
case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode |= RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
dev->name);
break;
case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
spin_lock_irqsave(&p->lock, flags);
p->operation_mode &= ~RMNET_MODE_QOS;
spin_unlock_irqrestore(&p->lock, flags);
DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
dev->name);
break;
case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
ifr->ifr_ifru.ifru_data =
(void *)(p->operation_mode & RMNET_MODE_QOS);
break;
case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
break;
case RMNET_IOCTL_OPEN: /* Open transport port */
rc = __rmnet_open(dev);
DBG0("[%s] rmnet_ioctl(): open transport port\n",
dev->name);
break;
case RMNET_IOCTL_CLOSE: /* Close transport port */
DBG0("[%s] rmnet_ioctl(): close transport port\n",
dev->name);
break;
default:
pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
dev->name, cmd);
return -EINVAL;
}
DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
dev->name, __func__, cmd, old_opmode, p->operation_mode);
return rc;
}
static void __init rmnet_setup(struct net_device *dev)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_ops_ether;
ether_setup(dev);
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
dev->needed_headroom = HEADROOM_FOR_SMUX + HEADROOM_FOR_QOS ;
dev->needed_tailroom = TAILROOM;
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
static int smux_rmnet_probe(struct platform_device *pdev)
{
int i;
int r;
struct rmnet_private *p;
for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
p = netdev_priv(netdevs[i]);
if (p != NULL) {
r = msm_smux_open(p->ch_id,
netdevs[i],
rmnet_smux_notify,
get_rx_buffers);
if (r < 0) {
DBG0("%s: ch=%d open failed with rc %d\n",
__func__, p->ch_id, r);
}
}
}
return 0;
}
static int smux_rmnet_remove(struct platform_device *pdev)
{
int i;
int r;
struct rmnet_private *p;
for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
p = netdev_priv(netdevs[i]);
if (p != NULL) {
r = msm_smux_close(p->ch_id);
if (r < 0) {
DBG0("%s: ch=%d close failed with rc %d\n",
__func__, p->ch_id, r);
continue;
}
netif_carrier_off(netdevs[i]);
netif_stop_queue(netdevs[i]);
}
}
return 0;
}
static struct platform_driver smux_rmnet_driver = {
.probe = smux_rmnet_probe,
.remove = smux_rmnet_remove,
.driver = {
.name = "SMUX_RMNET",
.owner = THIS_MODULE,
},
};
static int __init rmnet_init(void)
{
int ret;
struct device *d;
struct net_device *dev;
struct rmnet_private *p;
unsigned n;
#ifdef CONFIG_MSM_RMNET_DEBUG
timeout_us = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
timeout_suspend_us = 0;
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* CONFIG_MSM_RMNET_DEBUG */
for (n = 0; n < RMNET_SMUX_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct rmnet_private),
"rmnet_smux%d", rmnet_setup);
if (!dev) {
pr_err("%s: no memory for netdev %d\n", __func__, n);
return -ENOMEM;
}
netdevs[n] = dev;
d = &(dev->dev);
p = netdev_priv(dev);
/* Initial config uses Ethernet */
p->operation_mode = RMNET_MODE_LLP_ETH;
p->ch_id = n;
p->in_reset = 0;
spin_lock_init(&p->lock);
spin_lock_init(&p->tx_queue_lock);
#ifdef CONFIG_MSM_RMNET_DEBUG
p->timeout_us = timeout_us;
p->wakeups_xmit = p->wakeups_rcv = 0;
#endif
ret = register_netdev(dev);
if (ret) {
pr_err("%s: unable to register netdev"
" %d rc=%d\n", __func__, n, ret);
free_netdev(dev);
return ret;
}
#ifdef CONFIG_MSM_RMNET_DEBUG
if (device_create_file(d, &dev_attr_timeout))
continue;
if (device_create_file(d, &dev_attr_wakeups_xmit))
continue;
if (device_create_file(d, &dev_attr_wakeups_rcv))
continue;
#ifdef CONFIG_HAS_EARLYSUSPEND
if (device_create_file(d, &dev_attr_timeout_suspend))
continue;
/* Only care about rmnet0 for suspend/resume tiemout hooks. */
if (n == 0)
rmnet0 = d;
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* CONFIG_MSM_RMNET_DEBUG */
}
ret = platform_driver_register(&smux_rmnet_driver);
if (ret) {
pr_err("%s: registration failed n=%d rc=%d\n",
__func__, n, ret);
return ret;
}
return 0;
}
module_init(rmnet_init);
MODULE_DESCRIPTION("MSM RMNET SMUX TRANSPORT");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,756 @@
/* Copyright (c) 2013 - 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*/
/*
* WWAN Network Interface.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wakelock.h>
#include <linux/msm_rmnet.h>
#include <linux/if_arp.h>
#include <linux/platform_device.h>
#include <net/pkt_sched.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <linux/ratelimit.h>
#include <mach/ipa.h>
#define WWAN_DEV_NAME "rmnet%d"
#define WWAN_METADATA_SHFT 16
#define WWAN_METADATA_MASK 0x00FF0000
#define IPA_RM_INACTIVITY_TIMER 1000
#define WWAN_DEVICE_COUNT (8)
#define WWAN_DATA_LEN 2000
#define HEADROOM_FOR_A2_MUX 8 /* for mux header */
#define TAILROOM 8 /* for padding by mux layer */
enum wwan_device_status {
WWAN_DEVICE_INACTIVE = 0,
WWAN_DEVICE_ACTIVE = 1
};
static enum ipa_rm_resource_name
ipa_rm_resource_by_ch_id[WWAN_DEVICE_COUNT] = {
IPA_RM_RESOURCE_WWAN_0_PROD,
IPA_RM_RESOURCE_WWAN_1_PROD,
IPA_RM_RESOURCE_WWAN_2_PROD,
IPA_RM_RESOURCE_WWAN_3_PROD,
IPA_RM_RESOURCE_WWAN_4_PROD,
IPA_RM_RESOURCE_WWAN_5_PROD,
IPA_RM_RESOURCE_WWAN_6_PROD,
IPA_RM_RESOURCE_WWAN_7_PROD
};
static enum a2_mux_logical_channel_id
a2_mux_lcid_by_ch_id[WWAN_DEVICE_COUNT] = {
A2_MUX_WWAN_0,
A2_MUX_WWAN_1,
A2_MUX_WWAN_2,
A2_MUX_WWAN_3,
A2_MUX_WWAN_4,
A2_MUX_WWAN_5,
A2_MUX_WWAN_6,
A2_MUX_WWAN_7
};
/**
* struct wwan_private - WWAN private data
* @stats: iface statistics
* @ch_id: channel id
* @lock: spinlock for mutual exclusion
* @device_status: holds device status
*
* WWAN private - holds all relevant info about WWAN driver
*/
struct wwan_private {
struct net_device_stats stats;
uint32_t ch_id;
spinlock_t lock;
struct completion resource_granted_completion;
enum wwan_device_status device_status;
};
static struct net_device *netdevs[WWAN_DEVICE_COUNT];
static unsigned long rmnet_jiffies;
static __be16 wwan_ip_type_trans(struct sk_buff *skb)
{
__be16 protocol = 0;
/* Determine L3 protocol */
switch (skb->data[0] & 0xf0) {
case 0x40:
protocol = htons(ETH_P_IP);
break;
case 0x60:
protocol = htons(ETH_P_IPV6);
break;
default:
pr_err("[%s] %s() L3 protocol decode error: 0x%02x",
skb->dev->name, __func__, skb->data[0] & 0xf0);
/* skb will be dropped in upper layer for unknown protocol */
break;
}
return protocol;
}
/**
* a2_mux_recv_notify() - Deliver an RX packet to network stack
*
* @skb: skb to be delivered
* @dev: network device
*
* Return codes:
* None
*/
static void a2_mux_recv_notify(void *dev, struct sk_buff *skb)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
skb->dev = dev;
skb->protocol = wwan_ip_type_trans(skb);
wwan_ptr->stats.rx_packets++;
wwan_ptr->stats.rx_bytes += skb->len;
pr_debug("[%s] Rx packet #%lu len=%d\n",
skb->dev->name,
wwan_ptr->stats.rx_packets, skb->len);
netif_rx(skb);
}
/**
* wwan_send_packet() - Deliver a TX packet to A2 MUX driver.
*
* @skb: skb to be delivered
* @dev: network device
*
* Return codes:
* 0: success
* -EAGAIN: A2 MUX is not ready to send the skb. try later
* -EFAULT: A2 MUX rejected the skb
* -EPREM: Unknown error
*/
static int wwan_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
int ret;
ret = a2_mux_write(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id], skb);
if (ret != 0 && ret != -EAGAIN && ret != -EFAULT) {
pr_err("[%s] %s: write returned error %d",
dev->name, __func__, ret);
return -EPERM;
}
return ret;
}
/**
* a2_mux_write_done() - Update device statistics and start
* network stack queue is was stop and A2 MUX queue is below low
* watermark.
*
* @dev: network device
* @skb: skb to be delivered
*
* Return codes:
* None
*/
static void a2_mux_write_done(void *dev, struct sk_buff *skb)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
unsigned long flags;
pr_debug("%s: write complete\n", __func__);
wwan_ptr->stats.tx_packets++;
wwan_ptr->stats.tx_bytes += skb->len;
pr_debug("[%s] Tx packet #%lu len=%d mark=0x%x\n",
((struct net_device *)(dev))->name, wwan_ptr->stats.tx_packets,
skb->len, skb->mark);
dev_kfree_skb_any(skb);
spin_lock_irqsave(&wwan_ptr->lock, flags);
if (netif_queue_stopped(dev) &&
a2_mux_is_ch_low(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) {
pr_debug("%s: Low WM hit, waking queue=%p\n",
__func__, skb);
netif_wake_queue(dev);
}
if (a2_mux_is_ch_empty(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) {
if (ipa_emb_ul_pipes_empty())
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
else
pr_err_ratelimited("%s: ch=%d empty but UL desc FIFOs not empty\n",
__func__, wwan_ptr->ch_id);
}
spin_unlock_irqrestore(&wwan_ptr->lock, flags);
}
/**
* a2_mux_notify() - Callback function for A2 MUX events Handles
* A2_MUX_RECEIVE and A2_MUX_WRITE_DONE events.
*
* @dev: network device
* @event: A2 MUX event
* @data: Additional data provided by A2 MUX
*
* Return codes:
* None
*/
static void a2_mux_notify(void *dev, enum a2_mux_event_type event,
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
switch (event) {
case A2_MUX_RECEIVE:
if (!skb) {
pr_err("[%s] %s: No skb received",
((struct net_device *)dev)->name, __func__);
return;
}
a2_mux_recv_notify(dev, skb);
break;
case A2_MUX_WRITE_DONE:
a2_mux_write_done(dev, skb);
break;
default:
pr_err("%s: unknown event %d\n", __func__, event);
break;
}
}
/**
* ipa_rm_resource_granted() - Called upon
* IPA_RM_RESOURCE_GRANTED event. Wakes up queue is was stopped.
*
* @work: work object supplied ny workqueue
*
* Return codes:
* None
*/
static void ipa_rm_resource_granted(void *dev)
{
netif_wake_queue(dev);
}
/**
* ipa_rm_notify() - Callback function for RM events. Handles
* IPA_RM_RESOURCE_GRANTED and IPA_RM_RESOURCE_RELEASED events.
* IPA_RM_RESOURCE_GRANTED is handled in the context of shared
* workqueue.
*
* @dev: network device
* @event: IPA RM event
* @data: Additional data provided by IPA RM
*
* Return codes:
* None
*/
static void ipa_rm_notify(void *dev, enum ipa_rm_event event,
unsigned long data)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
pr_debug("%s: event %d\n", __func__, event);
switch (event) {
case IPA_RM_RESOURCE_GRANTED:
if (wwan_ptr->device_status == WWAN_DEVICE_INACTIVE) {
complete_all(&wwan_ptr->resource_granted_completion);
break;
}
ipa_rm_resource_granted(dev);
break;
case IPA_RM_RESOURCE_RELEASED:
break;
default:
pr_err("%s: unknown event %d\n", __func__, event);
break;
}
}
static int wwan_register_to_ipa(struct net_device *dev)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
struct ipa_tx_intf tx_properties = {0};
struct ipa_ioc_tx_intf_prop tx_ioc_properties[2] = { {0}, {0} };
struct ipa_ioc_tx_intf_prop *tx_ipv4_property;
struct ipa_ioc_tx_intf_prop *tx_ipv6_property;
struct ipa_rx_intf rx_properties = {0};
struct ipa_ioc_rx_intf_prop rx_ioc_properties[2] = { {0}, {0} };
struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
int ret = 0;
pr_debug("[%s] %s:\n", dev->name, __func__);
tx_properties.prop = tx_ioc_properties;
tx_ipv4_property = &tx_properties.prop[0];
tx_ipv4_property->ip = IPA_IP_v4;
tx_ipv4_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS;
snprintf(tx_ipv4_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d",
A2_MUX_HDR_NAME_V4_PREF,
a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
tx_ipv6_property = &tx_properties.prop[1];
tx_ipv6_property->ip = IPA_IP_v6;
tx_ipv6_property->dst_pipe = IPA_CLIENT_A2_EMBEDDED_CONS;
snprintf(tx_ipv6_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d",
A2_MUX_HDR_NAME_V6_PREF,
a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
tx_properties.num_props = 2;
rx_properties.prop = rx_ioc_properties;
rx_ipv4_property = &rx_properties.prop[0];
rx_ipv4_property->ip = IPA_IP_v4;
rx_ipv4_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
rx_ipv4_property->attrib.meta_data =
wwan_ptr->ch_id << WWAN_METADATA_SHFT;
rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
rx_ipv4_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
rx_ipv6_property = &rx_properties.prop[1];
rx_ipv6_property->ip = IPA_IP_v6;
rx_ipv6_property->attrib.attrib_mask |= IPA_FLT_META_DATA;
rx_ipv6_property->attrib.meta_data =
wwan_ptr->ch_id << WWAN_METADATA_SHFT;
rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK;
rx_ipv6_property->src_pipe = IPA_CLIENT_A2_EMBEDDED_PROD;
rx_properties.num_props = 2;
ret = ipa_register_intf(dev->name, &tx_properties, &rx_properties);
if (ret) {
pr_err("[%s] %s: ipa_register_intf failed %d\n", dev->name,
__func__, ret);
return ret;
}
return 0;
}
static int __wwan_open(struct net_device *dev)
{
int r;
struct wwan_private *wwan_ptr = netdev_priv(dev);
pr_debug("[%s] __wwan_open()\n", dev->name);
if (wwan_ptr->device_status != WWAN_DEVICE_ACTIVE) {
INIT_COMPLETION(wwan_ptr->resource_granted_completion);
r = ipa_rm_inactivity_timer_request_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
if (r < 0 && r != -EINPROGRESS) {
pr_err("%s: ipa rm timer request resource failed %d\n",
__func__, r);
return -ENODEV;
}
if (r == -EINPROGRESS) {
wait_for_completion(
&wwan_ptr->resource_granted_completion);
}
r = a2_mux_open_channel(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id],
dev, a2_mux_notify);
if (r < 0) {
pr_err("%s: ch=%d failed with rc %d\n",
__func__, wwan_ptr->ch_id, r);
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
return -ENODEV;
}
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
r = wwan_register_to_ipa(dev);
if (r < 0) {
pr_err("%s: ch=%d failed to register to IPA rc %d\n",
__func__, wwan_ptr->ch_id, r);
return -ENODEV;
}
}
wwan_ptr->device_status = WWAN_DEVICE_ACTIVE;
return 0;
}
/**
* wwan_open() - Opens the wwan network interface. Opens logical
* channel on A2 MUX driver and starts the network stack queue
*
* @dev: network device
*
* Return codes:
* 0: success
* -ENODEV: Error while opening logical channel on A2 MUX driver
*/
static int wwan_open(struct net_device *dev)
{
int rc = 0;
pr_debug("[%s] wwan_open()\n", dev->name);
rc = __wwan_open(dev);
if (rc == 0)
netif_start_queue(dev);
return rc;
}
static int __wwan_close(struct net_device *dev)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
int rc = 0;
if (wwan_ptr->device_status == WWAN_DEVICE_ACTIVE) {
wwan_ptr->device_status = WWAN_DEVICE_INACTIVE;
/* do not close wwan port once up, this causes
remote side to hang if tried to open again */
INIT_COMPLETION(wwan_ptr->resource_granted_completion);
rc = ipa_rm_inactivity_timer_request_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
if (rc < 0 && rc != -EINPROGRESS) {
pr_err("%s: ipa rm timer request resource failed %d\n",
__func__, rc);
return -ENODEV;
}
if (rc == -EINPROGRESS) {
wait_for_completion(
&wwan_ptr->resource_granted_completion);
}
rc = a2_mux_close_channel(
a2_mux_lcid_by_ch_id[wwan_ptr->ch_id]);
if (rc) {
pr_err("[%s] %s: a2_mux_close_channel failed %d\n",
dev->name, __func__, rc);
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
return rc;
}
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
rc = ipa_deregister_intf(dev->name);
if (rc) {
pr_err("[%s] %s: ipa_deregister_intf failed %d\n",
dev->name, __func__, rc);
return rc;
}
return rc;
} else
return -EBADF;
}
/**
* wwan_stop() - Stops the wwan network interface. Closes
* logical channel on A2 MUX driver and stops the network stack
* queue
*
* @dev: network device
*
* Return codes:
* 0: success
* -ENODEV: Error while opening logical channel on A2 MUX driver
*/
static int wwan_stop(struct net_device *dev)
{
pr_debug("[%s] wwan_stop()\n", dev->name);
__wwan_close(dev);
netif_stop_queue(dev);
return 0;
}
static int wwan_change_mtu(struct net_device *dev, int new_mtu)
{
if (0 > new_mtu || WWAN_DATA_LEN < new_mtu)
return -EINVAL;
pr_debug("[%s] MTU change: old=%d new=%d\n",
dev->name, dev->mtu, new_mtu);
dev->mtu = new_mtu;
return 0;
}
/**
* wwan_xmit() - Transmits an skb. In charge of asking IPA
* RM needed resources. In case that IPA RM is not ready, then
* the skb is saved for tranmitting as soon as IPA RM resources
* are granted.
*
* @skb: skb to be transmitted
* @dev: network device
*
* Return codes:
* 0: success
* NETDEV_TX_BUSY: Error while transmitting the skb. Try again
* later
* -EFAULT: Error while transmitting the skb
*/
static int wwan_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
unsigned long flags;
int ret = 0;
int i;
if (netif_queue_stopped(dev)) {
pr_err("[%s]fatal: wwan_xmit called when netif_queue stopped\n",
dev->name);
return 0;
}
rmnet_jiffies = jiffies;
for (i = 0; i < dev->num_tx_queues; i++)
netdev_get_tx_queue(dev, i)->trans_start = rmnet_jiffies;
dev->trans_start = rmnet_jiffies;
ret = ipa_rm_inactivity_timer_request_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
if (ret == -EINPROGRESS) {
netif_stop_queue(dev);
return NETDEV_TX_BUSY;
}
if (ret) {
pr_err("[%s] fatal: ipa rm timer request resource failed %d\n",
dev->name, ret);
return -EFAULT;
}
ret = wwan_send_packet(skb, dev);
if (ret == -EPERM) {
ret = NETDEV_TX_BUSY;
goto exit;
}
/*
* detected SSR a bit early. shut some things down now, and leave
* the rest to the main ssr handling code when that happens later
*/
if (ret == -EFAULT) {
netif_carrier_off(dev);
dev_kfree_skb_any(skb);
ret = 0;
goto exit;
}
if (ret == -EAGAIN) {
/*
* This should not happen
* EAGAIN means we attempted to overflow the high watermark
* Clearly the queue is not stopped like it should be, so
* stop it and return BUSY to the TCP/IP framework. It will
* retry this packet with the queue is restarted which happens
* in the write_done callback when the low watermark is hit.
*/
netif_stop_queue(dev);
ret = NETDEV_TX_BUSY;
goto exit;
}
spin_lock_irqsave(&wwan_ptr->lock, flags);
if (a2_mux_is_ch_full(a2_mux_lcid_by_ch_id[wwan_ptr->ch_id])) {
netif_stop_queue(dev);
pr_debug("%s: High WM hit, stopping queue=%p\n",
__func__, skb);
}
spin_unlock_irqrestore(&wwan_ptr->lock, flags);
return ret;
exit:
ipa_rm_inactivity_timer_release_resource(
ipa_rm_resource_by_ch_id[wwan_ptr->ch_id]);
return ret;
}
static struct net_device_stats *wwan_get_stats(struct net_device *dev)
{
struct wwan_private *wwan_ptr = netdev_priv(dev);
return &wwan_ptr->stats;
}
static void wwan_tx_timeout(struct net_device *dev)
{
pr_warning("[%s] wwan_tx_timeout(), data stall in UL\n", dev->name);
ipa_bam_reg_dump();
}
/**
* wwan_ioctl() - I/O control for wwan network driver.
*
* @dev: network device
* @ifr: ignored
* @cmd: cmd to be excecuded. can be one of the following:
* WWAN_IOCTL_OPEN - Open the network interface
* WWAN_IOCTL_CLOSE - Close the network interface
*
* Return codes:
* 0: success
* NETDEV_TX_BUSY: Error while transmitting the skb. Try again
* later
* -EFAULT: Error while transmitting the skb
*/
static int wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int rc = 0;
switch (cmd) {
case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
break;
case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP;
break;
case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
break;
case RMNET_IOCTL_FLOW_ENABLE:
tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 1);
pr_debug("[%s] %s: enabled flow", dev->name, __func__);
break;
case RMNET_IOCTL_FLOW_DISABLE:
tc_qdisc_flow_control(dev, (u32)ifr->ifr_data, 0);
pr_debug("[%s] %s: disabled flow", dev->name, __func__);
break;
case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
/* QoS disabled */
ifr->ifr_ifru.ifru_data = (void *) 0;
break;
case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
ifr->ifr_ifru.ifru_data = (void *) RMNET_MODE_LLP_IP;
break;
case RMNET_IOCTL_OPEN: /* Open transport port */
rc = __wwan_open(dev);
pr_debug("[%s] wwan_ioctl(): open transport port\n",
dev->name);
break;
case RMNET_IOCTL_CLOSE: /* Close transport port */
rc = __wwan_close(dev);
pr_debug("[%s] wwan_ioctl(): close transport port\n",
dev->name);
break;
default:
pr_err("[%s] error: wwan_ioct called for unsupported cmd[%d]",
dev->name, cmd);
return -EINVAL;
}
return rc;
}
static const struct net_device_ops wwan_ops_ip = {
.ndo_open = wwan_open,
.ndo_stop = wwan_stop,
.ndo_start_xmit = wwan_xmit,
.ndo_get_stats = wwan_get_stats,
.ndo_tx_timeout = wwan_tx_timeout,
.ndo_do_ioctl = wwan_ioctl,
.ndo_change_mtu = wwan_change_mtu,
.ndo_set_mac_address = 0,
.ndo_validate_addr = 0,
};
/**
* wwan_setup() - Setups the wwan network driver.
*
* @dev: network device
*
* Return codes:
* None
*/
static void wwan_setup(struct net_device *dev)
{
dev->netdev_ops = &wwan_ops_ip;
ether_setup(dev);
/* set this after calling ether_setup */
dev->header_ops = 0; /* No header */
dev->type = ARPHRD_RAWIP;
dev->hard_header_len = 0;
dev->mtu = WWAN_DATA_LEN;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
dev->needed_headroom = HEADROOM_FOR_A2_MUX;
dev->needed_tailroom = TAILROOM;
dev->watchdog_timeo = 30 * HZ;
}
/**
* wwan_init() - Initialized the module and registers as a
* network interface to the network stack
*
* Return codes:
* 0: success
* -ENOMEM: No memory available
* -EFAULT: Internal error
*/
static int __init wwan_init(void)
{
int ret;
struct net_device *dev;
struct wwan_private *wwan_ptr;
unsigned n;
struct ipa_rm_create_params ipa_rm_params;
pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT);
for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
dev = alloc_netdev(sizeof(struct wwan_private),
WWAN_DEV_NAME, wwan_setup);
if (!dev) {
pr_err("%s: no memory for netdev %d\n", __func__, n);
ret = -ENOMEM;
goto fail;
}
netdevs[n] = dev;
wwan_ptr = netdev_priv(dev);
wwan_ptr->ch_id = n;
spin_lock_init(&wwan_ptr->lock);
init_completion(&wwan_ptr->resource_granted_completion);
memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params));
ipa_rm_params.name = ipa_rm_resource_by_ch_id[n];
ipa_rm_params.reg_params.user_data = dev;
ipa_rm_params.reg_params.notify_cb = ipa_rm_notify;
ret = ipa_rm_create_resource(&ipa_rm_params);
if (ret) {
pr_err("%s: unable to create resourse %d in IPA RM\n",
__func__, ipa_rm_resource_by_ch_id[n]);
goto fail;
}
ret = ipa_rm_inactivity_timer_init(ipa_rm_resource_by_ch_id[n],
IPA_RM_INACTIVITY_TIMER);
if (ret) {
pr_err("%s: ipa rm timer init failed %d on ins %d\n",
__func__, ret, n);
goto fail;
}
ret = ipa_rm_add_dependency(ipa_rm_resource_by_ch_id[n],
IPA_RM_RESOURCE_A2_CONS);
if (ret) {
pr_err("%s: unable to add dependency %d rc=%d\n",
__func__, n, ret);
goto fail;
}
ret = register_netdev(dev);
if (ret) {
pr_err("%s: unable to register netdev %d rc=%d\n",
__func__, n, ret);
goto fail;
}
}
return 0;
fail:
for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
if (!netdevs[n])
break;
unregister_netdev(netdevs[n]);
ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]);
free_netdev(netdevs[n]);
netdevs[n] = NULL;
}
return ret;
}
late_initcall(wwan_init);
void wwan_cleanup(void)
{
unsigned n;
pr_info("%s: WWAN devices[%d]\n", __func__, WWAN_DEVICE_COUNT);
for (n = 0; n < WWAN_DEVICE_COUNT; n++) {
unregister_netdev(netdevs[n]);
ipa_rm_inactivity_timer_destroy(ipa_rm_resource_by_ch_id[n]);
free_netdev(netdevs[n]);
netdevs[n] = NULL;
}
}
MODULE_DESCRIPTION("WWAN Network Interface");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
+800
View File
@@ -0,0 +1,800 @@
/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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.
*/
/* qualcomm fast Ethernet controller HW description */
#ifndef _QFEC_EMAC_H_
# define _QFEC_EMAC_H_
# ifndef __KERNEL__
# include "stdint.h"
# endif
# define MskBits(nBits, pos) (((1 << nBits)-1)<<pos)
/* Rx/Tx Ethernet Buffer Descriptors
* status contains the ownership, status and receive length bits
* ctl contains control and size bits for two buffers
* p_buf contains a ptr to the data buffer
* MAC writes timestamp low into p_buf
* next contains either ptr to 2nd buffer or next buffer-desc
* MAC writes timestamp high into next
*
* status/ctl bit definition depend on RX or TX usage
*/
struct qfec_buf_desc {
uint32_t status;
uint32_t ctl;
void *p_buf;
void *next;
};
/* ownership bit operations */
# define BUF_OWN 0x80000000 /* DMA owns buffer */
# define BUF_OWN_DMA BUF_OWN
/* RX buffer status bits */
# define BUF_RX_AFM 0x40000000 /* dest addr filt fail */
# define BUF_RX_FL 0x3fff0000 /* frame length */
# define BUF_RX_FL_GET(p) ((p.status & BUF_RX_FL) >> 16)
# define BUF_RX_FL_SET(p, x) \
(p.status = (p.status & ~BUF_RX_FL) | ((x << 16) & BUF_RX_FL))
# define BUF_RX_FL_GET_FROM_STATUS(status) \
(((status) & BUF_RX_FL) >> 16)
# define BUF_RX_ES 0x00008000 /* error summary */
# define BUF_RX_DE 0x00004000 /* error descriptor (es) */
# define BUF_RX_SAF 0x00002000 /* source addr filt fail */
# define BUF_RX_LE 0x00001000 /* length error */
# define BUF_RX_OE 0x00000800 /* overflow error (es) */
# define BUF_RX_VLAN 0x00000400 /* vlan tag */
# define BUF_RX_FS 0x00000200 /* first descriptor */
# define BUF_RX_LS 0x00000100 /* last descriptor */
# define BUF_RX_IPC 0x00000080 /* cksum-err/giant-frame (es) */
# define BUF_RX_LC 0x00000040 /* late collision (es) */
# define BUF_RX_FT 0x00000020 /* frame type */
# define BUF_RX_RWT 0x00000010 /* rec watchdog timeout (es) */
# define BUF_RX_RE 0x00000008 /* rec error (es) */
# define BUF_RX_DBE 0x00000004 /* dribble bit err */
# define BUF_RX_CE 0x00000002 /* crc err (es) */
# define BUF_RX_CSE 0x00000001 /* checksum err */
# define BUF_RX_ERRORS \
(BUF_RX_DE | BUF_RX_SAF | BUF_RX_LE | BUF_RX_OE \
| BUF_RX_IPC | BUF_RX_LC | BUF_RX_RWT | BUF_RX_RE \
| BUF_RX_DBE | BUF_RX_CE | BUF_RX_CSE)
/* RX buffer control bits */
# define BUF_RX_DI 0x80000000 /* disable intrp on compl */
# define BUF_RX_RER 0x02000000 /* rec end of ring */
# define BUF_RX_RCH 0x01000000 /* 2nd addr chained */
# define BUF_RX_SIZ2 0x003ff800 /* buffer 2 size */
# define BUF_RX_SIZ2_GET(p) ((p.control&BUF_RX_SIZ2) >> 11)
# define BUF_RX_SIZ 0x000007ff /* rx buf 1 size */
# define BUF_RX_SIZ_GET(p) (p.ctl&BUF_RX_SIZ)
/* TX buffer status bits */
# define BUF_TX_TTSS 0x00020000 /* time stamp status */
# define BUF_TX_IHE 0x00010000 /* IP hdr err */
# define BUF_TX_ES 0x00008000 /* error summary */
# define BUF_TX_JT 0x00004000 /* jabber timeout (es) */
# define BUF_TX_FF 0x00002000 /* frame flushed (es) */
# define BUF_TX_PCE 0x00001000 /* payld cksum err */
# define BUF_TX_LOC 0x00000800 /* loss carrier (es) */
# define BUF_TX_NC 0x00000400 /* no carrier (es) */
# define BUF_TX_LC 0x00000200 /* late collision (es) */
# define BUF_TX_EC 0x00000100 /* excessive collision (es) */
# define BUF_TX_VLAN 0x00000080 /* VLAN frame */
# define BUF_TX_CC MskBits(4, 3) /* collision count */
# define BUF_TX_CC_GET(p) ((p.status&BUF_TX_CC)>>3)
# define BUF_TX_ED 0x00000004 /* excessive deferral (es) */
# define BUF_TX_UF 0x00000002 /* underflow err (es) */
# define BUF_TX_DB 0x00000001 /* deferred bit */
/* TX buffer control bits */
# define BUF_TX_IC 0x80000000 /* intrpt on compl */
# define BUF_TX_LS 0x40000000 /* last segment */
# define BUF_TX_FS 0x20000000 /* first segment */
# define BUF_TX_CIC 0x18000000 /* cksum insert control */
# define BUF_TX_CIC_SET(n) (BUF_TX_CIC&(n<<27))
# define BUF_TX_DC 0x04000000 /* disable CRC */
# define BUF_TX_TER 0x02000000 /* end of ring */
# define BUF_TX_TCH 0x01000000 /* 2nd addr chained */
# define BUF_TX_DP 0x00800000 /* disable padding */
# define BUF_TX_TTSE 0x00400000 /* timestamp enable */
# define BUF_TX_SIZ2 0x003ff800 /* buffer 2 size */
# define BUF_TX_SIZ2_SET(n) (BUF_TX_SIZ2(n<<11))
# define BUF_TX_SIZ 0x000007ff /* buffer 1 size */
# define BUF_TX_SIZ_SET(n) (BUF_TX_SI1 & n)
/* Ethernet Controller Registers */
# define BUS_MODE_REG 0x1000
# define BUS_MODE_MB 0x04000000 /* mixed burst */
# define BUS_MODE_AAL 0x02000000 /* address alignment beats */
# define BUS_MODE_8XPBL 0x01000000 /* */
# define BUS_MODE_USP 0x00800000 /* use separate PBL */
# define BUS_MODE_RPBL 0x007e0000 /* rxDMA PBL */
# define BUS_MODE_FB 0x00010000 /* fixed burst */
# define BUS_MODE_PR 0x0000c000 /* tx/rx priority */
# define BUS_MODE_PR4 0x0000c000 /* tx/rx priority 4:1 */
# define BUS_MODE_PR3 0x00008000 /* tx/rx priority 3:1 */
# define BUS_MODE_PR2 0x00004000 /* tx/rx priority 2:1 */
# define BUS_MODE_PR1 0x00000000 /* tx/rx priority 1:1 */
# define BUS_MODE_PBL 0x00003f00 /* programmable burst length */
# define BUS_MODE_PBLSET(n) (BUS_MODE_PBL&(n<<8))
# define BUS_MODE_DSL 0x0000007c /* descriptor skip length */
# define BUS_MODE_DSL_SET(n) (BUS_MODE_DSL & (n << 2))
# define BUS_MODE_DA 0x00000002 /* DMA arbitration scheme */
# define BUS_MODE_SWR 0x00000001 /* software reset */
#define BUS_MODE_REG_DEFAULT (BUS_MODE_FB \
| BUS_MODE_AAL \
| BUS_MODE_PBLSET(16) \
| BUS_MODE_DA \
| BUS_MODE_DSL_SET(0))
# define TX_POLL_DEM_REG 0x1004 /* transmit poll demand */
# define RX_POLL_DEM_REG 0x1008 /* receive poll demand */
# define RX_DES_LST_ADR_REG 0x100c /* receive buffer descriptor */
# define TX_DES_LST_ADR_REG 0x1010 /* transmit buffer descriptor */
# define STATUS_REG 0x1014
# define STATUS_REG_RSVRD_1 0xc0000000 /* reserved */
# define STATUS_REG_TTI 0x20000000 /* time-stamp trigger intrpt */
# define STATUS_REG_GPI 0x10000000 /* gmac PMT interrupt */
# define STATUS_REG_GMI 0x08000000 /* gmac MMC interrupt */
# define STATUS_REG_GLI 0x04000000 /* gmac line interface intrpt */
# define STATUS_REG_EB 0x03800000 /* error bits */
# define STATUS_REG_EB_DATA 0x00800000 /* error during data transfer */
# define STATUS_REG_EB_RDWR 0x01000000 /* error during rd/wr transfer */
# define STATUS_REG_EB_DESC 0x02000000 /* error during desc access */
# define STATUS_REG_TS 0x00700000 /* transmit process state */
# define STATUS_REG_TS_STOP 0x00000000 /* stopped */
# define STATUS_REG_TS_FETCH_DESC 0x00100000 /* fetching descriptor */
# define STATUS_REG_TS_WAIT 0x00200000 /* waiting for status */
# define STATUS_REG_TS_READ 0x00300000 /* reading host memory */
# define STATUS_REG_TS_TIMESTAMP 0x00400000 /* timestamp write status */
# define STATUS_REG_TS_RSVRD 0x00500000 /* reserved */
# define STATUS_REG_TS_SUSPEND 0x00600000 /* desc-unavail/buffer-unflw */
# define STATUS_REG_TS_CLOSE 0x00700000 /* closing desc */
# define STATUS_REG_RS 0x000e0000 /* receive process state */
# define STATUS_REG_RS_STOP 0x00000000 /* stopped */
# define STATUS_REG_RS_FETCH_DESC 0x00020000 /* fetching descriptor */
# define STATUS_REG_RS_RSVRD_1 0x00040000 /* reserved */
# define STATUS_REG_RS_WAIT 0x00060000 /* waiting for packet */
# define STATUS_REG_RS_SUSPEND 0x00080000 /* desc unavail */
# define STATUS_REG_RS_CLOSE 0x000a0000 /* closing desc */
# define STATUS_REG_RS_TIMESTAMP 0x000c0000 /* timestamp write status */
# define STATUS_REG_RS_RSVRD_2 0x000e0000 /* writing host memory */
# define STATUS_REG_NIS 0x00010000 /* normal intrpt 14|6|2|0 */
# define STATUS_REG_AIS 0x00008000 /* intrpts 13|10|9|8|7|5|4|3|1 */
# define STATUS_REG_ERI 0x00004000 /* early receive interrupt */
# define STATUS_REG_FBI 0x00002000 /* fatal bus error interrupt */
# define STATUS_REG_RSVRD_2 0x00001800 /* reserved */
# define STATUS_REG_ETI 0x00000400 /* early transmit interrupt */
# define STATUS_REG_RWT 0x00000200 /* receive watchdog timeout */
# define STATUS_REG_RPS 0x00000100 /* receive process stopped */
# define STATUS_REG_RU 0x00000080 /* receive buffer unavailable */
# define STATUS_REG_RI 0x00000040 /* receive interrupt */
# define STATUS_REG_UNF 0x00000020 /* transmit underflow */
# define STATUS_REG_OVF 0x00000010 /* receive overflow */
# define STATUS_REG_TJT 0x00000008 /* transmit jabber timeout */
# define STATUS_REG_TU 0x00000004 /* transmit buffer unavailable */
# define STATUS_REG_TPS 0x00000002 /* transmit process stopped */
# define STATUS_REG_TI 0x00000001 /* transmit interrupt */
# define STATUS_REG_AIS_BITS (STATUS_REG_FBI | STATUS_REG_ETI \
| STATUS_REG_RWT | STATUS_REG_RPS \
| STATUS_REG_RU | STATUS_REG_UNF \
| STATUS_REG_OVF | STATUS_REG_TJT \
| STATUS_REG_TPS | STATUS_REG_AIS)
# define OPER_MODE_REG 0x1018
# define OPER_MODE_REG_DT 0x04000000 /* disab drop ip cksum err fr */
# define OPER_MODE_REG_RSF 0x02000000 /* rec store and forward */
# define OPER_MODE_REG_DFF 0x01000000 /* disable flush of rec frames */
# define OPER_MODE_REG_RFA2 0x00800000 /* thresh MSB for act flow-ctl */
# define OPER_MODE_REG_RFD2 0x00400000 /* thresh MSB deAct flow-ctl */
# define OPER_MODE_REG_TSF 0x00200000 /* tx store and forward */
# define OPER_MODE_REG_FTF 0x00100000 /* flush tx FIFO */
# define OPER_MODE_REG_RSVD1 0x000e0000 /* reserved */
# define OPER_MODE_REG_TTC 0x0001c000 /* transmit threshold control */
# define OPER_MODE_REG_TTC_SET(x) (OPER_MODE_REG_TTC & (x << 14))
# define OPER_MODE_REG_ST 0x00002000 /* start/stop transmission cmd */
# define OPER_MODE_REG_RFD 0x00001800 /* thresh for deAct flow-ctl */
# define OPER_MODE_REG_RFA 0x00000600 /* threshold for act flow-ctl */
# define OPER_MODE_REG_EFC 0x00000100 /* enable HW flow-ctl */
# define OPER_MODE_REG_FEF 0x00000080 /* forward error frames */
# define OPER_MODE_REG_FUF 0x00000040 /* forward undersize good fr */
# define OPER_MODE_REG_RSVD2 0x00000020 /* reserved */
# define OPER_MODE_REG_RTC 0x00000018 /* receive threshold control */
# define OPER_MODE_REG_RTC_SET(x) (OPER_MODE_REG_RTC & (x << 3))
# define OPER_MODE_REG_OSF 0x00000004 /* operate on second frame */
# define OPER_MODE_REG_SR 0x00000002 /* start/stop receive */
# define OPER_MODE_REG_RSVD3 0x00000001 /* reserved */
#define OPER_MODE_REG_DEFAULT (OPER_MODE_REG_RSF \
| OPER_MODE_REG_TSF \
| OPER_MODE_REG_TTC_SET(5) \
| OPER_MODE_REG_RTC_SET(1) \
| OPER_MODE_REG_OSF)
# define INTRP_EN_REG 0x101c
# define INTRP_EN_REG_RSVD1 0xfffc0000 /* */
# define INTRP_EN_REG_NIE 0x00010000 /* normal intrpt summ enable */
# define INTRP_EN_REG_AIE 0x00008000 /* abnormal intrpt summary en */
# define INTRP_EN_REG_ERE 0x00004000 /* early receive intrpt enable */
# define INTRP_EN_REG_FBE 0x00002000 /* fatal bus error enable */
# define INTRP_EN_REG_RSVD2 0x00001800 /* */
# define INTRP_EN_REG_ETE 0x00000400 /* early tx intrpt enable */
# define INTRP_EN_REG_RWE 0x00000200 /* rx watchdog timeout enable */
# define INTRP_EN_REG_RSE 0x00000100 /* rx stopped enable */
# define INTRP_EN_REG_RUE 0x00000080 /* rx buf unavailable enable */
# define INTRP_EN_REG_RIE 0x00000040 /* rx interrupt enable */
# define INTRP_EN_REG_UNE 0x00000020 /* underflow interrupt enable */
# define INTRP_EN_REG_OVE 0x00000010 /* overflow interrupt enable */
# define INTRP_EN_REG_TJE 0x00000008 /* tx jabber timeout enable */
# define INTRP_EN_REG_TUE 0x00000004 /* tx buf unavailable enable */
# define INTRP_EN_REG_TSE 0x00000002 /* tx stopped enable */
# define INTRP_EN_REG_TIE 0x00000001 /* tx interrupt enable */
# define INTRP_EN_REG_All (~(INTRP_EN_REG_RSVD1))
# define MIS_FR_REG 0x1020
# define MIS_FR_REG_FIFO_OVFL 0x10000000 /* fifo overflow */
# define MIS_FR_REG_FIFO_CNT 0x0FFE0000 /* fifo cnt */
# define MIS_FR_REG_MISS_OVFL 0x00010000 /* missed-frame overflow */
# define MIS_FR_REG_MISS_CNT 0x0000FFFF /* missed-frame cnt */
# define RX_INTRP_WTCHDOG_REG 0x1024
# define AXI_BUS_MODE_REG 0x1028
# define AXI_BUS_MODE_EN_LPI 0x80000000 /* enable low power interface */
# define AXI_BUS_MODE_UNLK_MGC_PKT 0x40000000 /* unlock-magic-pkt/rem-wk-up */
# define AXI_BUS_MODE_WR_OSR_LMT 0x00F00000 /* max wr out stndg req limit */
# define AXI_BUS_MODE_RD_OSR_LMT 0x000F0000 /* max rd out stndg req limit */
# define AXI_BUS_MODE_AXI_AAL 0x00001000 /* address aligned beats */
# define AXI_BUS_MODE_BLEN256 0x00000080 /* axi burst length 256 */
# define AXI_BUS_MODE_BLEN128 0x00000040 /* axi burst length 128 */
# define AXI_BUS_MODE_BLEN64 0x00000020 /* axi burst length 64 */
# define AXI_BUS_MODE_BLEN32 0x00000010 /* axi burst length 32 */
# define AXI_BUS_MODE_BLEN16 0x00000008 /* axi burst length 16 */
# define AXI_BUS_MODE_BLEN8 0x00000004 /* axi burst length 8 */
# define AXI_BUS_MODE_BLEN4 0x00000002 /* axi burst length 4 */
# define AXI_BUS_MODE_UNDEF 0x00000001 /* axi undef burst length */
#define AXI_BUS_MODE_DEFAULT (AXI_BUS_MODE_WR_OSR_LMT \
| AXI_BUS_MODE_RD_OSR_LMT \
| AXI_BUS_MODE_BLEN16 \
| AXI_BUS_MODE_BLEN8 \
| AXI_BUS_MODE_BLEN4)
# define AXI_STATUS_REG 0x102c
/* 0x1030-0x1044 reserved */
# define CUR_HOST_TX_DES_REG 0x1048
# define CUR_HOST_RX_DES_REG 0x104c
# define CUR_HOST_TX_BU_ADR_REG 0x1050
# define CUR_HOST_RX_BU_ADR_REG 0x1054
# define HW_FEATURE_REG 0x1058
# define MAC_CONFIG_REG 0x0000
# define MAC_CONFIG_REG_RSVD1 0xf8000000 /* */
# define MAC_CONFIG_REG_SFTERR 0x04000000 /* smii force tx error */
# define MAC_CONFIG_REG_CST 0x02000000 /* crc strip for type frame */
# define MAC_CONFIG_REG_TC 0x01000000 /* tx cfg in rgmii/sgmii/smii */
# define MAC_CONFIG_REG_WD 0x00800000 /* watchdog disable */
# define MAC_CONFIG_REG_JD 0x00400000 /* jabber disable */
# define MAC_CONFIG_REG_BE 0x00200000 /* frame burst enable */
# define MAC_CONFIG_REG_JE 0x00100000 /* jumbo frame enable */
# define MAC_CONFIG_REG_IFG 0x000e0000 /* inter frame gap, 96-(8*n) */
# define MAC_CONFIG_REG_DCRS 0x00010000 /* dis carrier sense during tx */
# define MAC_CONFIG_REG_PS 0x00008000 /* port select: 0/1 g/(10/100) */
# define MAC_CONFIG_REG_FES 0x00004000 /* speed 100 mbps */
# define MAC_CONFIG_REG_SPD (MAC_CONFIG_REG_PS | MAC_CONFIG_REG_FES)
# define MAC_CONFIG_REG_SPD_1G (0)
# define MAC_CONFIG_REG_SPD_100 (MAC_CONFIG_REG_PS | MAC_CONFIG_REG_FES)
# define MAC_CONFIG_REG_SPD_10 (MAC_CONFIG_REG_PS)
# define MAC_CONFIG_REG_SPD_SET(x) (MAC_CONFIG_REG_PS_FES & (x << 14))
# define MAC_CONFIG_REG_DO 0x00002000 /* disable receive own */
# define MAC_CONFIG_REG_LM 0x00001000 /* loopback mode */
# define MAC_CONFIG_REG_DM 0x00000800 /* (full) duplex mode */
# define MAC_CONFIG_REG_IPC 0x00000400 /* checksum offload */
# define MAC_CONFIG_REG_DR 0x00000200 /* disable retry */
# define MAC_CONFIG_REG_LUD 0x00000100 /* link up/down */
# define MAC_CONFIG_REG_ACS 0x00000080 /* auto pad/crc stripping */
# define MAC_CONFIG_REG_BL 0x00000060 /* back-off limit */
# define MAC_CONFIG_REG_BL_10 0x00000000 /* 10 */
# define MAC_CONFIG_REG_BL_8 0x00000020 /* 8 */
# define MAC_CONFIG_REG_BL_4 0x00000040 /* 4 */
# define MAC_CONFIG_REG_BL_1 0x00000060 /* 1 */
# define MAC_CONFIG_REG_DC 0x00000010 /* deferral check */
# define MAC_CONFIG_REG_TE 0x00000008 /* transmitter enable */
# define MAC_CONFIG_REG_RE 0x00000004 /* receiver enable */
# define MAC_CONFIG_REG_RSVD2 0x00000003 /* */
# define MAC_FR_FILTER_REG 0x0004
# define MAC_FR_FILTER_RA 0x80000000 /* receive all */
# define MAC_FR_FILTER_HPF 0x00000400 /* hash or perfect filter */
# define MAC_FR_FILTER_SAF 0x00000200 /* source addr filt en */
# define MAC_FR_FILTER_SAIF 0x00000100 /* SA inverse filter */
# define MAC_FR_FILTER_PCF_MASK 0x000000c0 /* pass control frames */
# define MAC_FR_FILTER_PCF_0 0x00000000 /* */
# define MAC_FR_FILTER_PCF_1 0x00000040 /* */
# define MAC_FR_FILTER_PCF_2 0x00000080 /* */
# define MAC_FR_FILTER_PCF_3 0x000000c0 /* */
# define MAC_FR_FILTER_DBF 0x00000020 /* disable broadcast frames */
# define MAC_FR_FILTER_PM 0x00000010 /* pass all multicast */
# define MAC_FR_FILTER_DAIF 0x00000008 /* DA inverse filtering */
# define MAC_FR_FILTER_HMC 0x00000004 /* hash multicast */
# define MAC_FR_FILTER_HUC 0x00000002 /* hash unicast */
# define MAC_FR_FILTER_PR 0x00000001 /* promiscuous mode */
# define HASH_TABLE_HIGH_REG 0x0008
# define HASH_TABLE_LOW_REG 0x000c
# define GMII_ADR_REG 0x0010
# define GMII_ADR_REG_PA 0x0000f800 /* addr bits */
# define GMII_ADR_REG_GR 0x000007c0 /* addr bits */
# define GMII_ADR_REG_RSVRD1 0x00000020 /* */
# define GMII_ADR_REG_CR 0x0000001c /* csr clock range */
# define GMII_ADR_REG_GW 0x00000002 /* gmii write */
# define GMII_ADR_REG_GB 0x00000001 /* gmii busy */
# define GMII_ADR_REG_ADR_SET(x) (GMII_ADR_REG_PA & (x << 11))
# define GMII_ADR_REG_ADR_GET(x) ((x & GMII_ADR_REG_PA) >> 11)
# define GMII_ADR_REG_REG_SET(x) (GMII_ADR_REG_GR & (x << 6))
# define GMII_ADR_REG_REG_GET(x) (((x & GMII_ADR_REG_GR) >> 6)
# define GMII_ADR_REG_CSR_SET(x) (GMII_ADR_REG_CR & (x << 2))
# define GMII_ADR_REG_CSR_GET(x) (((x & GMII_ADR_REG_CR) >> 2)
# define GMII_DATA_REG 0x0014
# define GMII_DATA_REG_DATA 0x0000ffff /* gmii data */
# define FLOW_CONTROL_REG 0x0018
# define FLOW_CONTROL_PT 0xFFFF0000 /* pause time */
# define FLOW_CONTROL_DZPQ 0x00000080 /* disable zero-quanta pause */
# define FLOW_CONTROL_PLT 0x00000030 /* pause level threshold */
# define FLOW_CONTROL_UP 0x00000008 /* unicast pause frame detect */
# define FLOW_CONTROL_RFE 0x00000004 /* receive flow control enable */
# define FLOW_CONTROL_TFE 0x00000002 /* transmit flow control enable */
# define FLOW_CONTROL_FCB 0x00000001 /* flow control busy (BPA) */
# define VLAN_TAG_REG 0x001c
# define VERSION_REG 0x0020
/* don't define these until HW if finished */
/* # define VERSION_USER 0x10 */
/* # define VERSION_QFEC 0x36 */
# define VERSION_REG_USER(x) (0xFF & (x >> 8))
# define VERSION_REG_QFEC(x) (0xFF & x)
# define DEBUG_REG 0x0024
# define DEBUG_REG_RSVD1 0xfc000000 /* */
# define DEBUG_REG_TX_FIFO_FULL 0x02000000 /* Tx fifo full */
# define DEBUG_REG_TX_FIFO_NEMP 0x01000000 /* Tx fifo not empty */
# define DEBUG_REG_RSVD2 0x00800000 /* */
# define DEBUG_REG_TX_WR_ACTIVE 0x00400000 /* Tx fifo write ctrl active */
# define DEBUG_REG_TX_RD_STATE 0x00300000 /* Tx fifo rd ctrl state */
# define DEBUG_REG_TX_RD_IDLE 0x00000000 /* idle */
# define DEBUG_REG_TX_RD_WAIT 0x00100000 /* waiting for status */
# define DEBUG_REG_TX_RD_PASUE 0x00200000 /* generating pause */
# define DEBUG_REG_TX_RD_WRTG 0x00300000 /* wr stat flush fifo */
# define DEBUG_REG_TX_PAUSE 0x00080000 /* Tx in pause condition */
# define DEBUG_REG_TX_CTRL_STATE 0x00060000 /* Tx frame controller state */
# define DEBUG_REG_TX_CTRL_IDLE 0x00090000 /* idle */
# define DEBUG_REG_TX_CTRL_WAIT 0x00020000 /* waiting for status*/
# define DEBUG_REG_TX_CTRL_PAUSE 0x00040000 /* generating pause */
# define DEBUG_REG_TX_CTRL_XFER 0x00060000 /* transferring input */
# define DEBUG_REG_TX_ACTIVE 0x00010000 /* Tx actively transmitting */
# define DEBUG_REG_RSVD3 0x0000fc00 /* */
# define DEBUG_REG_RX_STATE 0x00000300 /* Rx fifo state */
# define DEBUG_REG_RX_EMPTY 0x00000000 /* empty */
# define DEBUG_REG_RX_LOW 0x00000100 /* below threshold */
# define DEBUG_REG_RX_HIGH 0x00000200 /* above threshold */
# define DEBUG_REG_RX_FULL 0x00000300 /* full */
# define DEBUG_REG_RSVD4 0x00000080 /* */
# define DEBUG_REG_RX_RD_STATE 0x00000060 /* Rx rd ctrl state */
# define DEBUG_REG_RX_RD_IDLE 0x00000000 /* idle */
# define DEBUG_REG_RX_RD_RDG_FR 0x00000020 /* reading frame data */
# define DEBUG_REG_RX_RD_RDG_STA 0x00000040 /* reading status */
# define DEBUG_REG_RX_RD_FLUSH 0x00000060 /* flush fr data/stat */
# define DEBUG_REG_RX_ACTIVE 0x00000010 /* Rx wr ctlr active */
# define DEBUG_REG_RSVD5 0x00000008 /* */
# define DEBUG_REG_SM_FIFO_RW_STA 0x00000006 /* small fifo rd/wr state */
# define DEBUG_REG_RX_RECVG 0x00000001 /* Rx actively receiving data */
# define REM_WAKEUP_FR_REG 0x0028
# define PMT_CTRL_STAT_REG 0x002c
/* 0x0030-0x0034 reserved */
# define INTRP_STATUS_REG 0x0038
# define INTRP_STATUS_REG_RSVD1 0x0000fc00 /* */
# define INTRP_STATUS_REG_TSI 0x00000200 /* time stamp int stat */
# define INTRP_STATUS_REG_RSVD2 0x00000100 /* */
# define INTRP_STATUS_REG_RCOI 0x00000080 /* rec checksum offload int */
# define INTRP_STATUS_REG_TI 0x00000040 /* tx int stat */
# define INTRP_STATUS_REG_RI 0x00000020 /* rx int stat */
# define INTRP_STATUS_REG_NI 0x00000010 /* normal int summary */
# define INTRP_STATUS_REG_PMTI 0x00000008 /* PMT int */
# define INTRP_STATUS_REG_ANC 0x00000004 /* auto negotiation complete */
# define INTRP_STATUS_REG_LSC 0x00000002 /* link status change */
# define INTRP_STATUS_REG_MII 0x00000001 /* rgMii/sgMii int */
# define INTRP_MASK_REG 0x003c
# define INTRP_MASK_REG_RSVD1 0xfc00 /* */
# define INTRP_MASK_REG_TSIM 0x0200 /* time stamp int mask */
# define INTRP_MASK_REG_RSVD2 0x01f0 /* */
# define INTRP_MASK_REG_PMTIM 0x0000 /* PMT int mask */
# define INTRP_MASK_REG_ANCM 0x0000 /* auto negotiation compl mask */
# define INTRP_MASK_REG_LSCM 0x0000 /* link status change mask */
# define INTRP_MASK_REG_MIIM 0x0000 /* rgMii/sgMii int mask */
# define MAC_ADR_0_HIGH_REG 0x0040
# define MAC_ADR_0_LOW_REG 0x0044
/* additional pairs of registers for MAC addresses 1-15 */
# define MAC_ADR_HIGH_REG_N(n) (((n) < 16) ? \
(MAC_ADR_0_HIGH_REG + (n) * 8) : \
(MAC_ADR16_HIGH_REG + ((n) - 16) * 8))
# define MAC_ADR_LOW_REG_N(n) (((n) < 16) ? \
(MAC_ADR_0_LOW_REG + (n) * 8) : \
(MAC_ADR16_LOW_REG + ((n) - 16) * 8))
# define AN_CONTROL_REG 0x00c0
# define AN_CONTROL_REG_RSVRD1 0xfff80000 /* */
# define AN_CONTROL_REG_SGM_RAL 0x00040000 /* sgmii ral control */
# define AN_CONTROL_REG_LR 0x00020000 /* lock to reference */
# define AN_CONTROL_REG_ECD 0x00010000 /* enable comma detect */
# define AN_CONTROL_REG_RSVRD2 0x00008000 /* */
# define AN_CONTROL_REG_ELE 0x00004000 /* external loopback enable */
# define AN_CONTROL_REG_RSVRD3 0x00002000 /* */
# define AN_CONTROL_REG_ANE 0x00001000 /* auto negotiation enable */
# define AN_CONTROL_REG_RSRVD4 0x00000c00 /* */
# define AN_CONTROL_REG_RAN 0x00000200 /* restart auto negotiation */
# define AN_CONTROL_REG_RSVRD5 0x000001ff /* */
# define AN_STATUS_REG 0x00c4
# define AN_STATUS_REG_RSVRD1 0xfffffe00 /* */
# define AN_STATUS_REG_ES 0x00000100 /* extended status */
# define AN_STATUS_REG_RSVRD2 0x000000c0 /* */
# define AN_STATUS_REG_ANC 0x00000020 /* auto-negotiation complete */
# define AN_STATUS_REG_RSVRD3 0x00000010 /* */
# define AN_STATUS_REG_ANA 0x00000008 /* auto-negotiation ability */
# define AN_STATUS_REG_LS 0x00000004 /* link status */
# define AN_STATUS_REG_RSVRD4 0x00000003 /* */
# define AN_ADVERTISE_REG 0x00c8
# define AN_LNK_PRTNR_ABIL_REG 0x00cc
# define AN_EXPANDSION_REG 0x00d0
# define TBI_EXT_STATUS_REG 0x00d4
# define SG_RG_SMII_STATUS_REG 0x00d8
# define LINK_STATUS_REG 0x00d8
# define LINK_STATUS_REG_RSVRD1 0xffffffc0 /* */
# define LINK_STATUS_REG_FCD 0x00000020 /* false carrier detect */
# define LINK_STATUS_REG_JT 0x00000010 /* jabber timeout */
# define LINK_STATUS_REG_UP 0x00000008 /* link status */
# define LINK_STATUS_REG_SPD 0x00000006 /* link speed */
# define LINK_STATUS_REG_SPD_2_5 0x00000000 /* 10M 2.5M * 4 */
# define LINK_STATUS_REG_SPD_25 0x00000002 /* 100M 25M * 4 */
# define LINK_STATUS_REG_SPD_125 0x00000004 /* 1G 125M * 8 */
# define LINK_STATUS_REG_F_DUPLEX 0x00000001 /* full duplex */
/* 0x00dc-0x00fc reserved */
/* MMC Register Map is from 0x0100-0x02fc */
# define MMC_CNTRL_REG 0x0100
# define MMC_INTR_RX_REG 0x0104
# define MMC_INTR_TX_REG 0x0108
# define MMC_INTR_MASK_RX_REG 0x010C
# define MMC_INTR_MASK_TX_REG 0x0110
# define NUM_MULTCST_FRM_RCVD_G 0x0190
/* 0x0300-0x06fc reserved */
/* precision time protocol time stamp registers */
# define TS_CTL_REG 0x0700
# define TS_CTL_ATSFC 0x00080000
# define TS_CTL_TSENMAC 0x00040000
# define TS_CTL_TSCLKTYPE 0x00030000
# define TS_CTL_TSCLK_ORD 0x00000000
# define TS_CTL_TSCLK_BND 0x00010000
# define TS_CTL_TSCLK_ETE 0x00020000
# define TS_CTL_TSCLK_PTP 0x00030000
# define TS_CTL_TSMSTRENA 0x00008000
# define TS_CTL_TSEVNTENA 0x00004000
# define TS_CTL_TSIPV4ENA 0x00002000
# define TS_CTL_TSIPV6ENA 0x00001000
# define TS_CTL_TSIPENA 0x00000800
# define TS_CTL_TSVER2ENA 0x00000400
# define TS_CTL_TSCTRLSSR 0x00000200
# define TS_CTL_TSENALL 0x00000100
# define TS_CTL_TSADDREG 0x00000020
# define TS_CTL_TSTRIG 0x00000010
# define TS_CTL_TSUPDT 0x00000008
# define TS_CTL_TSINIT 0x00000004
# define TS_CTL_TSCFUPDT 0x00000002
# define TS_CTL_TSENA 0x00000001
# define TS_SUB_SEC_INCR_REG 0x0704
# define TS_HIGH_REG 0x0708
# define TS_LOW_REG 0x070c
# define TS_HI_UPDT_REG 0x0710
# define TS_LO_UPDT_REG 0x0714
# define TS_APPEND_REG 0x0718
# define TS_TARG_TIME_HIGH_REG 0x071c
# define TS_TARG_TIME_LOW_REG 0x0720
# define TS_HIGHER_WD_REG 0x0724
# define TS_STATUS_REG 0x072c
/* 0x0730-0x07fc reserved */
# define MAC_ADR16_HIGH_REG 0x0800
# define MAC_ADR16_LOW_REG 0x0804
/* additional pairs of registers for MAC addresses 17-31 */
# define MAC_ADR_MAX 32
# define QFEC_INTRP_SETUP (INTRP_EN_REG_AIE \
| INTRP_EN_REG_FBE \
| INTRP_EN_REG_RWE \
| INTRP_EN_REG_RSE \
| INTRP_EN_REG_RUE \
| INTRP_EN_REG_UNE \
| INTRP_EN_REG_OVE \
| INTRP_EN_REG_TJE \
| INTRP_EN_REG_TSE \
| INTRP_EN_REG_NIE \
| INTRP_EN_REG_RIE \
| INTRP_EN_REG_TIE)
/*
* ASIC Ethernet clock register definitions:
* address offsets and some register definitions
*/
# define EMAC_CLK_REG_BASE 0x94020000
/*
* PHY clock PLL register locations
*/
# define ETH_MD_REG 0x02A4
# define ETH_NS_REG 0x02A8
/* definitions of NS_REG control bits
*/
# define ETH_NS_SRC_SEL 0x0007
# define ETH_NS_PRE_DIV_MSK 0x0018
# define ETH_NS_PRE_DIV(x) (ETH_NS_PRE_DIV_MSK & (x << 3))
# define ETH_NS_MCNTR_MODE_MSK 0x0060
# define ETH_NS_MCNTR_MODE_BYPASS 0x0000
# define ETH_NS_MCNTR_MODE_SWALLOW 0x0020
# define ETH_NS_MCNTR_MODE_DUAL 0x0040
# define ETH_NS_MCNTR_MODE_SINGLE 0x0060
# define ETH_NS_MCNTR_RST 0x0080
# define ETH_NS_MCNTR_EN 0x0100
# define EMAC_PTP_NS_CLK_EN 0x0200
# define EMAC_PTP_NS_CLK_INV 0x0400
# define EMAC_PTP_NS_ROOT_EN 0x0800
/* clock sources
*/
# define CLK_SRC_TCXO 0x0
# define CLK_SRC_PLL_GLOBAL 0x1
# define CLK_SRC_PLL_ARM 0x2
# define CLK_SRC_PLL_QDSP6 0x3
# define CLK_SRC_PLL_EMAC 0x4
# define CLK_SRC_EXT_CLK2 0x5
# define CLK_SRC_EXT_CLK1 0x6
# define CLK_SRC_CORE_TEST 0x7
# define ETH_MD_M(x) (x << 16)
# define ETH_MD_2D_N(x) ((~(x) & 0xffff))
# define ETH_NS_NM(x) ((~(x) << 16) & 0xffff0000)
/*
* PHY interface clock divider
*/
# define ETH_X_EN_NS_REG 0x02AC
# define ETH_RX_CLK_FB_INV 0x80
# define ETH_RX_CLK_FB_EN 0x40
# define ETH_TX_CLK_FB_INV 0x20
# define ETH_TX_CLK_FB_EN 0x10
# define ETH_RX_CLK_INV 0x08
# define ETH_RX_CLK_EN 0x04
# define ETH_TX_CLK_INV 0x02
# define ETH_TX_CLK_EN 0x01
# define ETH_X_EN_NS_DEFAULT \
(ETH_RX_CLK_FB_EN | ETH_TX_CLK_FB_EN | ETH_RX_CLK_EN | ETH_TX_CLK_EN)
# define EMAC_PTP_MD_REG 0x02B0
/* PTP clock divider
*/
# define EMAC_PTP_NS_REG 0x02B4
/*
* clock interface pin controls
*/
# define EMAC_NS_REG 0x02B8
# define EMAC_RX_180_CLK_INV 0x2000
# define EMAC_RX_180_CLK_EN 0x1000
# define EMAC_RX_180_CLK_EN_INV (EMAC_RX_180_CLK_INV | EMAC_RX_180_CLK_EN)
# define EMAC_TX_180_CLK_INV 0x0800
# define EMAC_TX_180_CLK_EN 0x0400
# define EMAC_TX_180_CLK_EN_INV (EMAC_TX_180_CLK_INV | EMAC_TX_180_CLK_EN)
# define EMAC_REVMII_RX_CLK_INV 0x0200
# define EMAC_REVMII_RX_CLK_EN 0x0100
# define EMAC_RX_CLK_INV 0x0080
# define EMAC_RX_CLK_EN 0x0040
# define EMAC_REVMII_TX_CLK_INV 0x0020
# define EMAC_REVMII_TX_CLK_EN 0x0010
# define EMAC_TX_CLK_INV 0x0008
# define EMAC_TX_CLK_EN 0x0004
# define EMAC_RX_R_CLK_EN 0x0002
# define EMAC_TX_R_CLK_EN 0x0001
# define EMAC_NS_DEFAULT \
(EMAC_RX_180_CLK_EN_INV | EMAC_TX_180_CLK_EN_INV \
| EMAC_REVMII_RX_CLK_EN | EMAC_REVMII_TX_CLK_EN \
| EMAC_RX_CLK_EN | EMAC_TX_CLK_EN \
| EMAC_RX_R_CLK_EN | EMAC_TX_R_CLK_EN)
/*
*
*/
# define EMAC_TX_FS_REG 0x02BC
# define EMAC_RX_FS_REG 0x02C0
/*
* Ethernet controller PHY interface select
*/
# define EMAC_PHY_INTF_SEL_REG 0x18030
# define EMAC_PHY_INTF_SEL_MII 0x0
# define EMAC_PHY_INTF_SEL_RGMII 0x1
# define EMAC_PHY_INTF_SEL_REVMII 0x7
# define EMAC_PHY_INTF_SEL_MASK 0x7
/*
* MDIO addresses
*/
# define EMAC_PHY_ADDR_REG 0x18034
# define EMAC_REVMII_PHY_ADDR_REG 0x18038
/*
* clock routing
*/
# define EMAC_CLKMUX_SEL_REG 0x1803c
# define EMAC_CLKMUX_SEL_0 0x1
# define EMAC_CLKMUX_SEL_1 0x2
#endif