Commit 61b9da92 authored by David S. Miller's avatar David S. Miller

Merge tag 'arcnet-for-4.4-rc1' of git://git.pengutronix.de/git/mgr/linux

Michael Grzeschik says:

====================
This series includes code simplifaction. The main changes are the correct
xceiver handling (enable/disable) of the com20020 cards. The driver now handles
link status change detection. The EAE PCI-ARCNET cards now make use of the
rotary encoded subdevice indexing and got support for led triggers on transmit
and reconnection events.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4b3418fb 59fbcbc6
......@@ -237,6 +237,8 @@ struct Outgoing {
numsegs; /* number of segments */
};
#define ARCNET_LED_NAME_SZ (IFNAMSIZ + 6)
struct arcnet_local {
uint8_t config, /* current value of CONFIG register */
timeout, /* Extended timeout for COM20020 */
......@@ -260,6 +262,13 @@ struct arcnet_local {
/* On preemtive and SMB a lock is needed */
spinlock_t lock;
struct led_trigger *tx_led_trig;
char tx_led_trig_name[ARCNET_LED_NAME_SZ];
struct led_trigger *recon_led_trig;
char recon_led_trig_name[ARCNET_LED_NAME_SZ];
struct timer_list timer;
/*
* Buffer management: an ARCnet card has 4 x 512-byte buffers, each of
* which can be used for either sending or receiving. The new dynamic
......@@ -309,6 +318,8 @@ struct arcnet_local {
int (*reset)(struct net_device *dev, int really_reset);
void (*open)(struct net_device *dev);
void (*close)(struct net_device *dev);
void (*datatrigger) (struct net_device * dev, int enable);
void (*recontrigger) (struct net_device * dev, int enable);
void (*copy_to_card)(struct net_device *dev, int bufnum,
int offset, void *buf, int count);
......@@ -319,6 +330,16 @@ struct arcnet_local {
void __iomem *mem_start; /* pointer to ioremap'ed MMIO */
};
enum arcnet_led_event {
ARCNET_LED_EVENT_RECON,
ARCNET_LED_EVENT_OPEN,
ARCNET_LED_EVENT_STOP,
ARCNET_LED_EVENT_TX,
};
void arcnet_led_event(struct net_device *netdev, enum arcnet_led_event event);
void devm_arcnet_led_init(struct net_device *netdev, int index, int subid);
#if ARCNET_DEBUG_MAX & D_SKB
void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc);
#else
......
......@@ -52,6 +52,8 @@
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/leds.h>
#include "arcdevice.h"
#include "com9026.h"
......@@ -189,6 +191,71 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum,
#endif
/* Trigger a LED event in response to a ARCNET device event */
void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event)
{
struct arcnet_local *lp = netdev_priv(dev);
unsigned long led_delay = 350;
unsigned long tx_delay = 50;
switch (event) {
case ARCNET_LED_EVENT_RECON:
led_trigger_blink_oneshot(lp->recon_led_trig,
&led_delay, &led_delay, 0);
break;
case ARCNET_LED_EVENT_OPEN:
led_trigger_event(lp->tx_led_trig, LED_OFF);
led_trigger_event(lp->recon_led_trig, LED_OFF);
break;
case ARCNET_LED_EVENT_STOP:
led_trigger_event(lp->tx_led_trig, LED_OFF);
led_trigger_event(lp->recon_led_trig, LED_OFF);
break;
case ARCNET_LED_EVENT_TX:
led_trigger_blink_oneshot(lp->tx_led_trig,
&tx_delay, &tx_delay, 0);
break;
}
}
EXPORT_SYMBOL_GPL(arcnet_led_event);
static void arcnet_led_release(struct device *gendev, void *res)
{
struct arcnet_local *lp = netdev_priv(to_net_dev(gendev));
led_trigger_unregister_simple(lp->tx_led_trig);
led_trigger_unregister_simple(lp->recon_led_trig);
}
/* Register ARCNET LED triggers for a arcnet device
*
* This is normally called from a driver's probe function
*/
void devm_arcnet_led_init(struct net_device *netdev, int index, int subid)
{
struct arcnet_local *lp = netdev_priv(netdev);
void *res;
res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL);
if (!res) {
netdev_err(netdev, "cannot register LED triggers\n");
return;
}
snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name),
"arc%d-%d-tx", index, subid);
snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name),
"arc%d-%d-recon", index, subid);
led_trigger_register_simple(lp->tx_led_trig_name,
&lp->tx_led_trig);
led_trigger_register_simple(lp->recon_led_trig_name,
&lp->recon_led_trig);
devres_add(&netdev->dev, res);
}
EXPORT_SYMBOL_GPL(devm_arcnet_led_init);
/* Unregister a protocol driver from the arc_proto_map. Protocol drivers
* are responsible for registering themselves, but the unregister routine
* is pretty generic so we'll do it here.
......@@ -314,6 +381,16 @@ static void arcdev_setup(struct net_device *dev)
dev->flags = IFF_BROADCAST;
}
static void arcnet_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
if (!netif_carrier_ok(dev)) {
netif_carrier_on(dev);
netdev_info(dev, "link up\n");
}
}
struct net_device *alloc_arcdev(const char *name)
{
struct net_device *dev;
......@@ -325,6 +402,9 @@ struct net_device *alloc_arcdev(const char *name)
struct arcnet_local *lp = netdev_priv(dev);
spin_lock_init(&lp->lock);
init_timer(&lp->timer);
lp->timer.data = (unsigned long) dev;
lp->timer.function = arcnet_timer;
}
return dev;
......@@ -423,8 +503,11 @@ int arcnet_open(struct net_device *dev)
lp->hw.intmask(dev, lp->intmask);
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
netif_carrier_off(dev);
netif_start_queue(dev);
mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN);
return 0;
out_module_put:
......@@ -438,7 +521,11 @@ int arcnet_close(struct net_device *dev)
{
struct arcnet_local *lp = netdev_priv(dev);
arcnet_led_event(dev, ARCNET_LED_EVENT_STOP);
del_timer_sync(&lp->timer);
netif_stop_queue(dev);
netif_carrier_off(dev);
/* flush TX and disable RX */
lp->hw.intmask(dev, 0);
......@@ -515,7 +602,7 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
struct ArcProto *proto;
int txbuf;
unsigned long flags;
int freeskb, retval;
int retval;
arc_printk(D_DURING, dev,
"transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
......@@ -554,15 +641,13 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
* the package later - forget about it now
*/
dev->stats.tx_bytes += skb->len;
freeskb = 1;
dev_kfree_skb(skb);
} else {
/* do it the 'split' way */
lp->outgoing.proto = proto;
lp->outgoing.skb = skb;
lp->outgoing.pkt = pkt;
freeskb = 0;
if (proto->continue_tx &&
proto->continue_tx(dev, txbuf)) {
arc_printk(D_NORMAL, dev,
......@@ -574,7 +659,6 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
lp->next_tx = txbuf;
} else {
retval = NETDEV_TX_BUSY;
freeskb = 0;
}
arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
......@@ -588,10 +672,9 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
__FILE__, __LINE__, __func__, lp->hw.status(dev));
spin_unlock_irqrestore(&lp->lock, flags);
if (freeskb)
dev_kfree_skb(skb);
arcnet_led_event(dev, ARCNET_LED_EVENT_TX);
spin_unlock_irqrestore(&lp->lock, flags);
return retval; /* no need to try again */
}
EXPORT_SYMBOL(arcnet_send_packet);
......@@ -843,6 +926,13 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n",
status);
if (netif_carrier_ok(dev)) {
netif_carrier_off(dev);
netdev_info(dev, "link down\n");
}
mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
arcnet_led_event(dev, ARCNET_LED_EVENT_RECON);
/* MYRECON bit is at bit 7 of diagstatus */
if (diagstatus & 0x80)
arc_printk(D_RECON, dev, "Put out that recon myself\n");
......@@ -893,6 +983,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
lp->num_recons = lp->network_down = 0;
arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n");
netif_carrier_on(dev);
}
if (didsomething)
......
......@@ -41,6 +41,7 @@
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/leds.h>
#include "arcdevice.h"
#include "com20020.h"
......@@ -62,12 +63,43 @@ module_param(clockp, int, 0);
module_param(clockm, int, 0);
MODULE_LICENSE("GPL");
static void led_tx_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct com20020_dev *card;
struct com20020_priv *priv;
struct com20020_pci_card_info *ci;
card = container_of(led_cdev, struct com20020_dev, tx_led);
priv = card->pci_priv;
ci = priv->ci;
outb(!!value, priv->misc + ci->leds[card->index].green);
}
static void led_recon_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct com20020_dev *card;
struct com20020_priv *priv;
struct com20020_pci_card_info *ci;
card = container_of(led_cdev, struct com20020_dev, recon_led);
priv = card->pci_priv;
ci = priv->ci;
outb(!!value, priv->misc + ci->leds[card->index].red);
}
static void com20020pci_remove(struct pci_dev *pdev);
static int com20020pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct com20020_pci_card_info *ci;
struct com20020_pci_channel_map *mm;
struct net_device *dev;
struct arcnet_local *lp;
struct com20020_priv *priv;
......@@ -84,9 +116,22 @@ static int com20020pci_probe(struct pci_dev *pdev,
ci = (struct com20020_pci_card_info *)id->driver_data;
priv->ci = ci;
mm = &ci->misc_map;
INIT_LIST_HEAD(&priv->list_dev);
if (mm->size) {
ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset;
r = devm_request_region(&pdev->dev, ioaddr, mm->size,
"com20020-pci");
if (!r) {
pr_err("IO region %xh-%xh already allocated.\n",
ioaddr, ioaddr + mm->size - 1);
return -EBUSY;
}
priv->misc = ioaddr;
}
for (i = 0; i < ci->devcount; i++) {
struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
struct com20020_dev *card;
......@@ -96,6 +141,7 @@ static int com20020pci_probe(struct pci_dev *pdev,
ret = -ENOMEM;
goto out_port;
}
dev->dev_port = i;
dev->netdev_ops = &com20020_netdev_ops;
......@@ -131,6 +177,13 @@ static int com20020pci_probe(struct pci_dev *pdev,
lp->timeout = timeout;
lp->hw.owner = THIS_MODULE;
/* Get the dev_id from the PLX rotary coder */
if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15))
dev->dev_id = 0xc;
dev->dev_id ^= inb(priv->misc + ci->rotary) >> 4;
snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i);
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
pr_err("IO address %Xh is empty!\n", ioaddr);
ret = -EIO;
......@@ -148,14 +201,41 @@ static int com20020pci_probe(struct pci_dev *pdev,
card->index = i;
card->pci_priv = priv;
card->tx_led.brightness_set = led_tx_set;
card->tx_led.default_trigger = devm_kasprintf(&pdev->dev,
GFP_KERNEL, "arc%d-%d-tx",
dev->dev_id, i);
card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"pci:green:tx:%d-%d",
dev->dev_id, i);
card->tx_led.dev = &dev->dev;
card->recon_led.brightness_set = led_recon_set;
card->recon_led.default_trigger = devm_kasprintf(&pdev->dev,
GFP_KERNEL, "arc%d-%d-recon",
dev->dev_id, i);
card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"pci:red:recon:%d-%d",
dev->dev_id, i);
card->recon_led.dev = &dev->dev;
card->dev = dev;
ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
if (ret)
goto out_port;
ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
if (ret)
goto out_port;
dev_set_drvdata(&dev->dev, card);
ret = com20020_found(dev, IRQF_SHARED);
if (ret)
goto out_port;
devm_arcnet_led_init(dev, dev->dev_id, i);
list_add(&card->list, &priv->list_dev);
}
......@@ -234,6 +314,18 @@ static struct com20020_pci_card_info card_info_eae_arc1 = {
.size = 0x08,
},
},
.misc_map = {
.bar = 2,
.offset = 0x10,
.size = 0x04,
},
.leds = {
{
.green = 0x0,
.red = 0x1,
},
},
.rotary = 0x0,
.flags = ARC_CAN_10MBIT,
};
......@@ -251,6 +343,21 @@ static struct com20020_pci_card_info card_info_eae_ma1 = {
.size = 0x08,
}
},
.misc_map = {
.bar = 2,
.offset = 0x10,
.size = 0x04,
},
.leds = {
{
.green = 0x0,
.red = 0x1,
}, {
.green = 0x2,
.red = 0x3,
},
},
.rotary = 0x0,
.flags = ARC_CAN_10MBIT,
};
......
......@@ -118,7 +118,7 @@ int com20020_check(struct net_device *dev)
arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
}
lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
/* set node ID to 0x42 (but transmitter is disabled, so it's okay) */
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG);
......@@ -131,11 +131,6 @@ int com20020_check(struct net_device *dev)
}
arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status);
/* Enable TX */
lp->config |= TXENcfg;
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
arcnet_outb(arcnet_inb(ioaddr, 8), ioaddr, COM20020_REG_W_XREG);
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
ioaddr, COM20020_REG_W_COMMAND);
status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS);
......@@ -169,9 +164,33 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr)
return 0;
}
static int com20020_netdev_open(struct net_device *dev)
{
int ioaddr = dev->base_addr;
struct arcnet_local *lp = netdev_priv(dev);
lp->config |= TXENcfg;
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
return arcnet_open(dev);
}
static int com20020_netdev_close(struct net_device *dev)
{
int ioaddr = dev->base_addr;
struct arcnet_local *lp = netdev_priv(dev);
arcnet_close(dev);
/* disable transmitter */
lp->config &= ~TXENcfg;
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
return 0;
}
const struct net_device_ops com20020_netdev_ops = {
.ndo_open = arcnet_open,
.ndo_stop = arcnet_close,
.ndo_open = com20020_netdev_open,
.ndo_stop = com20020_netdev_close,
.ndo_start_xmit = arcnet_send_packet,
.ndo_tx_timeout = arcnet_timeout,
.ndo_set_mac_address = com20020_set_hwaddr,
......@@ -215,7 +234,7 @@ int com20020_found(struct net_device *dev, int shared)
arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND);
}
lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE;
/* Default 0x38 + register: Node ID */
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG);
......@@ -274,7 +293,7 @@ static int com20020_reset(struct net_device *dev, int really_reset)
dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS));
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2);
lp->config |= (lp->timeout << 3) | (lp->backplane << 2);
/* power-up defaults */
arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG);
arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
......
......@@ -26,6 +26,7 @@
*/
#ifndef __COM20020_H
#define __COM20020_H
#include <linux/leds.h>
int com20020_check(struct net_device *dev);
int com20020_found(struct net_device *dev, int shared);
......@@ -36,6 +37,11 @@ extern const struct net_device_ops com20020_netdev_ops;
#define PLX_PCI_MAX_CARDS 2
struct ledoffsets {
int green;
int red;
};
struct com20020_pci_channel_map {
u32 bar;
u32 offset;
......@@ -47,6 +53,10 @@ struct com20020_pci_card_info {
int devcount;
struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS];
struct com20020_pci_channel_map misc_map;
struct ledoffsets leds[PLX_PCI_MAX_CARDS];
int rotary;
unsigned int flags;
};
......@@ -54,12 +64,16 @@ struct com20020_pci_card_info {
struct com20020_priv {
struct com20020_pci_card_info *ci;
struct list_head list_dev;
resource_size_t misc;
};
struct com20020_dev {
struct list_head list;
struct net_device *dev;
struct led_classdev tx_led;
struct led_classdev recon_led;
struct com20020_priv *pci_priv;
int index;
};
......
......@@ -283,6 +283,13 @@ static inline void led_trigger_register_simple(const char *name,
static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
static inline void led_trigger_event(struct led_trigger *trigger,
enum led_brightness event) {}
static inline void led_trigger_blink(struct led_trigger *trigger,
unsigned long *delay_on,
unsigned long *delay_off) {}
static inline void led_trigger_blink_oneshot(struct led_trigger *trigger,
unsigned long *delay_on,
unsigned long *delay_off,
int invert) {}
static inline void led_trigger_set_default(struct led_classdev *led_cdev) {}
static inline void led_trigger_set(struct led_classdev *led_cdev,
struct led_trigger *trigger) {}
......
Markdown is supported
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