can: c_can: Added support for Bosch C_CAN controller
Bosch C_CAN controller is a full-CAN implementation which is compliant to CAN protocol version 2.0 part A and B. Bosch C_CAN user manual can be obtained from: http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/users_manual_c_can.pdf This patch adds the support for this controller. The following are the design choices made while writing the controller driver: 1. Interface Register set IF1 has be used only in the current design. 2. Out of the 32 Message objects available, 16 are kept aside for RX purposes and the rest for TX purposes. 3. NAPI implementation is such that both the TX and RX paths function in polling mode. Signed-off-by: Bhupesh Sharma <bhupesh.sharma@st.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c906041412
commit
881ff67ad4
|
@ -115,6 +115,8 @@ source "drivers/net/can/mscan/Kconfig"
|
||||||
|
|
||||||
source "drivers/net/can/sja1000/Kconfig"
|
source "drivers/net/can/sja1000/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/net/can/c_can/Kconfig"
|
||||||
|
|
||||||
source "drivers/net/can/usb/Kconfig"
|
source "drivers/net/can/usb/Kconfig"
|
||||||
|
|
||||||
source "drivers/net/can/softing/Kconfig"
|
source "drivers/net/can/softing/Kconfig"
|
||||||
|
|
|
@ -13,6 +13,7 @@ obj-y += softing/
|
||||||
|
|
||||||
obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
||||||
obj-$(CONFIG_CAN_MSCAN) += mscan/
|
obj-$(CONFIG_CAN_MSCAN) += mscan/
|
||||||
|
obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||||
|
|
15
drivers/net/can/c_can/Kconfig
Normal file
15
drivers/net/can/c_can/Kconfig
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
menuconfig CAN_C_CAN
|
||||||
|
tristate "Bosch C_CAN devices"
|
||||||
|
depends on CAN_DEV && HAS_IOMEM
|
||||||
|
|
||||||
|
if CAN_C_CAN
|
||||||
|
|
||||||
|
config CAN_C_CAN_PLATFORM
|
||||||
|
tristate "Generic Platform Bus based C_CAN driver"
|
||||||
|
---help---
|
||||||
|
This driver adds support for the C_CAN chips connected to
|
||||||
|
the "platform bus" (Linux abstraction for directly to the
|
||||||
|
processor attached devices) which can be found on various
|
||||||
|
boards from ST Microelectronics (http://www.st.com)
|
||||||
|
like the SPEAr1310 and SPEAr320 evaluation boards.
|
||||||
|
endif
|
8
drivers/net/can/c_can/Makefile
Normal file
8
drivers/net/can/c_can/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# Makefile for the Bosch C_CAN controller drivers.
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_CAN_C_CAN) += c_can.o
|
||||||
|
obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o
|
||||||
|
|
||||||
|
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
1158
drivers/net/can/c_can/c_can.c
Normal file
1158
drivers/net/can/c_can/c_can.c
Normal file
File diff suppressed because it is too large
Load diff
86
drivers/net/can/c_can/c_can.h
Normal file
86
drivers/net/can/c_can/c_can.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* CAN bus driver for Bosch C_CAN controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 ST Microelectronics
|
||||||
|
* Bhupesh Sharma <bhupesh.sharma@st.com>
|
||||||
|
*
|
||||||
|
* Borrowed heavily from the C_CAN driver originally written by:
|
||||||
|
* Copyright (C) 2007
|
||||||
|
* - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de>
|
||||||
|
* - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch>
|
||||||
|
*
|
||||||
|
* Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B.
|
||||||
|
* Bosch C_CAN user manual can be obtained from:
|
||||||
|
* http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
|
||||||
|
* users_manual_c_can.pdf
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2. This program is licensed "as is" without any
|
||||||
|
* warranty of any kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef C_CAN_H
|
||||||
|
#define C_CAN_H
|
||||||
|
|
||||||
|
/* c_can IF registers */
|
||||||
|
struct c_can_if_regs {
|
||||||
|
u16 com_req;
|
||||||
|
u16 com_mask;
|
||||||
|
u16 mask1;
|
||||||
|
u16 mask2;
|
||||||
|
u16 arb1;
|
||||||
|
u16 arb2;
|
||||||
|
u16 msg_cntrl;
|
||||||
|
u16 data[4];
|
||||||
|
u16 _reserved[13];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* c_can hardware registers */
|
||||||
|
struct c_can_regs {
|
||||||
|
u16 control;
|
||||||
|
u16 status;
|
||||||
|
u16 err_cnt;
|
||||||
|
u16 btr;
|
||||||
|
u16 interrupt;
|
||||||
|
u16 test;
|
||||||
|
u16 brp_ext;
|
||||||
|
u16 _reserved1;
|
||||||
|
struct c_can_if_regs ifregs[2]; /* [0] = IF1 and [1] = IF2 */
|
||||||
|
u16 _reserved2[8];
|
||||||
|
u16 txrqst1;
|
||||||
|
u16 txrqst2;
|
||||||
|
u16 _reserved3[6];
|
||||||
|
u16 newdat1;
|
||||||
|
u16 newdat2;
|
||||||
|
u16 _reserved4[6];
|
||||||
|
u16 intpnd1;
|
||||||
|
u16 intpnd2;
|
||||||
|
u16 _reserved5[6];
|
||||||
|
u16 msgval1;
|
||||||
|
u16 msgval2;
|
||||||
|
u16 _reserved6[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* c_can private data structure */
|
||||||
|
struct c_can_priv {
|
||||||
|
struct can_priv can; /* must be the first member */
|
||||||
|
struct napi_struct napi;
|
||||||
|
struct net_device *dev;
|
||||||
|
int tx_object;
|
||||||
|
int current_status;
|
||||||
|
int last_status;
|
||||||
|
u16 (*read_reg) (struct c_can_priv *priv, void *reg);
|
||||||
|
void (*write_reg) (struct c_can_priv *priv, void *reg, u16 val);
|
||||||
|
struct c_can_regs __iomem *regs;
|
||||||
|
unsigned long irq_flags; /* for request_irq() */
|
||||||
|
unsigned int tx_next;
|
||||||
|
unsigned int tx_echo;
|
||||||
|
void *priv; /* for board-specific data */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct net_device *alloc_c_can_dev(void);
|
||||||
|
void free_c_can_dev(struct net_device *dev);
|
||||||
|
int register_c_can_dev(struct net_device *dev);
|
||||||
|
void unregister_c_can_dev(struct net_device *dev);
|
||||||
|
|
||||||
|
#endif /* C_CAN_H */
|
215
drivers/net/can/c_can/c_can_platform.c
Normal file
215
drivers/net/can/c_can/c_can_platform.c
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* Platform CAN bus driver for Bosch C_CAN controller
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 ST Microelectronics
|
||||||
|
* Bhupesh Sharma <bhupesh.sharma@st.com>
|
||||||
|
*
|
||||||
|
* Borrowed heavily from the C_CAN driver originally written by:
|
||||||
|
* Copyright (C) 2007
|
||||||
|
* - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de>
|
||||||
|
* - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch>
|
||||||
|
*
|
||||||
|
* Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B.
|
||||||
|
* Bosch C_CAN user manual can be obtained from:
|
||||||
|
* http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
|
||||||
|
* users_manual_c_can.pdf
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2. This program is licensed "as is" without any
|
||||||
|
* warranty of any kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/version.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/list.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
|
#include <linux/can/dev.h>
|
||||||
|
|
||||||
|
#include "c_can.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 16-bit c_can registers can be arranged differently in the memory
|
||||||
|
* architecture of different implementations. For example: 16-bit
|
||||||
|
* registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
|
||||||
|
* Handle the same by providing a common read/write interface.
|
||||||
|
*/
|
||||||
|
static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||||
|
void *reg)
|
||||||
|
{
|
||||||
|
return readw(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||||
|
void *reg, u16 val)
|
||||||
|
{
|
||||||
|
writew(val, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||||
|
void *reg)
|
||||||
|
{
|
||||||
|
return readw(reg + (long)reg - (long)priv->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||||
|
void *reg, u16 val)
|
||||||
|
{
|
||||||
|
writew(val, reg + (long)reg - (long)priv->regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit c_can_plat_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
void __iomem *addr;
|
||||||
|
struct net_device *dev;
|
||||||
|
struct c_can_priv *priv;
|
||||||
|
struct resource *mem, *irq;
|
||||||
|
#ifdef CONFIG_HAVE_CLK
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
/* get the appropriate clk */
|
||||||
|
clk = clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
dev_err(&pdev->dev, "no clock defined\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* get the platform data */
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||||
|
if (!mem || (irq <= 0)) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto exit_free_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request_mem_region(mem->start, resource_size(mem),
|
||||||
|
KBUILD_MODNAME)) {
|
||||||
|
dev_err(&pdev->dev, "resource unavailable\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto exit_free_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = ioremap(mem->start, resource_size(mem));
|
||||||
|
if (!addr) {
|
||||||
|
dev_err(&pdev->dev, "failed to map can port\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto exit_release_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate the c_can device */
|
||||||
|
dev = alloc_c_can_dev();
|
||||||
|
if (!dev) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto exit_iounmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv = netdev_priv(dev);
|
||||||
|
|
||||||
|
dev->irq = irq->start;
|
||||||
|
priv->regs = addr;
|
||||||
|
#ifdef CONFIG_HAVE_CLK
|
||||||
|
priv->can.clock.freq = clk_get_rate(clk);
|
||||||
|
priv->priv = clk;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
|
||||||
|
case IORESOURCE_MEM_32BIT:
|
||||||
|
priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
|
||||||
|
priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
|
||||||
|
break;
|
||||||
|
case IORESOURCE_MEM_16BIT:
|
||||||
|
default:
|
||||||
|
priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
|
||||||
|
priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, dev);
|
||||||
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||||
|
|
||||||
|
ret = register_c_can_dev(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
||||||
|
KBUILD_MODNAME, ret);
|
||||||
|
goto exit_free_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
|
||||||
|
KBUILD_MODNAME, priv->regs, dev->irq);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_free_device:
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
free_c_can_dev(dev);
|
||||||
|
exit_iounmap:
|
||||||
|
iounmap(addr);
|
||||||
|
exit_release_mem:
|
||||||
|
release_mem_region(mem->start, resource_size(mem));
|
||||||
|
exit_free_clk:
|
||||||
|
#ifdef CONFIG_HAVE_CLK
|
||||||
|
clk_put(clk);
|
||||||
|
exit:
|
||||||
|
#endif
|
||||||
|
dev_err(&pdev->dev, "probe failed\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit c_can_plat_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
|
struct c_can_priv *priv = netdev_priv(dev);
|
||||||
|
struct resource *mem;
|
||||||
|
|
||||||
|
unregister_c_can_dev(dev);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
free_c_can_dev(dev);
|
||||||
|
iounmap(priv->regs);
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
release_mem_region(mem->start, resource_size(mem));
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_CLK
|
||||||
|
clk_put(priv->priv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver c_can_plat_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = KBUILD_MODNAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = c_can_plat_probe,
|
||||||
|
.remove = __devexit_p(c_can_plat_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init c_can_plat_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&c_can_plat_driver);
|
||||||
|
}
|
||||||
|
module_init(c_can_plat_init);
|
||||||
|
|
||||||
|
static void __exit c_can_plat_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&c_can_plat_driver);
|
||||||
|
}
|
||||||
|
module_exit(c_can_plat_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller");
|
Loading…
Reference in a new issue