| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376 |
- From c44de58d0972d05851512ba8cbd928b7adef5187 Mon Sep 17 00:00:00 2001
- From: Kurt Mahan <[email protected]>
- Date: Tue, 8 Jul 2008 17:11:33 -0600
- Subject: [PATCH] Add FlexCAN support.
- LTIBName: mcfv4e-flexcan
- Signed-off-by: Kurt Mahan <[email protected]>
- Signed-off-by: Huan Wang <[email protected]>
- ---
- drivers/net/can/Kconfig | 13 ++
- drivers/net/can/Makefile | 1 +
- drivers/net/can/flexcan/Makefile | 5 +
- drivers/net/can/flexcan/flexcan.c | 378 +++++++++++++++++++++++++++++++++
- drivers/net/can/flexcan/flexcan.h | 148 +++++++++++++
- drivers/net/can/flexcan/mcf548x_can.c | 213 ++++++++++++++++++
- include/asm-m68k/m5485sim.h | 2 +
- include/linux/can/dev.h | 62 ++++++
- include/linux/can/ioctl.h | 152 +++++++++++++
- include/linux/can/version.h | 22 ++
- net/can/Makefile | 3 +
- net/can/dev.c | 292 +++++++++++++++++++++++++
- 12 files changed, 1291 insertions(+), 0 deletions(-)
- create mode 100644 drivers/net/can/flexcan/Makefile
- create mode 100644 drivers/net/can/flexcan/flexcan.c
- create mode 100644 drivers/net/can/flexcan/flexcan.h
- create mode 100644 drivers/net/can/flexcan/mcf548x_can.c
- create mode 100644 include/linux/can/dev.h
- create mode 100644 include/linux/can/ioctl.h
- create mode 100644 include/linux/can/version.h
- create mode 100644 net/can/dev.c
- --- a/drivers/net/can/Kconfig
- +++ b/drivers/net/can/Kconfig
- @@ -12,6 +12,19 @@ config CAN_VCAN
- This driver can also be built as a module. If so, the module
- will be called vcan.
-
- +config CAN_FLEXCAN
- + tristate "Support for Freescale FLEXCAN based chips"
- + depends on CAN && (PPC || M68K || M68KNOMMU)
- + ---help---
- + Say Y here if you want to support for Freescale FlexCAN.
- +
- +config CAN_MCF547X_8X
- + tristate "Freescale MCF547X/MCF548X onboard CAN controller"
- + depends on CAN_FLEXCAN && (M547X || M548X)
- + ---help---
- + Say Y here if you want to support for Freescale MCF547x/MCF548x
- + onboard dualCAN controller.
- +
- config CAN_DEBUG_DEVICES
- bool "CAN devices debugging messages"
- depends on CAN
- --- a/drivers/net/can/Makefile
- +++ b/drivers/net/can/Makefile
- @@ -3,3 +3,4 @@
- #
-
- obj-$(CONFIG_CAN_VCAN) += vcan.o
- +obj-$(CONFIG_CAN_FLEXCAN) += flexcan/
- --- /dev/null
- +++ b/drivers/net/can/flexcan/Makefile
- @@ -0,0 +1,5 @@
- +
- +obj-$(CONFIG_CAN_MCF547X_8X) += flexcan-mcf548x.o
- +
- +flexcan-mcf548x-objs := flexcan.o mcf548x_can.o
- +
- --- /dev/null
- +++ b/drivers/net/can/flexcan/flexcan.c
- @@ -0,0 +1,378 @@
- +/*
- + * flexcan.c
- + *
- + * DESCRIPTION:
- + * CAN bus driver for the alone generic (as possible as) FLEXCAN controller.
- + *
- + * AUTHOR:
- + * Andrey Volkov <[email protected]>
- + *
- + * COPYRIGHT:
- + * 2005-2006, Varma Electronics Oy
- + *
- + * LICENCE:
- + * This program 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.
- + *
- + * 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.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, write to the Free Software
- + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- + *
- + * HISTORY:
- + * 2008-06-23 Support for MCF548x's FlexCAN
- + * Huan, Wang <[email protected]>
- + */
- +
- +
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/interrupt.h>
- +#include <linux/delay.h>
- +#include <linux/netdevice.h>
- +#include <linux/if_arp.h>
- +#include <linux/if_ether.h>
- +#include <linux/can.h>
- +#include <linux/list.h>
- +#include <linux/io.h>
- +
- +#include <linux/can/dev.h>
- +#include <linux/can/error.h>
- +#include "flexcan.h"
- +#include <asm/coldfire.h>
- +#include <asm/m5485sim.h>
- +#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
- +RCSID("$Id$");
- +
- +struct flexcan_priv {
- + struct can_priv can;
- + volatile unsigned long flags;
- + u8 shadow_statflg;
- + u8 shadow_canrier;
- + u8 cur_pri;
- + u8 tx_active;
- +
- + struct list_head tx_head;
- + struct napi_struct napi;
- + struct net_device *dev;
- +};
- +
- +
- +static int flexcan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
- +{
- + struct can_frame *frame = (struct can_frame *)skb->data;
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- + int i, len;
- + int txbuf = 0;
- + u32 can_id, can_ext, tmp, tmp1;
- +
- + /* Transmission inactive */
- + regs->cantxfg[txbuf].can_dlc = MB_CNT_CODE(0x08);
- +
- + can_ext = frame->can_id;
- + if (can_ext & CAN_EFF_FLAG) {
- + /* Frame format is extended */
- + regs->cantxfg[txbuf].can_dlc |= (1 << 21);
- + regs->cantxfg[txbuf].can_dlc |= (1 << 22);
- + can_id = frame->can_id & MB_ID_EXT;
- + if (frame->can_id & CAN_RTR_FLAG)
- + regs->cantxfg[txbuf].can_dlc |= (1 << 20);
- +
- + tmp = (can_id & CAN_SFF_MASK) << 18;
- + tmp1 = can_id >> 11;
- + can_id = tmp | tmp1;
- + regs->cantxfg[txbuf].can_id = can_id;
- + } else {
- + /* Frame format is standard */
- + can_id = frame->can_id & MB_ID_EXT;
- + if (frame->can_id & CAN_RTR_FLAG)
- + regs->cantxfg[txbuf].can_dlc |= (1 << 20);
- +
- + regs->cantxfg[txbuf].can_id = can_id << 18;
- + }
- +
- + len = 8;
- + for (i = 0; i < len; i++)
- + regs->cantxfg[txbuf].data[i] = frame->data[i];
- +
- + regs->cantxfg[txbuf].can_dlc |= len << 16;
- + /* Transmission active */
- + regs->cantxfg[txbuf].can_dlc |= MB_CNT_CODE(0x0c);
- + kfree_skb(skb);
- + return NETDEV_TX_OK;
- +}
- +
- +static void flexcan_tx_timeout(struct net_device *dev)
- +{
- + struct sk_buff *skb;
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- + struct can_frame *frame;
- + int length = 8;
- +
- + /* Diable the interrupts */
- + regs->imask = IMASK_BUFF_DISABLE_ALL;
- +
- + skb = dev_alloc_skb(sizeof(struct can_frame));
- + if (!skb) {
- + if (printk_ratelimit())
- + dev_notice(ND2D(dev), "TIMEOUT packet dropped.\n");
- + return;
- + }
- + frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
- +
- + frame->can_dlc = length;
- +
- + skb->dev = dev;
- + skb->protocol = __constant_htons(ETH_P_CAN);
- + skb->pkt_type = PACKET_BROADCAST;
- + skb->ip_summed = CHECKSUM_UNNECESSARY;
- +
- + netif_rx(skb);
- +}
- +
- +static irqreturn_t flexcan_isr(int irq, void *dev_id)
- +{
- + struct net_device *dev = (struct net_device *)dev_id;
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- + struct net_device_stats *stats = dev->get_stats(dev);
- + struct sk_buff *skb;
- + struct can_frame *frame;
- + u32 iflags, oflags;
- + int i, k;
- + int retval = 1;
- +
- + iflags = regs->iflag;
- + oflags = iflags;
- + for (i = 0; i < 16; i++) {
- + if (iflags & (0x01 << i)) {
- + struct flexcan_mb *mb = ®s->cantxfg[i];
- + int ctrl = mb->can_dlc;
- + int code = (ctrl >> 24) & 0x0f;
- + int length = (ctrl >> 16) & 0x0f;
- + u32 tmp, tmp1;
- +
- + if (code < 8 && (length > 0)) {
- + /* receive frame */
- + skb = dev_alloc_skb(sizeof(struct can_frame));
- + if (!skb)
- + dev_notice(ND2D(dev),
- + "Packets dropped.\n");
- + skb->dev = dev;
- + frame = (struct can_frame *)skb_put(skb,
- + sizeof(struct can_frame));
- +
- + frame->can_id &= 0x0;
- + frame->can_dlc = length;
- + tmp1 = mb->can_id & MB_ID_EXT;
- + if (ctrl & MB_CNT_IDE) {
- + tmp = tmp1;
- + tmp = (tmp >> 18) & CAN_SFF_MASK;
- + frame->can_id = (tmp1 << 11) | tmp;
- + frame->can_id &= CAN_EFF_MASK;
- + frame->can_id |= CAN_EFF_FLAG;
- + if (ctrl & MB_CNT_RTR)
- + frame->can_id |= CAN_RTR_FLAG;
- + } else {
- + frame->can_id = tmp1 >> 18;
- + if (ctrl & MB_CNT_RTR)
- + frame->can_id |= CAN_RTR_FLAG;
- + }
- +
- + for (k = 0; k < 8; k++)
- + frame->data[k] = mb->data[k];
- +
- + mb->can_dlc &= MB_CODE_MASK;
- + mb->can_dlc |= MB_CNT_CODE(0x04);
- +
- + stats->rx_packets++;
- + stats->rx_bytes += frame->can_dlc;
- + skb->dev = dev;
- + skb->protocol = __constant_htons(ETH_P_CAN);
- + skb->ip_summed = CHECKSUM_UNNECESSARY;
- +
- + retval = netif_rx(skb);
- + if (retval == NET_RX_DROP)
- + dev_notice(ND2D(dev),
- + "Packets dropped.\n");
- + } else {
- + /* transmit frame */
- + mb->can_dlc = MB_CNT_CODE(0x04);
- + }
- + }
- + }
- + regs->iflag = oflags;
- +
- + return IRQ_HANDLED;
- +}
- +
- +static int flexcan_do_set_bit_time(struct net_device *dev,
- + struct can_bittime *bt)
- +{
- + struct flexcan_priv *priv = netdev_priv(dev);
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- + int ret = 0;
- + u32 reg;
- +
- + if (bt->type != CAN_BITTIME_STD)
- + return -EINVAL;
- +
- + spin_lock_irq(&priv->can.irq_lock);
- +
- + reg = CANCTRL_PRESDIV(bt->std.brp) | CANCTRL_PSEG1(bt->std.phase_seg1
- + - 1) | CANCTRL_PSEG2(bt->std.phase_seg2 - 1);
- + regs->canctrl &= CANCTRL_BITTIME;
- + regs->canctrl |= (reg | CANCTRL_SAMP(bt->std.sam) |
- + CANCTRL_PROPSEG(bt->std.prop_seg - 1));
- +
- + spin_unlock_irq(&priv->can.irq_lock);
- + return ret;
- +}
- +
- +
- +static int flexcan_open(struct net_device *dev)
- +{
- + int ret, i, j;
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- +
- +#if defined(CONFIG_M547X_8X)
- + MCF_PAR_TIMER = MCF_PAR_TIMER | 0x28;
- + MCF_PAR_TIMER = MCF_PAR_TIMER & 0xf8;
- + MCF_PAR_DSPI = MCF_PAR_DSPI | 0x0a00;
- + MCF_PAR_FECI2CIRQ = MCF_PAR_FECI2CIRQ | 0x0283;
- + MCF_PAR_PSCn(2) = MCF_PAR_PSCn(2) & 0x0f;
- + MCF_PAR_PSCn(2) = MCF_PAR_PSCn(2) | 0x50;
- +#endif
- +
- + regs->canmcr |= CANMCR_SOFTRST;
- + regs->canmcr |= CANMCR_MDIS;
- + udelay(10);
- +
- + if ((regs->canmcr & CANMCR_SOFTRST) != 0x0) {
- + dev_err(ND2D(dev), "Failed to softreset can module.\n");
- + return -1;
- + }
- +
- + /* Enable error and bus off interrupt */
- + regs->canctrl |= (CANCTRL_RJW(3) | CANCTRL_ERRMSK |
- + CANCTRL_BOFFMSK);
- +
- + /* Set lowest buffer transmitted first */
- + regs->canctrl |= CANCTRL_LBUF;
- +
- + for (i = 0; i < 16; i++) {
- + regs->cantxfg[i].can_dlc = 0;
- + regs->cantxfg[i].can_id = 0;
- + for (j = 0; j < 8; j++)
- + regs->cantxfg[i].data[j] = 0;
- +
- + /* Put MB into rx queue */
- + regs->cantxfg[i].can_dlc = MB_CNT_CODE(0x04);
- + }
- +
- + /* acceptance mask/acceptance code (accept everything) */
- + regs->rxgmask = 0x00000000;
- + regs->rx14mask = 0x00000000;
- + regs->rx15mask = 0x00000000;
- + /* extended frame */
- + regs->cantxfg[14].can_dlc |= 0x600000;
- + /* Enable flexcan module */
- + regs->canmcr &= ~CANMCR_MDIS;
- + /* Synchronize with the can bus */
- + regs->canmcr &= ~CANMCR_HALT;
- +
- +#if defined(CONFIG_M547X_8X)
- + for (i = 0; i < 2; i++) {
- + MCF_ICR(ISC_CANn_MBOR(i)) = 0x33;
- + MCF_ICR(ISC_CANn_ERR(i)) = 0x33;
- + MCF_ICR(ISC_CANn_BUSOFF(i)) = 0x33;
- + }
- +
- + ret = request_irq(dev->irq + 64, flexcan_isr, IRQF_DISABLED,
- + dev->name, dev);
- + ret = request_irq(dev->irq + 1 + 64, flexcan_isr, IRQF_DISABLED,
- + dev->name, dev);
- + ret = request_irq(dev->irq + 2 + 64, flexcan_isr, IRQF_DISABLED,
- + dev->name, dev);
- + if (ret < 0) {
- + printk(KERN_ERR "%s - failed to attach interrupt.\n",
- + dev->name);
- + return ret;
- + }
- +#endif
- +
- + /* Enable all interrupts */
- + regs->imask = IMASK_BUFF_ENABLE_ALL;
- + netif_start_queue(dev);
- + return 0;
- +}
- +
- +static int flexcan_close(struct net_device *dev)
- +{
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- +
- + netif_stop_queue(dev);
- +
- + /* Disable all interrupts */
- + regs->imask = IMASK_BUFF_DISABLE_ALL;
- + free_irq(dev->irq + 64, dev);
- + free_irq(dev->irq + 1 + 64, dev);
- + free_irq(dev->irq + 2 + 64, dev);
- +
- + /* Disable module */
- + regs->canmcr |= CANMCR_MDIS;
- + return 0;
- +}
- +
- +int register_flexcandev(struct net_device *dev, int clock_src)
- +{
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- +
- + regs->canmcr &= ~CANMCR_MDIS;
- + udelay(100);
- + regs->canmcr |= (CANMCR_FRZ | CANMCR_HALT);
- + return register_netdev(dev);
- +}
- +EXPORT_SYMBOL(register_flexcandev);
- +
- +void unregister_flexcandev(struct net_device *dev)
- +{
- + struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
- +
- + regs->canmcr |= (CANMCR_FRZ | CANMCR_HALT);
- + regs->canmcr |= CANMCR_MDIS;
- +
- + unregister_netdev(dev);
- +}
- +EXPORT_SYMBOL(unregister_flexcandev);
- +
- +struct net_device *alloc_flexcandev(void)
- +{
- + struct net_device *dev;
- + struct flexcan_priv *priv;
- +
- + dev = alloc_candev(sizeof(struct flexcan_priv));
- + if (!dev)
- + return NULL;
- +
- + priv = netdev_priv(dev);
- + priv->dev = dev;
- + dev->open = flexcan_open;
- + dev->stop = flexcan_close;
- + dev->hard_start_xmit = flexcan_hard_start_xmit;
- + dev->tx_timeout = flexcan_tx_timeout;
- + dev->flags |= IFF_NOARP;
- + priv->can.do_set_bit_time = flexcan_do_set_bit_time;
- + return dev;
- +}
- +EXPORT_SYMBOL(alloc_flexcandev);
- +
- +MODULE_AUTHOR("Andrey Volkov <[email protected]>");
- +MODULE_LICENSE("GPL v2");
- +MODULE_DESCRIPTION("CAN port driver for flexcan based chip");
- --- /dev/null
- +++ b/drivers/net/can/flexcan/flexcan.h
- @@ -0,0 +1,148 @@
- +/*
- + * flexcan.h
- + *
- + * DESCRIPTION:
- + * Definitions of consts/structs to drive the Freescale FLEXCAN.
- + *
- + */
- +
- +#ifndef __FLEXCAN_H__
- +#define __FLEXCAN_H__
- +
- +#include <linux/autoconf.h>
- +#include <linux/types.h>
- +
- +/* FLEXCAN module configuration register (CANMCR) bits */
- +#define CANMCR_MDIS 0x80000000
- +#define CANMCR_FRZ 0x40000000
- +#define CANMCR_HALT 0x10000000
- +#define CANMCR_SOFTRST 0x02000000
- +#define CANMCR_FRZACK 0x01000000
- +#define CANMCR_SUPV 0x00800000
- +#define CANMCR_MAXMB(x) ((x)&0x0f)
- +
- +/* FLEXCAN control register (CANCTRL) bits */
- +#define CANCTRL_PRESDIV(x) (((x)&0xff)<<24)
- +#define CANCTRL_RJW(x) (((x)&0x03)<<22)
- +#define CANCTRL_PSEG1(x) (((x)&0x07)<<19)
- +#define CANCTRL_PSEG2(x) (((x)&0x07)<<16)
- +#define CANCTRL_BOFFMSK 0x00008000
- +#define CANCTRL_ERRMSK 0x00004000
- +#define CANCTRL_LPB 0x00001000
- +#define CANCTRL_SAMP(x) (((x)&0x1)<<7)
- +#define CANCTRL_BOFFREC 0x00000040
- +#define CANCTRL_TSYNC 0x00000020
- +#define CANCTRL_LBUF 0x00000010
- +#define CANCTRL_LOM 0x00000008
- +#define CANCTRL_PROPSEG(x) ((x)&0x07)
- +#define CANCTRL_BITTIME 0x00c0d078
- +
- +/* FLEXCAN error counter register (ERRCNT) bits */
- +#define ERRCNT_REXECTR(x) (((x)&0xff)<<8)
- +#define ERRCNT_TXECTR(x) ((x)&0xff)
- +
- +/* FLEXCAN error and status register (ERRSTAT) bits */
- +#define ERRSTAT_BITERR(x) (((x)&0x03)<<14)
- +#define ERRSTAT_ACKERR 0x00002000
- +#define ERRSTAT_CRCERR 0x00001000
- +#define ERRSTAT_FRMERR 0x00000800
- +#define ERRSTAT_STFERR 0x00000400
- +#define ERRSTAT_TXWRN 0x00000200
- +#define ERRSTAT_RXWRN 0x00000100
- +#define ERRSTAT_IDLE 0x00000080
- +#define ERRSTAT_TXRX 0x00000040
- +#define ERRSTAT_FLTCONF(x) (((x)&0x03)<<4)
- +#define ERRSTAT_BOFFINT 0x00000004
- +#define ERRSTAT_ERRINT 0x00000002
- +
- +/* FLEXCAN interrupt mask register (IMASK) bits */
- +#define IMASK_BUF15M 0x8000
- +#define IMASK_BUF14M 0x4000
- +#define IMASK_BUF13M 0x2000
- +#define IMASK_BUF12M 0x1000
- +#define IMASK_BUF11M 0x0800
- +#define IMASK_BUF10M 0x0400
- +#define IMASK_BUF9M 0x0200
- +#define IMASK_BUF8M 0x0100
- +#define IMASK_BUF7M 0x0080
- +#define IMASK_BUF6M 0x0040
- +#define IMASK_BUF5M 0x0020
- +#define IMASK_BUF4M 0x0010
- +#define IMASK_BUF3M 0x0008
- +#define IMASK_BUF2M 0x0004
- +#define IMASK_BUF1M 0x0002
- +#define IMASK_BUF0M 0x0001
- +#define IMASK_BUFnM(x) (0x1<<(x))
- +#define IMASK_BUFF_ENABLE_ALL 0xffff
- +#define IMASK_BUFF_DISABLE_ALL 0x0000
- +
- +/* FLEXCAN interrupt flag register (IFLAG) bits */
- +#define IFLAG_BUF15M 0x8000
- +#define IFLAG_BUF14M 0x4000
- +#define IFLAG_BUF13M 0x2000
- +#define IFLAG_BUF12M 0x1000
- +#define IFLAG_BUF11M 0x0800
- +#define IFLAG_BUF10M 0x0400
- +#define IFLAG_BUF9M 0x0200
- +#define IFLAG_BUF8M 0x0100
- +#define IFLAG_BUF7M 0x0080
- +#define IFLAG_BUF6M 0x0040
- +#define IFLAG_BUF5M 0x0020
- +#define IFLAG_BUF4M 0x0010
- +#define IFLAG_BUF3M 0x0008
- +#define IFLAG_BUF2M 0x0004
- +#define IFLAG_BUF1M 0x0002
- +#define IFLAG_BUF0M 0x0001
- +#define IFLAG_BUFnM(x) (0x1<<(x))
- +#define IFLAG_BUFF_SET_ALL 0xffff
- +#define IFLAG_BUFF_DISABLE_ALL 0x0000
- +
- +/* FLEXCAN message buffers */
- +#define MB_CNT_CODE(x) (((x)&0x0f)<<24)
- +#define MB_CNT_SRR 0x00400000
- +#define MB_CNT_IDE 0x00200000
- +#define MB_CNT_RTR 0x00100000
- +#define MB_CNT_LENGTH(x) (((x)&0x0f)<<16)
- +#define MB_CNT_TIMESTAMP(x) ((x)&0xffff)
- +
- +#define MB_ID_STD ((0x7ff)<<18)
- +#define MB_ID_EXT 0x1fffffff
- +#define MB_CODE_MASK 0xf0ffffff
- +
- +/* Structure of the message buffer */
- +struct flexcan_mb {
- + u32 can_dlc;
- + u32 can_id;
- + u8 data[8];
- +};
- +
- +/* Structure of the hardware registers */
- +struct flexcan_regs {
- + u32 canmcr;
- + u32 canctrl;
- + u32 timer;
- + u32 reserved1;
- + u32 rxgmask;
- + u32 rx14mask;
- + u32 rx15mask;
- + u32 errcnt;
- + u32 errstat;
- + u32 reserved2;
- + u32 imask;
- + u32 reserved3;
- + u32 iflag;
- + u32 reserved4[19];
- + struct flexcan_mb cantxfg[16];
- +};
- +
- +struct flexcan_platform_data {
- + u8 clock_src; /* FLEXCAN clock source CRIN or SYSCLK */
- + u32 clock_frq; /* can ref. clock, in Hz */
- +};
- +
- +struct net_device *alloc_flexcandev(void);
- +
- +extern int register_flexcandev(struct net_device *dev, int clock_src);
- +extern void unregister_flexcandev(struct net_device *dev);
- +
- +#endif /* __FLEXCAN_H__ */
- --- /dev/null
- +++ b/drivers/net/can/flexcan/mcf548x_can.c
- @@ -0,0 +1,213 @@
- +/*
- + * DESCRIPTION:
- + * CAN bus driver for the Freescale MCF548x embedded CPU.
- + *
- + * AUTHOR:
- + * Andrey Volkov <[email protected]>
- + *
- + * COPYRIGHT:
- + * 2004-2005, Varma Electronics Oy
- + *
- + * LICENCE:
- + * This program 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.
- + *
- + * 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.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, write to the Free Software
- + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- + *
- + * HISTORY:
- + * 2008-06-23 support for MCF548x's FlexCAN
- + * Huan, Wang <[email protected]>
- + * 2005-02-03 created
- + *
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/interrupt.h>
- +#include <linux/platform_device.h>
- +#include <linux/netdevice.h>
- +#include <linux/can.h>
- +#include <linux/can/dev.h>
- +#include <linux/io.h>
- +
- +#include "flexcan.h"
- +#include <asm/coldfire.h>
- +#include <asm/m5485sim.h>
- +#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
- +
- +RCSID("$Id$");
- +
- +#define PDEV_MAX 2
- +
- +struct platform_device *pdev[PDEV_MAX];
- +
- +static int __devinit mcf548x_can_probe(struct platform_device *pdev)
- +{
- + struct resource *mem;
- + struct net_device *dev;
- + struct flexcan_platform_data *pdata = pdev->dev.platform_data;
- + struct can_priv *can;
- + u32 mem_size;
- + int ret = -ENODEV;
- +
- + if (!pdata)
- + return ret;
- +
- + dev = alloc_flexcandev();
- + if (!dev)
- + return -ENOMEM;
- + can = netdev_priv(dev);
- +
- + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- +
- + dev->irq = platform_get_irq(pdev, 0);
- + if (!mem || !dev->irq)
- + goto req_error;
- +
- + mem_size = mem->end - mem->start + 1;
- + if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
- + dev_err(&pdev->dev, "resource unavailable\n");
- + goto req_error;
- + }
- + SET_NETDEV_DEV(dev, &pdev->dev);
- +
- + dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
- + if (!dev->base_addr) {
- + dev_err(&pdev->dev, "failed to map can port\n");
- + ret = -ENOMEM;
- + goto fail_map;
- + }
- + can->can_sys_clock = pdata->clock_frq;
- + platform_set_drvdata(pdev, dev);
- + ret = register_flexcandev(dev, pdata->clock_src);
- + if (ret >= 0) {
- + dev_info(&pdev->dev, "probe for port 0x%lX done\n",
- + dev->base_addr);
- + return ret;
- + }
- +
- + iounmap((unsigned long *)dev->base_addr);
- +fail_map:
- + release_mem_region(mem->start, mem_size);
- +req_error:
- + free_candev(dev);
- + dev_err(&pdev->dev, "probe failed\n");
- + return ret;
- +}
- +
- +static int __devexit mcf548x_can_remove(struct platform_device *pdev)
- +{
- + struct net_device *dev = platform_get_drvdata(pdev);
- + struct resource *mem;
- +
- + platform_set_drvdata(pdev, NULL);
- + unregister_flexcandev(dev);
- + iounmap((unsigned long *)dev->base_addr);
- +
- + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- + release_mem_region(mem->start, mem->end - mem->start + 1);
- + free_candev(dev);
- + return 0;
- +}
- +
- +static struct platform_driver mcf548x_can_driver = {
- + .driver = {
- + .name = "mcf548x-flexcan",
- + },
- + .probe = mcf548x_can_probe,
- + .remove = __devexit_p(mcf548x_can_remove),
- +};
- +
- +static struct resource mcf548x_can0_resources[] = {
- + [0] = {
- + .start = MCF_MBAR + 0x0000A000,
- + .end = MCF_MBAR + 0x0000A7FF,
- + .flags = IORESOURCE_MEM,
- + },
- + [1] = {
- + .start = 49,
- + .end = 49,
- + .flags = IORESOURCE_IRQ,
- + },
- +};
- +
- +static struct resource mcf548x_can1_resources[] = {
- + [0] = {
- + .start = MCF_MBAR + 0x0000A800,
- + .end = MCF_MBAR + 0x0000AFFF,
- + .flags = IORESOURCE_MEM,
- + },
- + [1] = {
- + .start = 55,
- + .end = 55,
- + .flags = IORESOURCE_IRQ,
- + },
- +};
- +
- +
- +static int __init mcf548x_of_to_pdev(void)
- +{
- + unsigned int i;
- + int err = -ENODEV;
- + struct flexcan_platform_data pdata;
- +
- + pdev[0] = platform_device_register_simple("mcf548x-flexcan", 0,
- + mcf548x_can0_resources, 2);
- + if (IS_ERR(pdev[0])) {
- + err = PTR_ERR(pdev[0]);
- + return err;
- + }
- + pdev[1] = platform_device_register_simple("mcf548x-flexcan", 1,
- + mcf548x_can1_resources, 2);
- + if (IS_ERR(pdev[1])) {
- + err = PTR_ERR(pdev[1]);
- + return err;
- + }
- +
- + /* FlexCAN clock */
- + pdata.clock_frq = 100000000;
- +
- + for (i = 0; i < PDEV_MAX; i++) {
- + err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
- + if (err)
- + return err;
- + }
- + return err;
- +}
- +
- +int __init mcf548x_can_init(void)
- +{
- + int err = mcf548x_of_to_pdev();
- +
- + if (err) {
- + printk(KERN_ERR "%s init failed with err=%d\n",
- + mcf548x_can_driver.driver.name, err);
- + return err;
- + }
- +
- + return platform_driver_register(&mcf548x_can_driver);
- +}
- +
- +void __exit mcf548x_can_exit(void)
- +{
- + int i;
- + platform_driver_unregister(&mcf548x_can_driver);
- + for (i = 0; i < PDEV_MAX; i++)
- + platform_device_unregister(pdev[i]);
- +}
- +
- +module_init(mcf548x_can_init);
- +module_exit(mcf548x_can_exit);
- +
- +MODULE_AUTHOR("Andrey Volkov <[email protected]>");
- +MODULE_DESCRIPTION("Freescale MCF548x CAN driver");
- +MODULE_LICENSE("GPL v2");
- --- a/include/asm-m68k/m5485sim.h
- +++ b/include/asm-m68k/m5485sim.h
- @@ -186,6 +186,8 @@
- #define MCF_PAR_PCIBR MCF_REG16(0x000A4A)
- #define MCF_PAR_PSCn(x) MCF_REG08(0x000A4F-((x)&0x3))
- #define MCF_PAR_FECI2CIRQ MCF_REG16(0x000A44)
- +#define MCF_PAR_DSPI MCF_REG16(0x000A50)
- +#define MCF_PAR_TIMER MCF_REG08(0X000A52)
- #define MCF_EPPAR MCF_REG16(0x000F00)
- #define MCF_EPIER MCF_REG08(0x000F05)
- #define MCF_EPFR MCF_REG08(0x000F0C)
- --- /dev/null
- +++ b/include/linux/can/dev.h
- @@ -0,0 +1,62 @@
- +/*
- + * linux/can/dev.h
- + *
- + * Definitions for CAN controller network devices lib (work in progress)
- + *
- + * * * $Id$
- + *
- + * Author: Andrey Volkov <[email protected]>
- + * Copyright (c) 2006 Varma Electronics Oy
- + *
- + */
- +
- +#ifndef CAN_DEVICE_H
- +#define CAN_DEVICE_H
- +
- +#include <linux/version.h>
- +#include <linux/can/error.h>
- +#include <linux/can/ioctl.h>
- +
- +struct can_priv {
- + struct can_device_stats can_stats;
- +
- + /* can-bus oscillator frequency, in Hz,
- + BE CAREFUL! SOME CONTROLLERS (LIKE SJA1000)
- + FOOLISH ABOUT THIS FRQ (for sja1000 as ex. this
- + clock must be xtal clock divided by 2). */
- + u32 can_sys_clock;
- +
- + /* by default max_brp is equal 64,
- + but for a Freescale TouCAN, as ex., it can be 255*/
- + u32 max_brp;
- + /* For the mostly all controllers, max_sjw is equal 4, but
- + some, hmm, CAN implementations hardwared it to 1 */
- + u8 max_sjw;
- +
- + u32 baudrate; /* in bauds */
- + struct can_bittime bit_time;
- +
- + spinlock_t irq_lock;
- + /* Please hold this lock when touching net_stats/can_stats*/
- + spinlock_t stats_lock;
- +
- + can_state_t state;
- + can_mode_t mode;
- + can_ctrlmode_t ctrlmode;
- +
- + int (*do_set_bit_time)(struct net_device *dev, struct can_bittime *br);
- + int (*do_get_state) (struct net_device *dev, can_state_t *state);
- + int (*do_set_mode) (struct net_device *dev, can_mode_t mode);
- + int (*do_set_ctrlmode)(struct net_device *dev, can_ctrlmode_t ctrlmode);
- + int (*do_get_ctrlmode)(struct net_device *dev, can_ctrlmode_t *ctrlmode);
- +};
- +
- +#define ND2D(_ndev) (_ndev->dev.parent)
- +
- +struct net_device *alloc_candev(int sizeof_priv);
- +void free_candev(struct net_device *dev);
- +
- +int can_calc_bit_time(struct can_priv *can, u32 baudrate,
- + struct can_bittime_std *bit_time);
- +
- +#endif /* CAN_DEVICE_H */
- --- /dev/null
- +++ b/include/linux/can/ioctl.h
- @@ -0,0 +1,152 @@
- +/*
- + * linux/can/ioctl.h
- + *
- + * Definitions for CAN controller setup (work in progress)
- + *
- + * $Id$
- + *
- + * Send feedback to <[email protected]>
- + *
- + */
- +
- +#ifndef CAN_IOCTL_H
- +#define CAN_IOCTL_H
- +
- +#include <linux/sockios.h>
- +
- +
- +/* max. 16 private ioctls */
- +
- +#define SIOCSCANBAUDRATE (SIOCDEVPRIVATE+0)
- +#define SIOCGCANBAUDRATE (SIOCDEVPRIVATE+1)
- +
- +#define SIOCSCANCUSTOMBITTIME (SIOCDEVPRIVATE+2)
- +#define SIOCGCANCUSTOMBITTIME (SIOCDEVPRIVATE+3)
- +
- +#define SIOCSCANMODE (SIOCDEVPRIVATE+4)
- +#define SIOCGCANMODE (SIOCDEVPRIVATE+5)
- +
- +#define SIOCSCANCTRLMODE (SIOCDEVPRIVATE+6)
- +#define SIOCGCANCTRLMODE (SIOCDEVPRIVATE+7)
- +
- +#define SIOCSCANFILTER (SIOCDEVPRIVATE+8)
- +#define SIOCGCANFILTER (SIOCDEVPRIVATE+9)
- +
- +#define SIOCGCANSTATE (SIOCDEVPRIVATE+10)
- +#define SIOCGCANSTATS (SIOCDEVPRIVATE+11)
- +
- +#define SIOCSCANERRORCONFIG (SIOCDEVPRIVATE+12)
- +#define SIOCGCANERRORCONFIG (SIOCDEVPRIVATE+13)
- +
- +/* parameters for ioctls */
- +
- +/* SIOC[SG]CANBAUDRATE */
- +/* baudrate for CAN-controller in bits per second. */
- +/* 0 = Scan for baudrate (Autobaud) */
- +
- +typedef __u32 can_baudrate_t;
- +
- +
- +/* SIOC[SG]CANCUSTOMBITTIME */
- +
- +typedef enum CAN_BITTIME_TYPE {
- + CAN_BITTIME_STD,
- + CAN_BITTIME_BTR
- +} can_bittime_type_t;
- +
- +/* TSEG1 of controllers usually is a sum of synch_seg (always 1),
- + * prop_seg and phase_seg1, TSEG2 = phase_seg2 */
- +
- +struct can_bittime_std {
- + __u32 brp; /* baud rate prescaler */
- + __u8 prop_seg; /* from 1 to 8 */
- + __u8 phase_seg1; /* from 1 to 8 */
- + __u8 phase_seg2; /* from 1 to 8 */
- + __u8 sjw:7; /* from 1 to 4 */
- + __u8 sam:1; /* 1 - enable triple sampling */
- +};
- +
- +struct can_bittime_btr {
- + __u8 btr0;
- + __u8 btr1;
- +};
- +
- +struct can_bittime {
- + can_bittime_type_t type;
- + union {
- + struct can_bittime_std std;
- + struct can_bittime_btr btr;
- + };
- +};
- +
- +#define CAN_BAUDRATE_UNCONFIGURED ((__u32) 0xFFFFFFFFU)
- +#define CAN_BAUDRATE_UNKNOWN 0
- +
- +/* SIOC[SG]CANMODE */
- +
- +typedef __u32 can_mode_t;
- +
- +#define CAN_MODE_STOP 0
- +#define CAN_MODE_START 1
- +#define CAN_MODE_SLEEP 2
- +
- +
- +/* SIOC[SG]CANCTRLMODE */
- +
- +typedef __u32 can_ctrlmode_t;
- +
- +#define CAN_CTRLMODE_LOOPBACK 0x1
- +#define CAN_CTRLMODE_LISTENONLY 0x2
- +
- +
- +/* SIOCGCANFILTER */
- +
- +typedef __u32 can_filter_t;
- +
- +/* filter modes (may vary due to controller specific capabilities) */
- +#define CAN_FILTER_CAPAB 0 /* get filter type capabilities
- + (32 Bit value) */
- +#define CAN_FILTER_MASK_VALUE 1 /* easy bit filter (see struct can_filter) */
- +#define CAN_FILTER_SFF_BITMASK 2 /* bitfield with 2048 bit SFF filter */
- + /* filters 3 - 31 currently undefined */
- +
- +#define CAN_FILTER_MAX 31 /* max. filter type value */
- +
- +
- +/* SIOCGCANSTATE */
- +
- +typedef __u32 can_state_t;
- +
- +#define CAN_STATE_ACTIVE 0
- +#define CAN_STATE_BUS_WARNING 1
- +#define CAN_STATE_BUS_PASSIVE 2
- +#define CAN_STATE_BUS_OFF 3
- +#define CAN_STATE_SCANNING_BAUDRATE 4
- +#define CAN_STATE_STOPPED 5
- +#define CAN_STATE_SLEEPING 6
- +
- +
- +/* SIOCGCANSTATS */
- +
- +struct can_device_stats {
- + int error_warning;
- + int data_overrun;
- + int wakeup;
- + int bus_error;
- + int error_passive;
- + int arbitration_lost;
- + int restarts;
- + int bus_error_at_init;
- +};
- +
- +/* SIOC[SG]CANERRORCONFIG */
- +
- +typedef enum CAN_ERRCFG_TYPE {
- + CAN_ERRCFG_MASK,
- + CAN_ERRCFG_BUSERR,
- + CAN_ERRCFG_BUSOFF
- +} can_errcfg_type_t;
- +
- +/* tbd */
- +
- +#endif /* CAN_IOCTL_H */
- --- /dev/null
- +++ b/include/linux/can/version.h
- @@ -0,0 +1,22 @@
- +/*
- + * linux/can/version.h
- + *
- + * Version information for the CAN network layer implementation
- +
- + * Author: Urs Thuermann <[email protected]>
- + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
- + * All rights reserved.
- + *
- + * Send feedback to <[email protected]>
- + *
- + */
- +
- +#ifndef CAN_VERSION_H
- +#define CAN_VERSION_H
- +
- +#define RCSID(s) asm(".section .rodata.str1.1,\"aMS\",@progbits,1\n\t" \
- + ".string \"" s "\"\n\t.previous\n")
- +
- +RCSID("$Id$");
- +
- +#endif /* CAN_VERSION_H */
- --- a/net/can/Makefile
- +++ b/net/can/Makefile
- @@ -10,3 +10,6 @@ can-raw-objs := raw.o
-
- obj-$(CONFIG_CAN_BCM) += can-bcm.o
- can-bcm-objs := bcm.o
- +
- +obj-$(CONFIG_CAN) += candev.o
- +candev-objs := dev.o
- --- /dev/null
- +++ b/net/can/dev.c
- @@ -0,0 +1,292 @@
- +/*
- + * $Id$
- + *
- + * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
- + * Copyright (C) 2006 Andrey Volkov, Varma Electronics
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the version 2 of the GNU General Public License
- + * 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.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, write to the Free Software
- + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- + */
- +
- +#include <linux/module.h>
- +#include <linux/netdevice.h>
- +#include <linux/if_arp.h>
- +#include <linux/can.h>
- +#include <linux/can/dev.h>
- +
- +MODULE_DESCRIPTION("CAN netdevice library");
- +MODULE_LICENSE("GPL v2");
- +MODULE_AUTHOR("Marc Kleine-Budde <[email protected]>, "
- + "Andrey Volkov <[email protected]>");
- +
- +/*
- + Abstract:
- + Baud rate calculated with next formula:
- + baud = frq/(brp*(1 + prop_seg+ phase_seg1 + phase_seg2))
- +
- + This calc function based on work of Florian Hartwich and Armin Bassemi
- + "The Configuration of the CAN Bit Timing"
- + (http://www.semiconductors.bosch.de/pdf/CiA99Paper.pdf)
- +
- + Parameters:
- + [in]
- + bit_time_nsec - expected bit time in nanosecs
- +
- + [out]
- + bit_time - calculated time segments, for meaning of
- + each field read CAN standard.
- +*/
- +
- +#define DEFAULT_MAX_BRP 64U
- +#define DEFAULT_MAX_SJW 4U
- +
- +/* All below values in tq units */
- +#define MAX_BIT_TIME 25U
- +#define MIN_BIT_TIME 8U
- +#define MAX_PROP_SEG 8U
- +#define MAX_PHASE_SEG1 8U
- +#define MAX_PHASE_SEG2 8U
- +
- +int can_calc_bit_time(struct can_priv *can, u32 baudrate,
- + struct can_bittime_std *bit_time)
- +{
- + int best_error = -1; /* Ariphmetic error */
- + int df, best_df = -1; /* oscillator's tolerance range */
- + u32 quanta; /*in tq units*/
- + u32 brp, phase_seg1, phase_seg2, sjw, prop_seg;
- + u32 brp_min, brp_max, brp_expected;
- + u64 tmp;
- +
- + /* baudrate range [1baud,1Mbaud] */
- + if (baudrate == 0 || baudrate > 1000000UL)
- + return -EINVAL;
- +
- + tmp = (u64)can->can_sys_clock*1000;
- + do_div(tmp, baudrate);
- + brp_expected = (u32)tmp;
- +
- + brp_min = brp_expected / (1000 * MAX_BIT_TIME);
- + if (brp_min == 0)
- + brp_min = 1;
- + if (brp_min > can->max_brp)
- + return -ERANGE;
- +
- + brp_max = (brp_expected + 500 * MIN_BIT_TIME) / (1000 * MIN_BIT_TIME);
- + if (brp_max == 0)
- + brp_max = 1;
- + if (brp_max > can->max_brp)
- + brp_max = can->max_brp;
- +
- + for (brp = brp_min; brp <= brp_max; brp++) {
- + quanta = brp_expected / (brp * 1000);
- + if (quanta < MAX_BIT_TIME && quanta * brp * 1000 !=
- + brp_expected)
- + quanta++;
- + if (quanta < MIN_BIT_TIME || quanta > MAX_BIT_TIME)
- + continue;
- +
- + phase_seg2 = min((quanta - 3) / 2, MAX_PHASE_SEG2);
- + for (sjw = can->max_sjw; sjw > 0; sjw--) {
- + for (; phase_seg2 > sjw; phase_seg2--) {
- + u32 err1, err2;
- + phase_seg1 = phase_seg2 % 2 ?
- + phase_seg2-1 : phase_seg2;
- + prop_seg = quanta-1 - phase_seg2 - phase_seg1;
- + /*
- + * FIXME: support of longer lines
- + * (i.e. bigger prop_seg) is more prefered
- + * than support of cheap oscillators
- + * (i.e. bigger df/phase_seg1/phase_seg2)
- + * */
- +
- + if (prop_seg < phase_seg1)
- + continue;
- + if (prop_seg > MAX_PROP_SEG)
- + goto next_brp;
- +
- + err1 = phase_seg1 * brp * 500 * 1000 /
- + (13 * brp_expected - phase_seg2 *
- + brp * 1000);
- + err2 = sjw * brp * 50 * 1000 / brp_expected;
- +
- + df = min(err1, err2);
- + if (df >= best_df) {
- + unsigned error = abs(brp_expected * 10 /
- + (brp * (1 + prop_seg +
- + phase_seg1 +
- + phase_seg2)) - 10000);
- +
- + if (error > 10 || error > best_error)
- + continue;
- +
- + if (error == best_error && prop_seg <
- + bit_time->prop_seg)
- + continue;
- +
- + best_error = error;
- + best_df = df;
- + bit_time->brp = brp;
- + bit_time->prop_seg = prop_seg;
- + bit_time->phase_seg1 = phase_seg1;
- + bit_time->phase_seg2 = phase_seg2;
- + bit_time->sjw = sjw;
- + bit_time->sam =
- + (bit_time->phase_seg1 > 3);
- + }
- + }
- + }
- +next_brp: ;
- + }
- +
- + if (best_error < 0)
- + return -EDOM;
- + return 0;
- +}
- +EXPORT_SYMBOL(can_calc_bit_time);
- +
- +static int can_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
- +{
- + struct can_priv *can = netdev_priv(dev);
- + struct can_bittime *bt = (struct can_bittime *)&ifr->ifr_ifru;
- + ulong *baudrate = (ulong *)&ifr->ifr_ifru;
- + int ret = -EOPNOTSUPP;
- +
- + dev_dbg(ND2D(dev), "(%s) 0x%08x %p\n", __func__, cmd, &ifr->ifr_ifru);
- +
- + switch (cmd) {
- + case SIOCSCANBAUDRATE:
- + if (can->do_set_bit_time) {
- + struct can_bittime bit_time;
- + ret = can_calc_bit_time(can, *baudrate, &bit_time.std);
- + if (ret != 0)
- + break;
- + bit_time.type = CAN_BITTIME_STD;
- + ret = can->do_set_bit_time(dev, &bit_time);
- + if (!ret) {
- + can->baudrate = *baudrate;
- + can->bit_time = bit_time;
- + }
- + }
- + break;
- + case SIOCGCANBAUDRATE:
- + *baudrate = can->baudrate;
- + ret = 0;
- + break;
- + case SIOCSCANCUSTOMBITTIME:
- + if (can->do_set_bit_time) {
- + ret = can->do_set_bit_time(dev, bt);
- + if (!ret) {
- + can->bit_time = *bt;
- + if (bt->type == CAN_BITTIME_STD && bt->std.brp) {
- + can->baudrate = can->can_sys_clock /
- + (bt->std.brp * (1 + bt->std.prop_seg +
- + bt->std.phase_seg1 +
- + bt->std.phase_seg2));
- + } else
- + can->baudrate = CAN_BAUDRATE_UNKNOWN;
- + }
- + }
- + break;
- + case SIOCGCANCUSTOMBITTIME:
- + *bt = can->bit_time;
- + ret = 0;
- + break;
- + case SIOCSCANMODE:
- + if (can->do_set_mode) {
- + can_mode_t mode =
- + *((can_mode_t *)(&ifr->ifr_ifru));
- + if (mode == CAN_MODE_START &&
- + can->baudrate == CAN_BAUDRATE_UNCONFIGURED) {
- + dev_info(ND2D(dev), "Impossible to start \
- + on UNKNOWN speed\n");
- + ret = EINVAL;
- + } else
- + return can->do_set_mode(dev, mode);
- + }
- + break;
- + case SIOCGCANMODE:
- + *((can_mode_t *)(&ifr->ifr_ifru)) = can->mode;
- + ret = 0;
- + break;
- + case SIOCSCANCTRLMODE:
- + if (can->do_set_ctrlmode) {
- + can_ctrlmode_t ctrlmode =
- + *((can_ctrlmode_t *)(&ifr->ifr_ifru));
- + return can->do_set_ctrlmode(dev, ctrlmode);
- + }
- + break;
- + case SIOCGCANCTRLMODE:
- + *((can_ctrlmode_t *)(&ifr->ifr_ifru)) = can->ctrlmode;
- + ret = 0;
- + break;
- + case SIOCSCANFILTER:
- + break;
- + case SIOCGCANFILTER:
- + break;
- + case SIOCGCANSTATE:
- + if (can->do_get_state)
- + return can->do_get_state(dev,
- + (can_state_t *)(&ifr->ifr_ifru));
- + break;
- + case SIOCGCANSTATS:
- + *((struct can_device_stats *)(&ifr->ifr_ifru)) = can->can_stats;
- + ret = 0;
- + break;
- + }
- +
- + return ret;
- +}
- +
- +static void can_setup(struct net_device *dev)
- +{
- + dev->type = ARPHRD_CAN;
- + dev->mtu = sizeof(struct can_frame);
- + dev->do_ioctl = can_ioctl;
- + dev->hard_header_len = 0;
- + dev->addr_len = 0;
- + dev->tx_queue_len = 10;
- +
- + /* New-style flags. */
- + dev->flags = IFF_NOARP;
- + dev->features = NETIF_F_NO_CSUM;
- +}
- +
- +/*
- + * Function alloc_candev
- + * Allocates and sets up an CAN device
- + */
- +struct net_device *alloc_candev(int sizeof_priv)
- +{
- + struct net_device *dev;
- + struct can_priv *priv;
- +
- + dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
- + if (!dev)
- + return NULL;
- +
- + priv = netdev_priv(dev);
- +
- + priv->baudrate = CAN_BAUDRATE_UNCONFIGURED;
- + priv->max_brp = DEFAULT_MAX_BRP;
- + priv->max_sjw = DEFAULT_MAX_SJW;
- + spin_lock_init(&priv->irq_lock);
- +
- + return dev;
- +}
- +EXPORT_SYMBOL(alloc_candev);
- +
- +void free_candev(struct net_device *dev)
- +{
- + free_netdev(dev);
- +}
- +EXPORT_SYMBOL(free_candev);
|