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: 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>
This commit is contained in:
		
							parent
							
								
									429da1cc84
								
							
						
					
					
						commit
						f534e52f09
					
				| @ -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 | ||||
|  | ||||
| @ -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
									
								
								drivers/net/can/sja1000/sja1000_platform.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								drivers/net/can/sja1000/sja1000_platform.c
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								include/linux/can/platform/sja1000.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								include/linux/can/platform/sja1000.h
									
									
									
									
									
										Normal file
									
								
							| @ -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_ */ | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user