irda: driver for Freescale FIRI controller
Subsystems (2)
PATCH 1/1 irda: driver for Freescale FIRI controller
From: Zhang Wei <[email protected]>
The driver supports SIR, MIR, FIR modes and maximum 4000000bps rate.
[AV: few small fixes, plus had made platform ops passing via node->data
to avoid #ifdef stuff in the fsl_soc (think DIU). ]
---
If anyone from the Freescale is already working on this patch for the mainline, I'll readily step back.
drivers/net/irda/Kconfig | 5 + drivers/net/irda/Makefile | 1 + drivers/net/irda/fsl_ir.c | 792 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/fsl_ir.h | 207 ++++++++++++ 4 files changed, 1005 insertions(+), 0 deletions(-) create mode 100644 drivers/net/irda/fsl_ir.c create mode 100644 include/linux/fsl_ir.h
drivers/net/irda/Kconfig +5 -0
@@ -341,5 +341,10 @@ config MCS_FIR To compile it as a module, choose M here: the module will be called mcs7780. +config FSL_FIR + tristate "Freescale Irda driver" + depends on IRDA && FSL_SOC + help + endmenu
drivers/net/irda/Makefile +1 -0
@@ -19,6 +19,7 @@ obj-$(CONFIG_VIA_FIR) += via-ircc.o obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o obj-$(CONFIG_MCS_FIR) += mcs7780.o obj-$(CONFIG_AU1000_FIR) += au1k_ir.o +obj-$(CONFIG_FSL_FIR) += fsl_ir.o # SIR drivers obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o # dongle drivers for SIR drivers
drivers/net/irda/fsl_ir.c new file +792 -0
792 lines not on this page · raw message
include/linux/fsl_ir.h new file +207 -0
207 lines not on this page · raw message
Signature · 1 line
1.5.5.1
Hi Anton,
Quoted 8 lines · Anton Vorontsov
From: Zhang Wei <[email protected]>
The driver supports SIR, MIR, FIR modes and maximum 4000000bps rate.
Signed-off-by Zhang Wei <[email protected]>[AV: few small fixes, plus had made platform ops passing via
node->data
to avoid #ifdef stuff in thefsl_soc(think DIU). ]Signed-off-by Anton Vorontsov <[email protected]>
Some comments below:
drivers/net/irda/Kconfig config MCS_FIR · line 345
Quoted 10 lines
drivers/net/irda/Kconfig +2
-0@@ -341,5 +341,10 @@ config MCS_FIR To compile it as a module, choose M here: the module will be called mcs7780. +config FSL_FIR + tristate "Freescale Irda driver"
I guess you could be a little more descriptive here. At least specify which chipsets this driver support, and that it is a FIR one.
Quoted 46 lines
drivers/net/irda/fsl_ir.c new file +43
-0@@ -0,0 +1,792 @@ +/* +* Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. +* +* Author: Zhang Wei, [email protected], Oct. 2007 +* +* Description: +* The IrDA driver for Freescale PowerPC MPC8610 processor. The driver +* support SIR and FIR mode. The maximum speed is 4Mbps. +* +* Changelog: +* Oct 2007 Zhang Wei <[email protected]> +* - Initial version. +* +* This file is part of the Linux kernel +* +* This is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +*/ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/of_platform.h> +#include <linux/fsl_ir.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> +#include <net/irda/irlap_frame.h> +#include <net/irda/irlap.h> + +#include <sysdev/fsl_soc.h> + +#define is_sir_speed (speed) ((speed <= 115200) ? 1 : 0) +#define is_sir (ir) (is_sir_speed(ir->speed)) + +static void init_iobuf (iobuff_t *io,void *buff,int size) +{ + io->head = buff; + io->truesize = size;
drivers/net/irda/fsl_ir.c line 53
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+ io->in_frame = FALSE; + io->state =OUTSIDE_FRAME ; + io->data = io->head; + io->skb =NULL ; +} + +static void ir_switch_mode (struct fsl_ir *ir,u32 speed) +{ + +if (ir->speed && (ir->speed <115200 ))/* Switch from SIR to FIR */
Dont you want <= 115200 here ?
Quoted 35 lines
drivers/net/irda/fsl_ir.c new file +35
-0+/* Disable SIRI */ +clrbits32 (&ir->reg_base->scr1,FSL_IR_SCR1_IREN | +FSL_IR_SCR1_SIRIEN ); + else { /* Switch from FIR to SIR */ +/* Disable FIRI */ +out_be32 (&ir->reg_base->firitcr,0 ); +out_be32 (&ir->reg_base->firircr,0 ); + } + +/* Switch the IrDA mode on board */ +if (ir->plat_op && ir->plat_op->set_ir_mode) + ir->plat_op->set_ir_mode (ir, speed); +} + +static int ir_crc_len (struct fsl_ir *ir) +{ +int crc_len; + +switch (ir->speed) { +case 576000 : +case 1152000 : + crc_len =2 ;/* CRC16, 16 bits */ +break ; +case 4000000 : + crc_len =4 ;/* CRC32, 32 bits */ +break ; +default : + crc_len =0 ; +break ; + } +return crc_len; +} + +static void fir_rx (struct fsl_ir *ir,int len) +{
drivers/net/irda/fsl_ir.c line 98
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+struct net_device *ndev =dev_get_drvdata (ir->dev); +struct sk_buff *skb; +int i; + +if (len <=ir_crc_len (ir)) +return ; + +do_gettimeofday (&ir->stamp); +/* Now, for new packet arriving */ + skb =alloc_skb (len +1 ,GFP_ATOMIC );
Please use dev_alloc_skb here.
drivers/net/irda/fsl_ir.c line 103
Quoted 5 lines
drivers/net/irda/fsl_ir.c new file +5
-0+if (!skb) { + ir->stats.rx_dropped++; +return ; + } +skb_reserve (skb,1 );
A comment here describing why you need a 1 byte header would be nice.
Quoted 5 lines
drivers/net/irda/fsl_ir.c new file +5
-0+ +for (i =0 ; i < len; i++) + skb->data[i] =in_8 ((u8 *)&ir->reg_base->rfifo); + + len -=ir_crc_len (ir);
drivers/net/irda/fsl_ir.c line 118
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+skb_put (skb, len); + + ir->stats.rx_packets++; + ir->stats.rx_bytes += len; + + skb->dev = ndev; +skb_reset_mac_header (skb); + skb->protocol =htons (ETH_P_IRDA ); +netif_rx (skb); +}
On a general note, this RX routine could be part of a bottom half. You're calling this from the IRQ handler, and this may not be a good idea.
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+ +static void fir_tx (struct fsl_ir *ir) +{ +int free_bytes; +struct sk_buff *skb = ir->tx_buff.skb; +size_t len; + +if (!skb) +return ; +
drivers/net/irda/fsl_ir.c line 138
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+spin_lock (&ir->tx_lock); +do { + free_bytes =128 - + ((in_be32 (&ir->reg_base->firitsr) >>8 ) &0xff ); +for (len =min (free_bytes, ir->tx_buff.len); len >0 ; + len--, ir->tx_buff.len--) +out_8 ((u8 *)&ir->reg_base->tfifo, + skb->data[skb->len - ir->tx_buff.len]); + }while (ir->tx_buff.len >0 ); +spin_unlock (&ir->tx_lock);
Here I would make use the _irqsave() versions as your skb could be NULLed from there. I know it's really unlikely, but still...
You should also probably take this lock in the set_speed() routine. This is another place where tx_buff could be accessed concurrently.
Quoted 4 lines
drivers/net/irda/fsl_ir.c new file +4
-0+static int fsl_ir_set_speed (struct net_device *ndev,u32 speed) +{ +u32 sbir; +u32 sbmr;
drivers/net/irda/fsl_ir.c line 223
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+struct fsl_ir *ir =netdev_priv (ndev); +struct irlap_cb *self; + +if (is_sir_speed (ir->speed) !=is_sir_speed (speed)) +ir_switch_mode (ir, speed); + + ir->speed = speed; +if (is_sir_speed (speed)) { +/* SIR */ + sbir =89 ;
If you can document this one as well, it would be nice.
Quoted 28 lines
drivers/net/irda/fsl_ir.c new file +28
-0+static irqreturn_t fsl_ir_sir_irq (struct net_device *ndev) +{ +struct fsl_ir *ir =netdev_priv (ndev); +u32 ssr1, ssr2; + ssr1 =in_be32 (&ir->reg_base->ssr1); + ssr2 =in_be32 (&ir->reg_base->ssr2); + +/* Tx is ready */ +if ((ssr1 &FSL_IR_SSR1_TRDY ) && ir->tx_buff.len) { +clrbits32 (&ir->reg_base->scr1,FSL_IR_SCR1_TRDYEN ); +tasklet_schedule (&ir->tx_tasklet); + } + +/* Last Tx transfer is finished */ +if (ssr2 &FSL_IR_SSR2_TXDC && !ir->tx_buff.len) { +if (ir->new_speed) { +fsl_ir_set_speed (ndev, ir->new_speed); + ir->new_speed =0 ; + } +clrbits32 (&ir->reg_base->scr4,FSL_IR_SCR4_TCEN ); + + ir->stats.tx_packets++; + ir->stats.tx_bytes += ir->tx_buff.data + - ir->tx_buff.head; + +netif_wake_queue (ndev); + } +
drivers/net/irda/fsl_ir.c line 415
Quoted 10 lines
drivers/net/irda/fsl_ir.c new file +10
-0+/* Rx is ready */ +if (ssr1 &FSL_IR_SSR1_RRDY ) { +int i; +int rxchars =in_be32 (&ir->reg_base->sfcr) &0x3f ; + +for (i =0 ; i < rxchars; i++) +async_unwrap_char (ndev, &ir->stats, &ir->rx_buff, +sir_get (ir)); + ndev->last_rx = jiffies; + }
Here and in the FIR case, this could be done asynchronously, from a BH.
drivers/net/irda/fsl_ir.c line 490
Quoted 5 lines
drivers/net/irda/fsl_ir.c new file +5
-0+static void fsl_ir_tx_do_tasklet (unsigned long data) +{ +struct fsl_ir *ir = (struct fsl_ir *)data; + +if (is_sir (ir)) {
A detail, but this test will always be true.
Quoted 18 lines
include/linux/fsl_ir.h new file +13
-0+struct fsl_ir { +struct fsl_ir_reg __iomem *reg_base; +struct resource res; +struct device *dev; +int irq; +struct fsl_ir_op *plat_op; + +struct irlap_cb *irlap; +struct qos_info qos; +struct net_device_stats stats; + +int speed; +int new_speed;
include/linux/fsl_ir.h line 200
Quoted 10 lines
include/linux/fsl_ir.h new file +10
-0+u32 clock_in; +int div; + +u8 txb[FSL_SIR_TXBUFF_SIZE ]; +u8 rxb[FSL_SIR_RXBUFF_SIZE ]; +iobuff_t tx_buff; +iobuff_t rx_buff; + +spinlock_t tx_lock;/* Lock for Tx */ +spinlock_t rx_lock;/* Lock for Rx */
This is not used, you could remove it.
include/linux/fsl_ir.h line 203
Quoted 3 lines
include/linux/fsl_ir.h new file +3
-0+ +struct tasklet_struct tx_tasklet; +struct delayed_work rx_work;
This one is not used either, but it would be nice to use it :-)
Thanks a lot for your work.
Cheers,
Samuel.
Quoted 5 lines · Samuel Ortiz
Hi Anton,
Quoted 2 lines · Anton Vorontsov
From: Zhang Wei <[email protected]>
Quoted 7 lines
Quoted 6 lines
The driver supports SIR, MIR, FIR modes and maximum 4000000bps rate.
Signed-off-by Zhang Wei <[email protected]>[AV: few small fixes, plus had made platform ops passing via
node->data
to avoid #ifdef stuff in thefsl_soc(think DIU). ]Signed-off-by Anton Vorontsov <[email protected]>Some comments below:
Sorry, it took me so long to answer. :-/
Much thanks for the comments, will fix them all. That is, your review work isn't in vain, I'm much appreciated it.
Thanks again,
Signature · 3 lines
Anton Vorontsov email: [email protected] irc://irc.freenode.net/bd2