From patchwork Tue Dec 31 16:32:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Jacques Hiblot X-Patchwork-Id: 3422191 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id EE8CA9F295 for ; Tue, 31 Dec 2013 17:07:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EC51E200E6 for ; Tue, 31 Dec 2013 17:07:34 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B0EB3200E5 for ; Tue, 31 Dec 2013 17:07:33 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Vy2mw-0006OU-4Z; Tue, 31 Dec 2013 17:07:30 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Vy2mt-0003na-3K; Tue, 31 Dec 2013 17:07:27 +0000 Received: from mail-wi0-f175.google.com ([209.85.212.175]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Vy2mp-0003nE-8L for linux-arm-kernel@lists.infradead.org; Tue, 31 Dec 2013 17:07:24 +0000 Received: by mail-wi0-f175.google.com with SMTP id hi5so17400756wib.8 for ; Tue, 31 Dec 2013 09:07:01 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Z3a6wy8Oi4+Lum78d/TSncOE2e+BB4j1xkltjIkidys=; b=X/2CMYGLhvx3/eRLroZBciEbAMDt4fhBHfYZn9YJ7KXy/d9s+CkSyzm1BNXPKBs2BT ALcUV6k5Xh3YXKc4EU0hiu4xoydjzuUb0ac63WW3JhEbyNH5c1VPlz1N27wTYmi3ObKi J0e8+MpGGejf3waDO/7bfXYRwqj/5ZVvi0KskaAp6lvX/PGBth7dFFk4HwKtFhKvUQsg TwbHD5spGU0E7uhQgR/4wiQD3Kqd91QQsVL3IxhEJAr7uDLJ6qlglejnIKDQ8Prd6GUW MdjFO/QoB2YWFN55WHLmDVQ9mAwhFbQv3s4nCgeJ6IZHktBkw2D/uh8OLlbGn9DFifjF 729g== X-Received: by 10.194.175.202 with SMTP id cc10mr24915400wjc.48.1388507755480; Tue, 31 Dec 2013 08:35:55 -0800 (PST) Received: from stedf17-labo202.ds.jdsu.net. (4-161.80-90.static-ip.oleane.fr. [90.80.161.4]) by mx.google.com with ESMTPSA id fj8sm76137500wib.1.2013.12.31.08.35.53 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 31 Dec 2013 08:35:54 -0800 (PST) From: jjhiblot@traphandler.com To: nicolas.ferre@atmel.com Subject: [PATCH 3/6] At91: dt: Added smc bus driver Date: Tue, 31 Dec 2013 17:32:11 +0100 Message-Id: <1388507534-10570-4-git-send-email-jjhiblot@traphandler.com> X-Mailer: git-send-email 1.8.4.2 In-Reply-To: <1388507534-10570-1-git-send-email-jjhiblot@traphandler.com> References: <1388507534-10570-1-git-send-email-jjhiblot@traphandler.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131231_120723_523303_629154F1 X-CRM114-Status: GOOD ( 21.06 ) X-Spam-Score: -2.6 (--) Cc: jean-jacques hiblot , Jean-Jacques Hiblot , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.3 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: jean-jacques hiblot The EBI/SMC external interface is used to access external peripherals (NAND and Ethernet controller in the case of sam9261ek). Different configurations and timings are required for those peripherals. This bus driver can be used to setup the bus timings/configuration from the device tree. Signed-off-by: Jean-Jacques Hiblot --- drivers/bus/Kconfig | 9 +++ drivers/bus/Makefile | 1 + drivers/bus/atmel-smc.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/bus/atmel-smc.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 552373c..8c944db5 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -12,6 +12,15 @@ config IMX_WEIM The WEIM(Wireless External Interface Module) works like a bus. You can attach many different devices on it, such as NOR, onenand. +config ATMEL_SMC + bool "Atmel SMC/EBI driver" + depends on SOC_AT91SAM9 && OF + help + Driver for Atmel SMC/EBI controller. + Used to configure the EBI (external bus interface) when the device- + tree is used. This bus supports NANDs, external ethernet controller, + SRAMs, ATA devices. + config MVEBU_MBUS bool depends on PLAT_ORION diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8947bdd..4364003 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -2,6 +2,7 @@ # Makefile for the bus drivers. # +obj-$(CONFIG_ATMEL_SMC) += atmel-smc.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o diff --git a/drivers/bus/atmel-smc.c b/drivers/bus/atmel-smc.c new file mode 100644 index 0000000..06e530d --- /dev/null +++ b/drivers/bus/atmel-smc.c @@ -0,0 +1,182 @@ +/* + * EBI driver for Atmel SAM9 chips + * inspired by the fsl weim bus driver + * + * Copyright (C) 2013 JJ Hiblot. + * + * 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 +#include +#include +#include +#include + +struct at91_smc_devtype { + unsigned int cs_count; +}; + +static const struct at91_smc_devtype sam9261_smc_devtype = { + .cs_count = 6, +}; + +static const struct of_device_id smc_id_table[] = { + { .compatible = "atmel,at91sam9261-smc", .data = &sam9261_smc_devtype}, + { } +}; +MODULE_DEVICE_TABLE(of, smc_id_table); + +struct smc_parameters_type { + const char *name; + u16 reg; + u16 width; + u16 shift; +}; + +#define SETUP 0 +#define PULSE 1 +#define CYCLE 2 +#define MODE 3 +static const struct smc_parameters_type smc_parameters[] = { + {"smc,ncs_read_setup", SETUP, 6, 24}, + {"smc,nrd_setup", SETUP, 6, 16}, + {"smc,ncs_write_setup", SETUP, 6, 8}, + {"smc,nwe_setup", SETUP, 6, 0}, + {"smc,ncs_read_pulse", PULSE, 6, 24}, + {"smc,nrd_pulse", PULSE, 6, 16}, + {"smc,ncs_write_pulse", PULSE, 6, 8}, + {"smc,nwe_pulse", PULSE, 6, 0}, + {"smc,read_cycle", CYCLE, 9, 16}, + {"smc,write_cycle", CYCLE, 9, 0}, + {"smc,burst_size", MODE, 2, 28}, + {"smc,burst_enabled", MODE, 1, 24}, + {"smc,tdf_mode", MODE, 1, 20}, + {"smc,tdf_cycles", MODE, 4, 16}, + {"smc,bus_width", MODE, 2, 12}, + {"smc,byte_access_type", MODE, 1, 8}, + {"smc,nwait_mode", MODE, 2, 4}, + {"smc,write_mode", MODE, 1, 0}, + {"smc,read_mode", MODE, 1, 1}, + {NULL} +}; + +/* Parse and set the timing for this device. */ +static int __init smc_timing_setup(struct device *dev, struct device_node *np, + void __iomem *base, const struct at91_smc_devtype *devtype) +{ + u32 val; + int ret; + u32 cs; + const struct smc_parameters_type *p = smc_parameters; + u32 shadow_smc_regs[5]; + + ret = of_property_read_u32(np, "smc,cs" , &cs); + if (ret < 0) { + dev_err(dev, "missing mandatory property : smc,cs\n"); + return ret; + } + if (val >= devtype->cs_count) { + dev_err(dev, "invalid value for property smc,cs (=%d)." + "Must be in range 0 to %d\n", cs, devtype->cs_count-1); + return -EINVAL; + } + + /* set the timing for EBI */ + base += (0x10 * cs); + shadow_smc_regs[SETUP] = readl_relaxed(base + AT91_SMC_SETUP); + shadow_smc_regs[PULSE] = readl_relaxed(base + AT91_SMC_PULSE); + shadow_smc_regs[CYCLE] = readl_relaxed(base + AT91_SMC_CYCLE); + shadow_smc_regs[MODE] = readl_relaxed(base + AT91_SMC_MODE); + + while (p->name) { + ret = of_property_read_u32(np, p->name , &val); + if (ret == -EINVAL) { + dev_dbg(dev, "cs %d: property %s not set.\n", cs, + p->name); + p++; + continue; + } else if (ret) { + dev_err(dev, "cs %d: can't get property %s.\n", cs, + p->name); + return ret; + } + if (val >= (1<width)) { + dev_err(dev, "cs %d: property %s out of range.\n", cs, + p->name); + return -ERANGE; + } + shadow_smc_regs[p->reg] &= ~(((1<width)-1) << p->shift); + shadow_smc_regs[p->reg] |= (val << p->shift); + p++; + } + writel_relaxed(shadow_smc_regs[SETUP], base + AT91_SMC_SETUP); + writel_relaxed(shadow_smc_regs[PULSE], base + AT91_SMC_PULSE); + writel_relaxed(shadow_smc_regs[CYCLE], base + AT91_SMC_CYCLE); + writel_relaxed(shadow_smc_regs[MODE], base + AT91_SMC_MODE); + return 0; +} + +static int __init smc_parse_dt(struct platform_device *pdev, + void __iomem *base) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id = of_match_device(smc_id_table, dev); + const struct at91_smc_devtype *devtype = of_id->data; + struct device_node *child; + int ret; + + for_each_child_of_node(dev->of_node, child) { + if (!child->name) + continue; + + ret = smc_timing_setup(dev, child, base, devtype); + if (ret) { + dev_err(dev, "%s set timing failed.\n", + child->full_name); + return ret; + } + } + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) + dev_err(dev, "%s fail to create devices.\n", + dev->of_node->full_name); + return ret; +} + +static int __init smc_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + int ret; + + /* get the resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_request_and_ioremap(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "can't map SMC base address\n"); + return PTR_ERR(base); + } + + /* parse the device node */ + ret = smc_parse_dt(pdev, base); + if (!ret) + dev_info(&pdev->dev, "Driver registered.\n"); + + return ret; +} + +static struct platform_driver smc_driver = { + .driver = { + .name = "atmel-smc", + .owner = THIS_MODULE, + .of_match_table = smc_id_table, + }, +}; +module_platform_driver_probe(smc_driver, smc_probe); + +MODULE_AUTHOR("JJ Hiblot"); +MODULE_DESCRIPTION("Atmel's SMC/EBI driver"); +MODULE_LICENSE("GPL");