Marc Kleine-Budde wrote:
> Hello,
> 
> here "./strip-src -m -i -v 2.6.31" version of the at91 driver,
> featuring:
> - reworked RX-path
> - NAPI for RX and CAN frame errors
>   TX and CAN state is still managed from irq handler
> - CAN chip is turned off after a bus off
>   the NAPI is stopped via a work_queue, because
>   NAPI cannot be disabled from IRQ context
>   (where the bus off is detected).

Very nice. Do you really need to stop the NAPI in case of bus-off? You
stopped the device and no more message will come in anyhow.

> I have to figure out if disabling NAPI is possbile from NAPI conext, so
> CAN state transition might be moved to NAPI, too.
> 
> Bus off and manual restart via the "ip" utility is working. However the
> last remaining bits of the auto recover handling must be removed,
> though.

Did you follow our recent discussion on how to handle auto recovery? The
 preferred solution is to *stop* the device if priv->can.restart_ms ==
0. If it's > 0, the hardware should do the recovery and when the state
changes to error-active, an RESTARTED error message should be generated.

> The driver with NAPI receiving messages with a length of 1 on a 1 Mbit
> link without swapping the messages (tested with pengutronix cansequence).

Please use Vladislavs' canecho_gen/duts at 125kB/sec, which is more
sensitive. Don't forget to fix the unhanded errno ENOBUFS of the write
functions.

> However if I add some "ping -f" load the driver keeps working (tested
> with looking at the data bytes in the driver), but the userspace doesn't
> get enough cpu cycles so AFAICS the pacakges must be dropped in the
> network layer.
> 
> Even on the 1 Mbit link the total number IRQs isn't reduced by NAPI
> (less than 1%). But with NAPI I can enable the Acknowledgement Error
> even on 1 Mbit, and the system doesn't lock up. It generated ~15K Int/s.

With NAPI, message processing is moved from the interrupt to the process
context, which efficiently avoids lockups. As longs as CPU resources are
available, no messages will be dropped.

> Adding again "ping -f" load, the CAN interrupts drop to ~11K Int/s,
> the Ethernet causes only ~7K Int/s.
> 
> Maybe we can think of some really "slow" polling for such interrupts.
> IIRC the SJA1000 has the same problem.

I remember some discussion on this topic a long time ago and there it
was regarded important to get and inspect all bus errors. Throttling was
not accepted.

> Here's the driver, comments welcome. There are some functions, that
> might be moved out of the driver into can/dev.c (alloc_can*frame).

There are various general coding style issues:

In source files, functions should be separated with *one* blank line.

The way you break function declarations is sometimes very unusual, e.g.:

static void at91_irq_err_state
(struct net_device *dev, struct can_frame *cf, enum can_state new_state)

I think:

static void at91_irq_err_state(struct net_device *dev,
                               struct can_frame *cf,
                               enum can_state new_state)

or

static void at91_irq_err_state(struct net_device *dev,
                struct can_frame *cf, enum can_state new_state)

is better readable. More comments inline.

> cheers, Marc
> 
> --- /dev/null 2009-04-16 11:22:08.000000000 +0200
> +++ at91_can.c        2009-09-11 00:15:50.000000000 +0200
> @@ -0,0 +1,1235 @@
> +/*
> + * at91_can.c - CAN network driver for AT91 SoC CAN controller
> + *
> + * (C) 2007 by Hans J. Koch <[email protected]>
> + * (C) 2008, 2009 by Marc Kleine-Budde <[email protected]>
> + *
> + * This software may be distributed under the terms of the GNU General
> + * Public License ("GPL") version 2 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * Send feedback to <[email protected]>
> + *
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/netdevice.h>
> +#include <linux/spinlock.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/if_arp.h>
> +#include <linux/skbuff.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +
> +#include <socketcan/can.h>
> +#include <socketcan/can/error.h>
> +#include <socketcan/can/dev.h>
> +
> +#include <mach/board.h>
> +
> +#define DRV_NAME             "at91_can"
> +#define AT91_NAPI_WEIGHT     12
> +
> +/*
> + * RX/TX Mailbox split
> + * don't dare to touch
> + */
> +#define AT91_MB_RX_NUM               12
> +#define AT91_MB_TX_SHIFT     2
> +
> +#define AT91_MB_RX_FIRST     0
> +#define AT91_MB_RX_LAST              (AT91_MB_RX_FIRST + AT91_MB_RX_NUM - 1)
> +#define AT91_MB_RX_BANKS     2
> +#define AT91_MB_RX_BANK_WIDTH        (AT91_MB_RX_NUM / AT91_MB_RX_BANKS)
> +#define AT91_MB_RX_BANK_LAST(i)      (AT91_MB_RX_FIRST + \
> +                              AT91_MB_RX_BANK_WIDTH * ((i) + 1) - 1)
> +#define AT91_MB_RX_BANK_MASK(i)      (((1 << AT91_MB_RX_BANK_WIDTH) - 1) << \
> +                              (AT91_MB_RX_BANK_WIDTH * (i)))
> +#define AT91_MB_RX_MASK(i)   ((1 << (i)) - 1)
> +
> +#define AT91_MB_TX_NUM               (1 << AT91_MB_TX_SHIFT)
> +#define AT91_MB_TX_FIRST     (AT91_MB_RX_LAST + 1)
> +#define AT91_MB_TX_LAST              (AT91_MB_TX_FIRST + AT91_MB_TX_NUM - 1)
> +
> +#define AT91_NEXT_PRIO_SHIFT (AT91_MB_TX_SHIFT)
> +#define AT91_NEXT_PRIO_MASK  (0xf << AT91_MB_TX_SHIFT)
> +#define AT91_NEXT_MB_MASK    (AT91_MB_TX_NUM - 1)
> +#define AT91_NEXT_MASK               ((AT91_MB_TX_NUM - 1) | 
> AT91_NEXT_PRIO_MASK)
> +
> +/* Common registers */
> +enum at91_reg {
> +     AT91_MR         = 0x000,
> +     AT91_IER        = 0x004,
> +     AT91_IDR        = 0x008,
> +     AT91_IMR        = 0x00C,
> +     AT91_SR         = 0x010,
> +     AT91_BR         = 0x014,
> +     AT91_TIM        = 0x018,
> +     AT91_TIMESTP    = 0x01C,
> +     AT91_ECR        = 0x020,
> +     AT91_TCR        = 0x024,
> +     AT91_ACR        = 0x028,
> +};
> +
> +/* Mailbox registers (0 <= i <= 15) */
> +#define AT91_MMR(i)          (enum at91_reg)(0x200 + ((i) * 0x20))
> +#define AT91_MAM(i)          (enum at91_reg)(0x204 + ((i) * 0x20))
> +#define AT91_MID(i)          (enum at91_reg)(0x208 + ((i) * 0x20))
> +#define AT91_MFID(i)         (enum at91_reg)(0x20C + ((i) * 0x20))
> +#define AT91_MSR(i)          (enum at91_reg)(0x210 + ((i) * 0x20))
> +#define AT91_MDL(i)          (enum at91_reg)(0x214 + ((i) * 0x20))
> +#define AT91_MDH(i)          (enum at91_reg)(0x218 + ((i) * 0x20))
> +#define AT91_MCR(i)          (enum at91_reg)(0x21C + ((i) * 0x20))
> +
> +/* Register bits */
> +#define AT91_MR_AT91EN               BIT(0)
> +#define AT91_MR_LPM          BIT(1)
> +#define AT91_MR_ABM          BIT(2)
> +#define AT91_MR_OVL          BIT(3)
> +#define AT91_MR_TEOF         BIT(4)
> +#define AT91_MR_TTM          BIT(5)
> +#define AT91_MR_TIMFRZ               BIT(6)
> +#define AT91_MR_DRPT         BIT(7)
> +
> +#define AT91_SR_RBSY         BIT(29)
> +
> +#define AT91_MMR_PRIO_SHIFT  (16)
> +
> +#define AT91_MID_MIDE                BIT(29)
> +
> +#define AT91_MSR_MRTR                BIT(20)
> +#define AT91_MSR_MABT                BIT(22)
> +#define AT91_MSR_MRDY                BIT(23)
> +#define AT91_MSR_MMI         BIT(24)
> +
> +#define AT91_MCR_MRTR                BIT(20)
> +#define AT91_MCR_MTCR                BIT(23)
> +
> +/* Mailbox Modes */
> +enum at91_mb_mode {
> +     AT91_MB_MODE_DISABLED   = 0,
> +     AT91_MB_MODE_RX         = 1,
> +     AT91_MB_MODE_RX_OVRWR   = 2,
> +     AT91_MB_MODE_TX         = 3,
> +     AT91_MB_MODE_CONSUMER   = 4,
> +     AT91_MB_MODE_PRODUCER   = 5,
> +};
> +
> +/* Interrupt mask bits */
> +#define AT91_IRQ_MB_RX               ((1 << (AT91_MB_RX_LAST + 1)) \
> +                              - (1 << AT91_MB_RX_FIRST))
> +#define AT91_IRQ_MB_TX               ((1 << (AT91_MB_TX_LAST + 1)) \
> +                              - (1 << AT91_MB_TX_FIRST))
> +#define AT91_IRQ_MB_AL               (AT91_IRQ_MB_RX | AT91_IRQ_MB_TX)
> +
> +#define AT91_IRQ_ERRA                (1 << 16)
> +#define AT91_IRQ_WARN                (1 << 17)
> +#define AT91_IRQ_ERRP                (1 << 18)
> +#define AT91_IRQ_BOFF                (1 << 19)
> +#define AT91_IRQ_SLEEP               (1 << 20)
> +#define AT91_IRQ_WAKEUP              (1 << 21)
> +#define AT91_IRQ_TOVF                (1 << 22)
> +#define AT91_IRQ_TSTP                (1 << 23)
> +#define AT91_IRQ_CERR                (1 << 24)
> +#define AT91_IRQ_SERR                (1 << 25)
> +#define AT91_IRQ_AERR                (1 << 26)
> +#define AT91_IRQ_FERR                (1 << 27)
> +#define AT91_IRQ_BERR                (1 << 28)
> +
> +#define AT91_IRQ_ERR_ALL     (0x1fff0000)
> +#define AT91_IRQ_ERR_FRAME   (AT91_IRQ_CERR | AT91_IRQ_SERR | \
> +                              AT91_IRQ_AERR | AT91_IRQ_FERR | AT91_IRQ_BERR)
> +#define AT91_IRQ_ERR_LINE    (AT91_IRQ_ERRA | AT91_IRQ_WARN | \
> +                              AT91_IRQ_ERRP | AT91_IRQ_BOFF)
> +
> +#define AT91_IRQ_ALL         (0x1fffffff)
> +
> +struct at91_priv {
> +     struct can_priv         can;       /* must be the first member! */
> +     struct net_device       *dev;
> +     struct napi_struct      napi;
> +
> +     void __iomem            *reg_base;
> +
> +     u32                     reg_sr;
> +     unsigned int            tx_next;
> +     unsigned int            tx_echo;
> +     unsigned int            rx_next;
> +
> +     struct work_struct      bus_off_task;
> +
> +     struct clk              *clk;
> +     struct at91_can_data    *pdata;
> +};
> +
> +
> +static struct can_bittiming_const at91_bittiming_const = {
> +     .tseg1_min = 4,
> +     .tseg1_max = 16,
> +     .tseg2_min = 2,
> +     .tseg2_max = 8,
> +     .sjw_max = 4,
> +     .brp_min = 2,
> +     .brp_max = 128,
> +     .brp_inc = 1,
> +};

Why do you don't use tab here fore aligment of the numbers?

> +
> +static inline int get_tx_next_mb(struct at91_priv *priv)
> +{
> +     return (priv->tx_next & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST;
> +}
> +
> +static inline int get_tx_next_prio(struct at91_priv *priv)
> +{
> +     return (priv->tx_next >> AT91_NEXT_PRIO_SHIFT) & 0xf;
> +}
> +
> +static inline int get_tx_echo_mb(struct at91_priv *priv)
> +{
> +     return (priv->tx_echo & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST;
> +}
> +
> +
> +static inline u32 at91_read(struct net_device *dev, enum at91_reg reg)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     return readl(priv->reg_base + reg);
> +}
> +
> +static inline void
> +at91_write(struct net_device *dev, enum at91_reg reg, u32 value)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     writel(value, priv->reg_base + reg);
> +}
> +
> +
> +static inline void
> +set_mb_mode_prio(struct net_device *dev, unsigned int mb,
> +              enum at91_mb_mode mode, int prio)
> +{
> +     at91_write(dev, AT91_MMR(mb),
> +                (mode << 24) | (prio << 16));

Fits on one line?

> +}
> +
> +static inline void
> +set_mb_mode(struct net_device *dev, unsigned int mb, enum at91_mb_mode mode)
> +{
> +     set_mb_mode_prio(dev, mb, mode, 0);
> +}
> +
> +
> +static struct sk_buff *
> +alloc_can_frame(struct net_device *dev, struct can_frame **cf)
> +{
> +     struct sk_buff *skb;
> +
> +     skb = netdev_alloc_skb(dev, sizeof(struct can_frame));
> +     if (unlikely(!skb))
> +             return NULL;
> +
> +     skb->protocol = htons(ETH_P_CAN);
> +     skb->ip_summed = CHECKSUM_UNNECESSARY;
> +     *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
> +
> +     return skb;
> +}
> +
> +
> +static struct sk_buff *
> +alloc_can_err_frame(struct net_device *dev, struct can_frame **cf)
> +{
> +     struct sk_buff *skb;
> +
> +     skb = alloc_can_frame(dev, cf);
> +     if (unlikely(!skb))
> +             return NULL;
> +
> +     memset(*cf, 0, sizeof(struct can_frame));
> +     (*cf)->can_id = CAN_ERR_FLAG;
> +     (*cf)->can_dlc = CAN_ERR_DLC;
> +
> +     return skb;
> +}

Yes, we should put similar functions into dev.[ch]. But I would use the
suffix "skb" instead of "frame" because the return value is of type
"static struct sk_buff *". Patches are welcome.

> +
> +/*
> + * Swtich transceiver on or off
> + */
> +static void at91_transceiver_switch(struct at91_priv *priv, int on)
> +{
> +     if (priv->pdata && priv->pdata->transceiver_switch)
> +             priv->pdata->transceiver_switch(on);
> +}
> +
> +
> +static void at91_setup_mailboxes(struct net_device *dev)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     unsigned int i;
> +
> +     /*
> +      * The first 12 mailboxes are used as a reception FIFO. The
> +      * last mailbox is configured with overwrite option. The
> +      * overwrite flag indicates a FIFO overflow.
> +      */
> +     for (i = AT91_MB_RX_FIRST; i < AT91_MB_RX_LAST; i++)
> +             set_mb_mode(dev, i, AT91_MB_MODE_RX);
> +     set_mb_mode(dev, AT91_MB_RX_LAST, AT91_MB_MODE_RX_OVRWR);
> +
> +     /* The last 4 mailboxes are used for transmitting. */
> +     for (i = AT91_MB_TX_FIRST; i <= AT91_MB_TX_LAST; i++)
> +             set_mb_mode_prio(dev, i, AT91_MB_MODE_TX, 0);
> +
> +     /* Reset tx and rx helper pointers */
> +     priv->tx_next = priv->tx_echo = priv->rx_next = 0;
> +}
> +
> +
> +static int at91_set_bittiming(struct net_device *dev)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     struct can_bittiming *bt = &priv->can.bittiming;
> +     u32 reg_br;
> +
> +     reg_br = ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 24) |
> +             ((bt->brp        - 1) << 16) |
> +             ((bt->sjw        - 1) << 12) |
> +             ((bt->prop_seg   - 1) <<  8) |
> +             ((bt->phase_seg1 - 1) <<  4) |
> +             ((bt->phase_seg2 - 1) <<  0);

No alignment please.

> +
> +     dev_info(ND2D(dev), "writing AT91_BR: 0x%08x\n", reg_br);
> +
> +     at91_write(dev, AT91_BR, reg_br);
> +
> +     return 0;
> +}
> +
> +
> +static void at91_chip_start(struct net_device *dev)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     u32 reg_mr, reg_ier;
> +
> +     /* disable interrupts */
> +     at91_write(dev, AT91_IDR, AT91_IRQ_ALL);
> +
> +     /* disable chip */
> +     reg_mr = at91_read(dev, AT91_MR);
> +     at91_write(dev, AT91_MR, reg_mr & ~AT91_MR_AT91EN);
> +
> +     at91_setup_mailboxes(dev);
> +     at91_transceiver_switch(priv, 1);
> +
> +     /* enable chip */
> +     reg_mr = at91_read(dev, AT91_MR);
> +     at91_write(dev, AT91_MR, reg_mr | AT91_MR_AT91EN);
> +
> +     priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +     /* Enable interrupts */
> +     reg_ier = AT91_IRQ_MB_RX | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME;
> +     at91_write(dev, AT91_IDR, AT91_IRQ_ALL);
> +     at91_write(dev, AT91_IER, reg_ier);
> +
> +     napi_enable(&priv->napi);
> +}
> +
> +
> +static void at91_chip_stop(struct net_device *dev, enum can_state state)
> +{
> +     struct at91_priv *priv = netdev_priv(dev);
> +     u32 reg_mr;
> +
> +     napi_disable(&priv->napi);
> +
> +     /* disable interrupts */
> +     at91_write(dev, AT91_IDR, AT91_IRQ_ALL);
> +
> +     reg_mr = at91_read(dev, AT91_MR);
> +     at91_write(dev, AT91_MR, reg_mr & ~AT91_MR_AT91EN);
> +
> +     at91_transceiver_switch(priv, 0);
> +     priv->can.state = state;
> +}
> +
> +
> +/*
> + * theory of operation:
> + *
> + * Accoring to the datasheet priority 0 is the highest priority, 15 is

s/AAccoring/According/ ?

> + * the lowest. If two mailboxes have the same priority level the
> + * message of the mailbox with the lowest number is sent first.
> + *
> + * We use the first TX mailbox mailbox (AT91_MB_TX_FIRST) with prio 0,

s/mailbox mailbox/mailbox/ ?

> + * then the next mailbox with prio 0, and so on, until all mailboxes
> + * are used. Then we start from the beginning with mailbox
> + * AT91_MB_TX_FIRST, but with prio 1, mailbox AT91_MB_TX_FIRST + 1
> + * prio 1. When we reach the last mailbox with prio 15, we have to
> + * stop sending, waiting for all messages to be delivered, than start

s/than/then/

Thanks.

Wolfgang.
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to