From patchwork Wed May 20 17:06:30 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wolfgang Grandegger X-Patchwork-Id: 27463 Return-Path: X-Original-To: patchwork-incoming@bilbo.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ozlabs.org (ozlabs.org [203.10.76.45]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "mx.ozlabs.org", Issuer "CA Cert Signing Authority" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id 455B2B707C for ; Thu, 21 May 2009 03:07:16 +1000 (EST) Received: by ozlabs.org (Postfix) id 5BBA0DE432; Thu, 21 May 2009 03:07:00 +1000 (EST) Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [127.0.0.1]) by ozlabs.org (Postfix) with ESMTP id 59D1CDE431 for ; Thu, 21 May 2009 03:07:00 +1000 (EST) X-Original-To: Linuxppc-dev@ozlabs.org Delivered-To: Linuxppc-dev@ozlabs.org Received: from mail-out.m-online.net (mail-out.m-online.net [212.18.0.9]) by ozlabs.org (Postfix) with ESMTP id 89153DE0CE; Thu, 21 May 2009 03:06:39 +1000 (EST) Received: from mail01.m-online.net (mail.m-online.net [192.168.3.149]) by mail-out.m-online.net (Postfix) with ESMTP id 509971C153EA; Wed, 20 May 2009 19:06:36 +0200 (CEST) X-Auth-Info: EsttHXs7bKZeNUTQOI0TgmVLHE1DKA4pecUGCP6x72s= Received: from lancy.denx.de (dslb-088-064-009-113.pools.arcor-ip.net [88.64.9.113]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp-auth.mnet-online.de (Postfix) with ESMTP id 4729E90262; Wed, 20 May 2009 19:06:36 +0200 (CEST) Message-ID: <4A143896.7050200@grandegger.com> Date: Wed, 20 May 2009 19:06:30 +0200 From: Wolfgang Grandegger User-Agent: Thunderbird 2.0.0.21 (X11/20090320) MIME-Version: 1.0 To: Linux Netdev List Subject: [net-next-2.6 PATCH] can: SJA1000: generic OF platform bus driver X-Enigmail-Version: 0.95.7 Cc: linuxppc-dev , devicetree-discuss X-BeenThere: linuxppc-dev@ozlabs.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org This patch adds a generic driver for SJA1000 chips on the OpenFirmware platform bus found on embedded PowerPC systems. You need a SJA1000 node definition in your flattened device tree source (DTS) file similar to: can@3,100 { compatible = "philips,sja1000"; reg = <3 0x100 0x80>; clock-frequency = <8000000>; cdr-reg = <0x48>; ocr-reg = <0x0a>; interrupts = <2 0>; interrupt-parent = <&mpic>; }; Signed-off-by: Wolfgang Grandegger --- drivers/net/can/Kconfig | 9 + drivers/net/can/sja1000/Makefile | 1 drivers/net/can/sja1000/sja1000_of_platform.c | 215 ++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) Index: net-next-2.6/drivers/net/can/Kconfig =================================================================== --- net-next-2.6.orig/drivers/net/can/Kconfig +++ net-next-2.6/drivers/net/can/Kconfig @@ -51,6 +51,15 @@ config CAN_SJA1000_PLATFORM boards from Phytec (http://www.phytec.de) like the PCM027, PCM038. +config CAN_SJA1000_OF_PLATFORM + depends on CAN_SJA1000 && PPC_OF + tristate "Generic OF Platform Bus based SJA1000 driver" + ---help--- + This driver adds support for the SJA1000 chips connected to + the OpenFirmware "platform bus" found on embedded systems with + OpenFirmware bindings, e.g. if you have a PowerPC based system + you may want to enable this option. + config CAN_EMS_PCI tristate "EMS CPC-PCI and CPC-PCIe Card" depends on PCI && CAN_SJA1000 Index: net-next-2.6/drivers/net/can/sja1000/Makefile =================================================================== --- net-next-2.6.orig/drivers/net/can/sja1000/Makefile +++ net-next-2.6/drivers/net/can/sja1000/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000.o obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o +obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o Index: net-next-2.6/drivers/net/can/sja1000/sja1000_of_platform.c =================================================================== --- /dev/null +++ net-next-2.6/drivers/net/can/sja1000/sja1000_of_platform.c @@ -0,0 +1,215 @@ +/* + * Driver for SJA1000 CAN controllers on the OpenFirmware platform bus + * + * Copyright (C) 2008-2009 Wolfgang Grandegger + * + * 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. + */ + +/* This is a generic driver for SJA1000 chips on the OpenFirmware platform + * bus found on embedded PowerPC systems. You need a SJA1000 CAN node + * definition in your flattened device tree source (DTS) file similar to: + * + * can@3,100 { + * compatible = "philips,sja1000"; + * reg = <3 0x100 0x80>; + * clock-frequency = <8000000>; + * cdr-reg = <0x48>; + * ocr-reg = <0x0a>; + * interrupts = <2 0>; + * interrupt-parent = <&mpic>; + * }; + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sja1000.h" + +#define DRV_NAME "sja1000_of_platform" + +MODULE_AUTHOR("Wolfgang Grandegger "); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the OF platform bus"); +MODULE_LICENSE("GPL v2"); + +#define SJA1000_OFP_CAN_CLOCK (16000000 / 2) + +#define SJA1000_OFP_OCR OCR_TX0_PULLDOWN +#define SJA1000_OFP_CDR (CDR_CBP | CDR_CLK_OFF) + +static u8 sja1000_ofp_read_reg(const struct net_device *dev, int reg) +{ + return in_8((void __iomem *)(dev->base_addr + reg)); +} + +static void sja1000_ofp_write_reg(const struct net_device *dev, int reg, u8 val) +{ + out_8((void __iomem *)(dev->base_addr + reg), val); +} + +static int __devexit sja1000_ofp_remove(struct of_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct device_node *np = ofdev->node; + struct resource res; + + dev_set_drvdata(&ofdev->dev, NULL); + + unregister_sja1000dev(dev); + free_sja1000dev(dev); + iounmap((void __iomem *)dev->base_addr); + irq_dispose_mapping(dev->irq); + + of_address_to_resource(np, 0, &res); + release_mem_region(res.start, resource_size(res)); + + return 0; +} + +static int __devinit sja1000_ofp_probe(struct of_device *ofdev, + const struct of_device_id *id) +{ + struct device_node *np = ofdev->node; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource res; + const u32 *prop; + int err, irq, res_size, prop_size; + void __iomem *base; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_err(&ofdev->dev, "invalid address\n"); + return err; + } + + res_size = resource_size(res); + + if (!request_mem_region(res.start, res_size, DRV_NAME)) { + dev_err(&ofdev->dev, "couldn't request %#x..%#x\n", + res.start, res.end); + return -EBUSY; + } + + base = ioremap_nocache(res.start, res_size); + if (!base) { + dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n", + res.start, res.end); + err = -ENOMEM; + goto exit_release_mem; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_unmap_mem; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_dispose_irq; + } + + priv = netdev_priv(dev); + + priv->read_reg = sja1000_ofp_read_reg; + priv->write_reg = sja1000_ofp_write_reg; + + prop = of_get_property(np, "clock-frequency", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->can.clock.freq = *prop; + else + priv->can.clock.freq = SJA1000_OFP_CAN_CLOCK; + + prop = of_get_property(np, "ocr-reg", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->ocr = (u8)*prop; + else + priv->ocr = SJA1000_OFP_OCR; + + prop = of_get_property(np, "cdr-reg", &prop_size); + if (prop && (prop_size == sizeof(u32))) + priv->cdr = (u8)*prop; + else + priv->cdr = SJA1000_OFP_CDR; + + priv->irq_flags = IRQF_SHARED; + + dev->irq = irq; + dev->base_addr = (unsigned long)base; + + dev_info(&ofdev->dev, + "base=0x%lx irq=%d clock=%d ocr=0x%02x cdr=0x%02x\n", + dev->base_addr, dev->irq, priv->can.clock.freq, + priv->ocr, priv->cdr); + + dev_set_drvdata(&ofdev->dev, dev); + SET_NETDEV_DEV(dev, &ofdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free_sja1000; + } + + return 0; + +exit_free_sja1000: + free_sja1000dev(dev); +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_unmap_mem: + iounmap(base); +exit_release_mem: + release_mem_region(res.start, res_size); + + return err; +} + +static struct of_device_id __devinitdata sja1000_ofp_table[] = { + {.compatible = "philips,sja1000"}, + {.compatible = "nxp,sja1000"}, + {}, +}; + +static struct of_platform_driver sja1000_ofp_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .probe = sja1000_ofp_probe, + .remove = __devexit_p(sja1000_ofp_remove), + .match_table = sja1000_ofp_table, +}; + +static int __init sja1000_ofp_init(void) +{ + return of_register_platform_driver(&sja1000_ofp_driver); +} +module_init(sja1000_ofp_init); + +static void __exit sja1000_ofp_exit(void) +{ + return of_unregister_platform_driver(&sja1000_ofp_driver); +}; +module_exit(sja1000_ofp_exit);