Skip to content

Commit

Permalink
can: SJA1000 generic platform bus driver
Browse files Browse the repository at this point in the history
This driver adds support for the SJA1000 chips connected to the
"platform bus", which can be found on various embedded systems.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Wolfgang Grandegger authored and David S. Miller committed May 18, 2009
1 parent 429da1c commit f534e52
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 0 deletions.
10 changes: 10 additions & 0 deletions drivers/net/can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ config CAN_SJA1000
---help---
Driver for the SJA1000 CAN controllers from Philips or NXP

config CAN_SJA1000_PLATFORM
depends on CAN_SJA1000
tristate "Generic Platform Bus based SJA1000 driver"
---help---
This driver adds support for the SJA1000 chips connected to
the "platform bus" (Linux abstraction for directly to the
processor attached devices). Which can be found on various
boards from Phytec (http://www.phytec.de) like the PCM027,
PCM038.

config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
Expand Down
1 change: 1 addition & 0 deletions drivers/net/can/sja1000/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
#

obj-$(CONFIG_CAN_SJA1000) += sja1000.o
obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o

ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
164 changes: 164 additions & 0 deletions drivers/net/can/sja1000/sja1000_platform.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (C) 2005 Sascha Hauer, Pengutronix
* Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
#include <linux/io.h>

#include "sja1000.h"

#define DRV_NAME "sja1000_platform"

MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
MODULE_LICENSE("GPL v2");

static u8 sp_read_reg(const struct net_device *dev, int reg)
{
return ioread8((void __iomem *)(dev->base_addr + reg));
}

static void sp_write_reg(const struct net_device *dev, int reg, u8 val)
{
iowrite8(val, (void __iomem *)(dev->base_addr + reg));
}

static int sp_probe(struct platform_device *pdev)
{
int err;
void __iomem *addr;
struct net_device *dev;
struct sja1000_priv *priv;
struct resource *res_mem, *res_irq;
struct sja1000_platform_data *pdata;

pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data provided!\n");
err = -ENODEV;
goto exit;
}

res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res_mem || !res_irq) {
err = -ENODEV;
goto exit;
}

if (!request_mem_region(res_mem->start, resource_size(res_mem),
DRV_NAME)) {
err = -EBUSY;
goto exit;
}

addr = ioremap_nocache(res_mem->start, resource_size(res_mem));
if (!addr) {
err = -ENOMEM;
goto exit_release;
}

dev = alloc_sja1000dev(0);
if (!dev) {
err = -ENOMEM;
goto exit_iounmap;
}
priv = netdev_priv(dev);

dev->base_addr = (unsigned long)addr;
dev->irq = res_irq->start;
priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
priv->read_reg = sp_read_reg;
priv->write_reg = sp_write_reg;
priv->can.clock.freq = pdata->clock;
priv->ocr = pdata->ocr;
priv->cdr = pdata->cdr;

dev_set_drvdata(&pdev->dev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);

err = register_sja1000dev(dev);
if (err) {
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
DRV_NAME, err);
goto exit_free;
}

dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n",
DRV_NAME, dev->base_addr, dev->irq);
return 0;

exit_free:
free_sja1000dev(dev);
exit_iounmap:
iounmap(addr);
exit_release:
release_mem_region(res_mem->start, resource_size(res_mem));
exit:
return err;
}

static int sp_remove(struct platform_device *pdev)
{
struct net_device *dev = dev_get_drvdata(&pdev->dev);
struct resource *res;

unregister_sja1000dev(dev);
dev_set_drvdata(&pdev->dev, NULL);

if (dev->base_addr)
iounmap((void __iomem *)dev->base_addr);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));

free_sja1000dev(dev);

return 0;
}

static struct platform_driver sp_driver = {
.probe = sp_probe,
.remove = sp_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};

static int __init sp_init(void)
{
return platform_driver_register(&sp_driver);
}

static void __exit sp_exit(void)
{
platform_driver_unregister(&sp_driver);
}

module_init(sp_init);
module_exit(sp_exit);
32 changes: 32 additions & 0 deletions include/linux/can/platform/sja1000.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef _CAN_PLATFORM_SJA1000_H_
#define _CAN_PLATFORM_SJA1000_H_

/* clock divider register */
#define CDR_CLKOUT_MASK 0x07
#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */
#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */
#define CDR_CBP 0x40 /* CAN input comparator bypass */
#define CDR_PELICAN 0x80 /* PeliCAN mode */

/* output control register */
#define OCR_MODE_BIPHASE 0x00
#define OCR_MODE_TEST 0x01
#define OCR_MODE_NORMAL 0x02
#define OCR_MODE_CLOCK 0x03
#define OCR_TX0_INVERT 0x04
#define OCR_TX0_PULLDOWN 0x08
#define OCR_TX0_PULLUP 0x10
#define OCR_TX0_PUSHPULL 0x18
#define OCR_TX1_INVERT 0x20
#define OCR_TX1_PULLDOWN 0x40
#define OCR_TX1_PULLUP 0x80
#define OCR_TX1_PUSHPULL 0xc0

struct sja1000_platform_data {
u32 clock; /* CAN bus oscillator frequency in Hz */

u8 ocr; /* output control register */
u8 cdr; /* clock divider register */
};

#endif /* !_CAN_PLATFORM_SJA1000_H_ */

0 comments on commit f534e52

Please sign in to comment.