Skip to content
Snippets Groups Projects
Commit ea1256d2 authored by Alessandro Rubini's avatar Alessandro Rubini
Browse files

nic: checkpointing the current situation

parent 8e8d6301
Branches
Tags
No related merge requests found
......@@ -27,7 +27,6 @@
static int __devexit wrn_remove(struct platform_device *pdev)
{
struct wrn_dev *wrn = pdev->dev.platform_data;
struct resource *res;
int i;
spin_lock(&wrn->lock);
......@@ -51,9 +50,12 @@ static int __devexit wrn_remove(struct platform_device *pdev)
iounmap(wrn->bases[i]);
}
if (wrn->irq_registered) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
free_irq(res->start, wrn);
/* Unregister all interrupts that were registered */
for (i = 0; wrn->irq_registered; i++) {
static int irqs[] = WRN_IRQ_NUMBERS;
if (wrn->irq_registered & (1 << i))
free_irq(irqs[i], wrn);
wrn->irq_registered &= ~(1 << i);
}
return 0;
}
......@@ -93,9 +95,13 @@ static int __devinit wrn_probe(struct platform_device *pdev)
struct net_device *netdev;
struct wrn_ep *ep;
struct wrn_dev *wrn = pdev->dev.platform_data;
struct resource *res;
int i, err = 0;
/* Lazily: irqs are not in the resource list */
static int irqs[] = WRN_IRQ_NUMBERS;
static char *irq_names[] = WRN_IRQ_NAMES;
static irq_handler_t irq_handlers[] = WRN_IRQ_HANDLERS;
/* No need to lock_irq: we only protect count and continue unlocked */
spin_lock(&wrn->lock);
if (++wrn->use_count != 1) {
......@@ -112,15 +118,13 @@ static int __devinit wrn_probe(struct platform_device *pdev)
wrn->txd = (void *)&wrn->regs->TX1_D1;
wrn->rxd = (void *)&wrn->regs->RX1_D1;
/* Get the interrupt number from the resource */
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
err = request_irq(res->start, wrn_interrupt,
IRQF_TRIGGER_LOW | IRQF_SHARED,
DRV_NAME,
wrn);
if (err) goto out;
wrn->irq_registered = 1;
/* Register the interrupt handlers (not shared) */
for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
err = request_irq(irqs[i], irq_handlers[i],
IRQF_TRIGGER_LOW, irq_names[i], wrn);
if (err) goto out;
wrn->irq_registered |= 1 << i;
}
/* Reset the device, just to be sure, before making anything */
writel(0, &wrn->regs->CR);
mdelay(10);
......
......@@ -58,31 +58,32 @@ static void wrn_update_link_status(struct net_device *dev)
netdev_dbg(dev, "%s: read %x %x", __func__, bmsr, bmcr);
/* FIXME: the link-up and link-down ops should be clarified */
if (!mii_link_ok(&ep->mii)) { /* no link */
if(!netif_carrier_ok(dev))
if (!mii_link_ok(&ep->mii)) {
/* No link, currently */
if(!netif_carrier_ok(dev)) {
/* No link in software structure: nothing to do */
return;
}
netif_carrier_off(dev);
clear_bit(WRN_EP_UP, &ep->ep_flags);
printk(KERN_INFO "%s: Link down.\n", dev->name);
}
if(netif_carrier_ok(dev))
/* Currently the link is active */
if(netif_carrier_ok(dev)) {
/* Software already knows it's up */
return;
}
/* What follows is the bring-up step */
if (bmcr & BMCR_ANENABLE) { /* AutoNegotiation is enabled */
if (!(bmsr & BMSR_ANEGCOMPLETE)) {
/*
* Do nothing - another interrupt is generated
* when negotiation complete
*/
/* Wait next timer, until it completes */
return;
}
lpa = wrn_phy_read(dev, 0, MII_LPA);
netif_carrier_on(dev);
set_bit(WRN_EP_UP, &ep->ep_flags);
if (0) { /* was commented in minic */
wrn_ep_write(ep, FCR,
......@@ -94,10 +95,11 @@ static void wrn_update_link_status(struct net_device *dev)
printk(KERN_INFO "%s: Link up, lpa 0x%04x.\n",
dev->name, lpa);
} else {
netif_carrier_on(dev);
/* No autonegotiation. It's up immediately */
printk(KERN_INFO "%s: Link up.\n", dev->name);
set_bit(WRN_EP_UP, &ep->ep_flags);
}
netif_carrier_on(dev);
set_bit(WRN_EP_UP, &ep->ep_flags);
/* reset RMON counters */
ecr = wrn_ep_read(ep, ECR);
......@@ -125,7 +127,21 @@ int wrn_ep_open(struct net_device *dev)
struct wrn_ep *ep = netdev_priv(dev);
unsigned long timerarg = (unsigned long)dev;
/* Prepare hardware registers: first config, then bring up */
writel(0
| EP_RFCR_QMODE_W(0x3) /* unqualified port */
| EP_RFCR_PRIO_VAL_W(4), /* some mid priority */
ep->ep_regs->RFCR);
writel(0, &ep->ep_regs->TSCR); /* no stamps */
writel(0
| EP_ECR_PORTID_W(ep->ep_number)
| EP_ECR_RST_CNT
| EP_ECR_TX_EN_FRA
| EP_ECR_RX_EN_FRA,
ep->ep_regs->ECR);
/* Prepare the timer for link-up notifications */
setup_timer(&ep->ep_link_timer, wrn_ep_check_link, timerarg);
mod_timer(&ep->ep_link_timer, jiffies + WRN_LINK_POLL_INTERVAL);
return 0;
......@@ -190,22 +206,7 @@ int wrn_endpoint_probe(struct net_device *dev)
ep->mii.advertising = ADVERTISE_1000XFULL;
ep->mii.full_duplex = 1;
writel(0, ep->ep_regs->TSCR); /* No stamps */
writel(0
| EP_RFCR_QMODE_W(0x3) /* unqualified port */
| EP_RFCR_PRIO_VAL_W(4), /* some mid priority */
ep->ep_regs->RFCR);
/* Enable transmission and reception; reset counters; set portid */
writel(0
| EP_ECR_PORTID_W(ep->ep_number)
| EP_ECR_RST_CNT
| EP_ECR_TX_EN_FRA
| EP_ECR_RX_EN_FRA,
ep->ep_regs->ECR);
/* Finally, register and succeed, or fail an undo */
/* Finally, register and succeed, or fail and undo */
err = register_netdev(dev);
if (err) {
printk(KERN_ERR DRV_NAME "Can't register dev %s\n",
......
......@@ -44,14 +44,7 @@ static struct resource wrn_resources[] = {
[WRN_RES_MEM_PPSG] = __RES( PPSG ),
[WRN_RES_MEM_CALIBRATOR]= __RES( CALIBRATOR ),
[WRN_RES_MEM_NIC] = __RES( NIC ),
[WRN_RES_MEM_TSTAMP] = __RES( TSTAMP ),
/* Last is the interrupt */
[WRN_RES_IRQ] = {
.start = WRN_INTERRUPT,
.end = WRN_INTERRUPT,
.flags = IORESOURCE_IRQ,
}
[WRN_RES_MEM_TSTAMP] = __RES( TSTAMP )
};
#undef __RES
......
......@@ -23,7 +23,7 @@
/*
* The following functions are the standard network device operations.
* They act on the _endpoint_ (as each Linux interface is one endpoint)
* so sometimes a call to something withing ./endpoint.c is performed.
* so sometimes a call to something within ./endpoint.c is performed.
*/
static int wrn_open(struct net_device *dev)
{
......@@ -35,36 +35,33 @@ static int wrn_open(struct net_device *dev)
if (!is_valid_ether_addr(dev->dev_addr))
return -EADDRNOTAVAIL;
/* Acknowledge all possibly pending interrupt sources */
/* FIXME */
/* Enable tx and rx, without timestamping at this point */
/* Most drivers call platform_set_drvdata() but we don't need it */
/* Mark it as down, and start the ep-specific polling timer */
clear_bit(WRN_EP_UP, &ep->ep_flags);
wrn_ep_open(dev);
/* Software-only management is in this file*/
if (netif_queue_stopped(dev)) {
netif_wake_queue(dev);
} else {
netif_start_queue(dev);
}
/* Mark it as down, and start the ep-specific polling timer */
clear_bit(WRN_EP_UP, &ep->ep_flags);
wrn_ep_open(dev);
/* Most drivers call platform_set_drvdata() but we don't need it */
return 0;
}
static int wrn_close(struct net_device *dev)
{
struct wrn_ep *ep = netdev_priv(dev);
int ret;
wrn_ep_close(dev);
clear_bit(WRN_EP_UP, &ep->ep_flags);
if ( (ret = wrn_ep_close(dev)) )
return ret;
/* FIXME: other things to cleanup on close? */
/* FIXME: software-only fixing at close time */
netif_stop_queue(dev);
netif_carrier_off(dev);
clear_bit(WRN_EP_UP, &ep->ep_flags);
return 0;
}
......@@ -90,15 +87,94 @@ static int wrn_set_mac_address(struct net_device *dev, void* vaddr)
return 0;
}
/* This is called with the lock taken */
static int __wrn_alloc_tx_desc(struct wrn_dev *wrn)
{
int ret, i = 0;
do {
/* First increment the position */
wrn->next_txdesc++;
if (unlikely(wrn->next_txdesc >= WRN_NR_TXDESC))
wrn->next_txdesc = 0;
/* then check if it's available */
ret = test_and_set_bit(wrn->next_txdesc, &wrn->tx_mask);
if (!ret)
return wrn->next_txdesc;
} while (++i < WRN_NR_TXDESC);
return -ENOMEM;
}
static void __wrn_tx_desc(struct wrn_dev *wrn, int desc,
void *data, int len)
{
/* FIXME: tx desc */
}
static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
/* FIXME: start_xmit is missing */
return -ENODEV;
struct wrn_ep *ep = netdev_priv(dev);
struct wrn_dev *wrn = ep->wrn;
union skb_shared_tx *shtx = skb_tx(skb);
unsigned long flags;
int desc;
u16 tx_oob = 0;
void *data;
unsigned len;
if (unlikely(skb->len > WRN_MTU)) {
/* FIXME: check this WRN_MTU is needed and used properly */
ep->stats.tx_errors++;
return -EMSGSIZE;
}
/* Allocate a descriptor (start from last allocated) */
spin_lock_irqsave(&wrn->lock, flags);
desc = __wrn_alloc_tx_desc(wrn);
spin_unlock_irqrestore(&wrn->lock, flags);
data = skb->data;
len = skb->len;
spin_lock_irqsave(&ep->lock, flags);
wrn->skb_desc[desc] = skb; /* Save for tx irq and stamping */
netif_stop_queue(dev); /* Queue is stopped until tx is over */
if(test_bit(WRN_EP_STAMPING_TX, &ep->ep_flags)) {
/* FIXME: the hw tx stamping */
#if 0
struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
shtx->in_progress = 1;
*(u16 *) hwts = tx_oob = nic->tx_hwtstamp_oob;
nic->tx_hwtstamp_oob ++;
if(nic->tx_hwtstamp_oob == 60000)
nic->tx_hwtstamp_oob = 1;
} else {
tx_oob = 0;
#endif
}
/* This both copies the data to the descriptr and fires tx */
__wrn_tx_desc(wrn, desc, data, len);
/* We are done, this is trivial maiintainance*/
ep->stats.tx_packets++;
ep->stats.tx_bytes += len;
dev->trans_start = jiffies;
spin_unlock_irqrestore(&ep->lock, flags);
return 0;
}
struct net_device_stats *wrn_get_stats(struct net_device *dev)
{
/* FIXME: getstats is missing */
struct wrn_ep *ep = netdev_priv(dev);
/* FIXME: we should get the RMON information from endpoint */
return &ep->stats;
return NULL;
}
......
......@@ -5,7 +5,8 @@
#include <mach/at91sam9263.h>
/* The interrupt is one of those managed by our WRVIC device */
#define WRN_INTERRUPT 192 /* First vic-managed irq */
#define WRN_IRQ_MAIN 192 /* First vic-managed irq */
#define WRN_IRQ_TSTAMP 193 /* FIXME: interrupt numbers */
/* This is the base address of all the FPGA regions (EBI1, CS0) */
#define FPGA_BASE_ADDRESS 0x70000000
......@@ -85,5 +86,12 @@ struct wrn_desc_rx {
/* Some macros to extract fields from descriptors -- FIXME*/
/* Some more constants */
#define WRN_MTU 1540
/* The number of descriptors is used to look in wrn bitmasks (max is 32) */
#define WRN_NR_TXDESC 8
#define WRN_NR_RXDESC 8
#endif /* __WR_NIC_HARDWARE_H__ */
......@@ -17,6 +17,12 @@
#include "wr-nic.h"
irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id)
{
/* FIXME: the tstamp interrupt */
return IRQ_NONE;
}
int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct wrn_ep *ep = netdev_priv(dev);
......@@ -28,12 +34,14 @@ int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
config.tx_type, config.rx_filter);
switch (config.tx_type) {
/* Set up time stamping on transmission */
case HWTSTAMP_TX_ON:
set_bit(WRN_EP_STAMPING_TX, &ep->ep_flags);
/* FIXME memset(nic->tx_tstable, 0, sizeof(nic->tx_tstable));*/
/* FIXME: enable timestamp on tx in hardware */
break;
case HWTSTAMP_TX_OFF:
/* FIXME: disable timestamp on tx in hardware */
clear_bit(WRN_EP_STAMPING_TX, &ep->ep_flags);
break;
......@@ -47,16 +55,15 @@ int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
*/
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
/* FIXME: disable rx in hardware */
clear_bit(WRN_EP_STAMPING_RX, &ep->ep_flags);
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
default: /* All other case: activate stamping */
/* FIXME: enable rx in hardware */
set_bit(WRN_EP_STAMPING_RX, &ep->ep_flags);
config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
/* FIXME: minic_update_ts_config(nic); */
......
......@@ -12,34 +12,47 @@
*/
#ifndef __WR_NIC_H__
#define __WR_NIC_H__
#include <linux/mii.h>
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/mii.h> /* Needed for stuct mii_if_info in wrn_dev */
#include <linux/netdevice.h> /* Needed for net_device_stats in wrn_dev */
#include <linux/timer.h> /* Needed for struct time_list in wrn_dev*/
#include "nic-hardware.h" /* Magic numbers: please fix them as needed */
#define DRV_NAME "wr-nic" /* Used in messages and device/driver names */
#define DRV_VERSION "0.1" /* For ethtool->get_drvinfo -- FIXME: auto-vers */
/* Structures we are using but may remain opaque */
struct net_device;
/*
* Interrupt information should be hidden in resource lists,
* but the looping code would be hairy). So let's define three
* arrays of the same size, and code loops over these
*/
#define WRN_IRQ_NUMBERS {WRN_IRQ_MAIN, WRN_IRQ_TSTAMP}
#define WRN_IRQ_NAMES {"wr-nic", "wr-tstamp"}
#define WRN_IRQ_HANDLERS {wrn_interrupt, wrn_tstamp_interrupt}
/*
* This is the main data structure for our NIC device
* This is the main data structure for our NIC device. As for locking,
* the rule is that _either_ the wrn _or_ the endpoint is locked. Not both.
*/
struct wrn_dev {
/* Base addresses. It's easier with an array, but not all are used */
void __iomem *bases[WRN_NBLOCKS];
struct NIC_WB __iomem *regs; /* shorthand for NIC-block registers */
spinlock_t lock;
struct wrn_d_tx __iomem *txd;
unsigned long tx_mask; /* descriptors in use */
struct wrn_d_rx __iomem *rxd;
unsigned long rx_mask; /* descriptors in use */
spinlock_t lock;
int next_txdesc;
/* For TX descriptors, we must keep track of the ownwes */
struct sk_buff *skb_desc[WRN_NR_TXDESC];
struct net_device *dev[WRN_NR_ENDPOINTS]; /* FIXME: unused */
struct net_device *dev[WRN_NR_ENDPOINTS];
/* FIXME: all dev fields must be verified */
......@@ -136,7 +149,7 @@ struct wrn_phase_req {
#define WRN_CAL_RX_OFF 4
#define WRN_CAL_RX_CHECK 5
/* This a WR-specific register in the dmdio space */
/* This a WR-specific register in the mdio space */
#define WRN_MDIO_WR_SPEC 0x00000010
#define WRN_MDIO_WR_SPEC_TX_CAL 0x01 /* TX calib pattern */
#define WRN_MDIO_WR_SPEC_RX_CAL_STAT 0x02 /* RX calib status */
......@@ -166,6 +179,7 @@ extern void wrn_endpoint_remove(struct net_device *netdev);
/* Following functions from timestamp.c */
extern int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id);
/* Following functions from dmtd.c */
extern int wrn_phase_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment