Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f534e52f authored by Wolfgang Grandegger's avatar Wolfgang Grandegger Committed by David S. Miller
Browse files

can: SJA1000 generic platform bus driver



This driver adds support for the SJA1000 chips connected to the
"platform bus", which can be found on various embedded systems.

Signed-off-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: default avatarWolfgang Grandegger <wg@grandegger.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: default avatarOliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 429da1cc
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
#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_ */