Message ID | 1352238427-26085-2-git-send-email-m-karicheri2@ti.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
On Tuesday 06 November 2012 03:47 PM, Murali Karicheri wrote: > This is a platform driver for asynchronous external memory interface > available on TI SoCs. This driver was previously located inside the > mach-davinci folder. As this DaVinci IP is re-used across multiple > family of devices such as c6x, keystone etc, the driver is moved to drivers. > The driver configures async bus parameters associated with a particular > chip select. For DaVinci NAND controller driver and driver for other async > devices such as NOR flash, ASRAM etc, the bus confuguration is > done by this driver at probe. A set of APIs (set/get) are provided to > update the values based on specific driver usage. > > This supports configuration of the bus either through platform_data or > through DT bindings. > > Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> > --- > .../devicetree/bindings/memory/davinci-aemif.txt | 103 +++++ > drivers/memory/Kconfig | 10 + > drivers/memory/Makefile | 1 + > drivers/memory/davinci-aemif.c | 479 ++++++++++++++++++++ > include/linux/platform_data/davinci-aemif.h | 47 ++ > 5 files changed, 640 insertions(+) > create mode 100644 Documentation/devicetree/bindings/memory/davinci-aemif.txt > create mode 100644 drivers/memory/davinci-aemif.c > create mode 100644 include/linux/platform_data/davinci-aemif.h > Please split the DT and drivers/memory/* changes in two seperate patches. > diff --git a/Documentation/devicetree/bindings/memory/davinci-aemif.txt b/Documentation/devicetree/bindings/memory/davinci-aemif.txt > new file mode 100644 > index 0000000..a79b3ed > --- /dev/null > +++ b/Documentation/devicetree/bindings/memory/davinci-aemif.txt > @@ -0,0 +1,103 @@ > +* Texas Instruments Davinci AEMIF bus interface > + > +This file provides information for the davinci-emif device and > +async bus bindings. > + > +Required properties:= > +- compatible: "ti,davinci-aemif"; > +- #address-cells : Should be either two or three. The first cell is the > + chipselect number, and the remaining cells are the > + offset into the chipselect. > +- #size-cells : Either one or two, depending on how large each chipselect > + can be. > +- reg : contains offset/length value for AEMIF control registers space > +- ranges : Each range corresponds to a single chipselect, and cover > + the entire access window as configured. > + > +Child device nodes describe the devices connected to IFC such as NOR (e.g. > +cfi-flash) and NAND (ti,davinci-nand, see Documentation/devicetree/bindings/ > +mtd/davinci-nand.txt). There might be board specific devices like FPGAs. > + > +In addition, optional child sub nodes contains bindings for the async bus > +interface for a given chip select. > + > +Optional cs node properties:- > +- compatible: "ti,davinci-cs" > + > + All of the params below in nanoseconds and are optional > + > +- ti,davinci-cs-asize - asynchronous data bus width (0 - 8bit, 1 - 16 bit) > +- ti,davinci-cs-ta - Minimum turn around time > +- ti,davinci-cs-rhold - read hold width > +- ti,davinci-cs-rstobe - read strobe width > +- ti,davinci-cs-rsetup - read setup width > +- ti,davinci-cs-whold - write hold width > +- ti,davinci-cs-wstrobe - write strobe width > +- ti,davinci-cs-wsetup - write setup width > +- ti,davinci-cs-ss - enable/disable select strobe (0 - disable, 1 - enable) > +- ti,davinci-cs-ew - enable/disable extended wait cycles (0 - disable, 1 - enable) > + > +if any of the above parameters are absent, hardware register default or that > +set by a boot loader are used. > + > +Example for aemif, davinci nand and nor flash chip select shown below. > + > +aemif@60000000 { > + compatible = "ti,davinci-aemif"; > + #address-cells = <2>; > + #size-cells = <1>; > + reg = <0x68000000 0x80000>; > + ranges = <2 0 0x60000000 0x02000000 > + 3 0 0x62000000 0x02000000 > + 4 0 0x64000000 0x02000000 > + 5 0 0x66000000 0x02000000 > + 6 0 0x68000000 0x02000000>; > + > + nand_cs:cs2@60000000 { > + compatible = "ti,davinci-cs"; > + #address-cells = <1>; > + #size-cells = <1>; > + /* all timings in nanoseconds */ > + ti,davinci-cs-ta = <0>; > + ti,davinci-cs-rhold = <7>; > + ti,davinci-cs-rstrobe = <42>; > + ti,davinci-cs-rsetup = <14>; > + ti,davinci-cs-whold = <7>; > + ti,davinci-cs-wstrobe = <42>; > + ti,davinci-cs-wsetup = <14>; > + }; > + > + nor_cs:cs3@62000000 { > + compatible = "ti,davinci-cs"; > + #address-cells = <1>; > + #size-cells = <1>; > + /* all timings in nanoseconds */ > + ti,davinci-cs-ta = <0>; > + ti,davinci-cs-rhold = <7>; > + ti,davinci-cs-rstrobe = <42>; > + ti,davinci-cs-rsetup = <14>; > + ti,davinci-cs-whold = <7>; > + ti,davinci-cs-wstrobe = <42>; > + ti,davinci-cs-wsetup = <14>; > + ti,davinci-cs-asize = <1>; > + }; > + > + nand@3,0 { > + compatible = "ti,davinci-nand"; > + reg = <3 0x0 0x807ff > + 6 0x0 0x8000>; > + #address-cells = <1>; > + #size-cells = <1>; > + > + .. See Documentation/devicetree/bindings/mtd/davinci-nand.txt > + }; > + > + flash@2,0 { > + compatible = "cfi-flash"; > + reg = <2 0x0 0x400000>; > + #address-cells = <1>; > + #size-cells = <1>; > + bank-width = <2>; > + device-width = <2>; > + }; > +}; > diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig > index 067f311..2636a95 100644 > --- a/drivers/memory/Kconfig > +++ b/drivers/memory/Kconfig > @@ -40,4 +40,14 @@ config TEGRA30_MC > analysis, especially for IOMMU/SMMU(System Memory Management > Unit) module. > > +config TI_DAVINCI_AEMIF > + bool "Texas Instruments DaVinci AEMIF driver" Add some default depends on ARCH here otherwise, this driver will get build for all the generic builds and might show build errors. > + help > + This driver is for the AEMIF module available in Texas Instruments > + SoCs. AEMIF stands for Asynchronous External Memory Interface and > + is intended to provide a glue-less interface to a variety of > + asynchronuous memory devices like ASRAM, NOR and NAND memory. A total > + of 256M bytes of any of these memories can be accessed at a given > + time via four chip selects with 64M byte access per chip select. > + > endif > diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile > index 42b3ce9..246aa61 100644 > --- a/drivers/memory/Makefile > +++ b/drivers/memory/Makefile > @@ -5,3 +5,4 @@ > obj-$(CONFIG_TI_EMIF) += emif.o > obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o > obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o > +obj-$(CONFIG_TI_DAVINCI_AEMIF) += davinci-aemif.o > diff --git a/drivers/memory/davinci-aemif.c b/drivers/memory/davinci-aemif.c > new file mode 100644 > index 0000000..27a6995 > --- /dev/null > +++ b/drivers/memory/davinci-aemif.c > @@ -0,0 +1,479 @@ > +/* > + * AEMIF support for DaVinci SoCs > + * > + * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/ s/2010/2012 > + * Copyright (C) Heiko Schocher <hs@denx.de> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/platform_data/davinci-aemif.h> > +#include <linux/platform_device.h> > +#include <linux/time.h> > + > +#define TA_SHIFT 2 > +#define RHOLD_SHIFT 4 > +#define RSTROBE_SHIFT 7 > +#define RSETUP_SHIFT 13 > +#define WHOLD_SHIFT 17 > +#define WSTROBE_SHIFT 20 > +#define WSETUP_SHIFT 26 > +#define EW_SHIFT 30 > +#define SS_SHIFT 31 > + > +#define TA(x) ((x) << TA_SHIFT) > +#define RHOLD(x) ((x) << RHOLD_SHIFT) > +#define RSTROBE(x) ((x) << RSTROBE_SHIFT) > +#define RSETUP(x) ((x) << RSETUP_SHIFT) > +#define WHOLD(x) ((x) << WHOLD_SHIFT) > +#define WSTROBE(x) ((x) << WSTROBE_SHIFT) > +#define WSETUP(x) ((x) << WSETUP_SHIFT) > +#define EW(x) ((x) << EW_SHIFT) > +#define SS(x) ((x) << SS_SHIFT) > + Can you not use BIT(*) directly instead above magics > +#define ASIZE_MAX 0x1 > +#define TA_MAX 0x3 > +#define RHOLD_MAX 0x7 > +#define RSTROBE_MAX 0x3f > +#define RSETUP_MAX 0xf > +#define WHOLD_MAX 0x7 > +#define WSTROBE_MAX 0x3f > +#define WSETUP_MAX 0xf > +#define EW_MAX 0x1 > +#define SS_MAX 0x1 > +#define NUM_CS 4 > + > +#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT) > +#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT) > +#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT) > +#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT) > +#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT) > +#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT) > +#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT) > +#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT) > +#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT) > + Same here.. > + > +#define CONFIG_MASK (TA(TA_MAX) | \ > + RHOLD(RHOLD_MAX) | \ > + RSTROBE(RSTROBE_MAX) | \ > + RSETUP(RSETUP_MAX) | \ > + WHOLD(WHOLD_MAX) | \ > + WSTROBE(WSTROBE_MAX) | \ > + WSETUP(WSETUP_MAX) | \ > + EW(EW_MAX) | SS(SS_MAX) | \ > + ASIZE_MAX) > + This mask is next to impossible if reviewer needs to decode it. > +#define DRV_NAME "davinci-aemif" > + > +struct aemif_device { > + struct davinci_aemif_pdata *cfg; > + void __iomem *base; > + struct clk *clk; > + /* clock rate in KHz */ > + unsigned long clk_rate; > +}; > + > +static struct aemif_device *aemif; > +/** > + * aemif_calc_rate - calculate timing data. > + * @wanted: The cycle time needed in nanoseconds. > + * @clk: The input clock rate in kHz. > + * @max: The maximum divider value that can be programmed. > + * > + * On success, returns the calculated timing value minus 1 for easy > + * programming into AEMIF timing registers, else negative errno. > + */ > +static int aemif_calc_rate(int wanted, unsigned long clk, int max) > +{ > + int result; > + > + result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1; > + > + pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted); > + > + /* It is generally OK to have a more relaxed timing than requested... */ > + if (result < 0) > + result = 0; > + don't break if--else with un-ncessary new line here. > + /* ... But configuring tighter timings is not an option. */ > + else if (result > max) > + result = -EINVAL; > + > + return result; > +} > + > +/** > + * davinci_aemif_config_abus - configure async bus parameters given > + * AEMIF interface > + * @cs: chip-select to program the timing values for > + * @data: aemif chip select configuration > + * @base: aemif io mapped configuration base > + * > + * This function programs the given timing values (in real clock) into the > + * AEMIF registers taking the AEMIF clock into account. > + * > + * This function does not use any locking while programming the AEMIF > + * because it is expected that there is only one user of a given > + * chip-select. > + * > + * Returns 0 on success, else negative errno. > + */ > +static int davinci_aemif_config_abus(unsigned int cs, > + void __iomem *base, > + struct davinci_aemif_cs_data *data) > +{ > + int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; > + unsigned offset = A1CR_OFFSET + cs * 4; > + u32 set, val; > + > + if (!data) > + return -EINVAL; > + > + ta = aemif_calc_rate(data->ta, aemif->clk_rate, TA_MAX); > + rhold = aemif_calc_rate(data->rhold, aemif->clk_rate, RHOLD_MAX); > + rstrobe = aemif_calc_rate(data->rstrobe, aemif->clk_rate, RSTROBE_MAX); > + rsetup = aemif_calc_rate(data->rsetup, aemif->clk_rate, RSETUP_MAX); > + whold = aemif_calc_rate(data->whold, aemif->clk_rate, WHOLD_MAX); > + wstrobe = aemif_calc_rate(data->wstrobe, aemif->clk_rate, WSTROBE_MAX); > + wsetup = aemif_calc_rate(data->wsetup, aemif->clk_rate, WSETUP_MAX); > + > + if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 || > + whold < 0 || wstrobe < 0 || wsetup < 0) { > + pr_err("%s: cannot get suitable timings\n", __func__); > + return -EINVAL; > + } > + > + set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) | > + WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup); > + > + set |= (data->asize & ACR_ASIZE_MASK); > + if (data->enable_ew) > + set |= ACR_EW_MASK; > + if (data->enable_ss) > + set |= ACR_SS_MASK; > + > + val = readl(aemif->base + offset); > + val &= ~CONFIG_MASK; > + val |= set; > + writel(val, aemif->base + offset); > + > + return 0; > +} > + > +inline int aemif_cycles_to_nsec(int val) > +{ > + return (val * NSEC_PER_MSEC) / aemif->clk_rate; > +} > + > +/** > + * davinci_aemif_get_hw_params - function to read hw register values > + * @cs: chip select > + * @data: ptr to cs data > + * > + * This function reads the defaults from the registers and update > + * the timing values. Required for get/set commands and also for > + * the case when driver needs to use defaults in hardware. > + */ > +static void davinci_aemif_get_hw_params(int cs, > + struct davinci_aemif_cs_data *data) > +{ > + u32 val, offset = A1CR_OFFSET + cs * 4; > + > + val = readl(aemif->base + offset); > + data->ta = aemif_cycles_to_nsec(TA_VAL(val)); > + data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val)); > + data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val)); > + data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val)); > + data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val)); > + data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val)); > + data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val)); > + data->enable_ew = EW_VAL(val); > + data->enable_ss = SS_VAL(val); > + data->asize = val & ASIZE_MAX; > +} > + > +/** > + * get_cs_data - helper function to get bus configuration data for a given cs > + * @cs: chip-select, values 2-5 > + */ > +static struct davinci_aemif_cs_data *get_cs_data(int cs) > +{ > + int i; > + > + for (i = 0; i < aemif->cfg->num_cs; i++) { > + if (cs == aemif->cfg->cs_data[i].cs) > + break; > + } > + > + if (i == aemif->cfg->num_cs) > + return NULL; > + > + return &aemif->cfg->cs_data[i]; > +} > + > +/** > + * davinci_aemif_set_abus_params - Set bus configuration data for a given cs > + * @cs: chip-select, values 2-5 > + * @data: ptr to a struct to hold the configuration data to be set > + * > + * This function is called to configure emif bus parameters for a given cs. > + * Users call this function after calling davinci_aemif_get_abus_params() > + * to get current parameters, modify and call this function > + */ > +int davinci_aemif_set_abus_params(unsigned int cs, > + struct davinci_aemif_cs_data *data) > +{ > + struct davinci_aemif_cs_data *curr_cs_data; > + int ret = -EINVAL, chip_cs; > + > + if (data == NULL) > + return ret; > + > + if (aemif->base == NULL) > + return ret; > + > + /* translate to chip CS which starts at 2 */ what is chip CS ? you can either use CS or chip select.. > + chip_cs = cs + 2; Use a macro like AEMIF_CS_START instead of hardcoding it. > + > + curr_cs_data = get_cs_data(chip_cs); > + if (curr_cs_data) { > + /* for configuration we use cs since it is used to index ACR */ > + ret = davinci_aemif_config_abus(chip_cs, aemif->base, data); > + if (!ret) { > + *curr_cs_data = *data; > + return 0; > + } > + } > + > + return ret; > +} > +EXPORT_SYMBOL(davinci_aemif_set_abus_params); > + Who is the user of this EXPORT ? > +/** > + * davinci_aemif_get_abus_params - Get bus configuration data for a given cs > + * @cs: chip-select, values 2-5 > + * returns: ptr to a struct having the current configuration data > + */ > +struct davinci_aemif_cs_data *davinci_aemif_get_abus_params(unsigned int cs) > +{ > + /* translate to chip CS which starts at 2 */ > + return get_cs_data(cs + 2); > +} > +EXPORT_SYMBOL(davinci_aemif_get_abus_params); > + This one too. [...] > +static int __devinit davinci_aemif_probe(struct platform_device *pdev) > +{ > + struct davinci_aemif_pdata *cfg; > + int ret = -ENODEV, i; > + struct resource *res; > + > + aemif = devm_kzalloc(&pdev->dev, sizeof(*aemif), GFP_KERNEL); > + > + if (!aemif) > + return -ENOMEM; > + > + aemif->clk = clk_get(NULL, "aemif"); Please use dev attributes. Above usage of clk_get isn't recommonded in drivers. You might have to add alias entries in your clock data for it to work though. > + if (IS_ERR(aemif->clk)) > + return PTR_ERR(aemif->clk); > + > + clk_prepare_enable(aemif->clk); > + aemif->clk_rate = clk_get_rate(aemif->clk) / 1000; > + /1000 for what ? Converting it into KHz ? > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + pr_err("No IO memory address defined\n"); > + goto error; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + aemif->base = devm_request_and_ioremap(&pdev->dev, res); > + if (!aemif->base) { > + ret = -EBUSY; > + pr_err("ioremap failed\n"); > + goto error; > + } > + > + if (pdev->dev.platform_data == NULL) { > + /* Not platform data, we get the cs data from the cs nodes */ > + cfg = devm_kzalloc(&pdev->dev, sizeof(*cfg), GFP_KERNEL); > + if (cfg == NULL) > + return -ENOMEM; > + > + aemif->cfg = cfg; > + if (of_davinci_aemif_cs_init(pdev->dev.of_node) < 0) { > + pr_err("No platform data or cs of node present\n"); > + goto error; > + } > + } else { > + cfg = pdev->dev.platform_data; > + aemif->cfg = cfg; > + } > + > + for (i = 0; i < cfg->num_cs; i++) { > + /* cs is from 2-5. Internally we use cs-2 to access ACR */ > + ret = davinci_aemif_config_abus(cfg->cs_data[i].cs - 2, > + aemif->base, &cfg->cs_data[i]); > + if (ret < 0) { > + pr_err("Error configuring chip select %d\n", > + cfg->cs_data[i].cs); > + goto error; > + } > + } > + return 0; > +error: > + clk_disable_unprepare(aemif->clk); > + clk_put(aemif->clk); Alos unmap 'aemif->base' here.. > + return ret; > +} > + > +static struct platform_driver davinci_aemif_driver = { > + .probe = davinci_aemif_probe, > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + .of_match_table = davinci_aemif_of_match, > + }, > +}; > + > +static int __init davinci_aemif_init(void) > +{ > + return platform_driver_register(&davinci_aemif_driver); > +} > +subsys_initcall(davinci_aemif_init); > + > +static void __exit davinci_aemif_exit(void) > +{ > + clk_disable_unprepare(aemif->clk); > + clk_put(aemif->clk); > + platform_driver_unregister(&davinci_aemif_driver); > +} > +module_exit(davinci_aemif_exit); > + > +MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>"); > +MODULE_DESCRIPTION("Texas Instruments AEMIF driver"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:" DRV_NAME); > diff --git a/include/linux/platform_data/davinci-aemif.h b/include/linux/platform_data/davinci-aemif.h > new file mode 100644 > index 0000000..03f3ad0 > --- /dev/null > +++ b/include/linux/platform_data/davinci-aemif.h > @@ -0,0 +1,47 @@ > +/* > + * TI DaVinci AEMIF support > + * > + * Copyright 2010 (C) Texas Instruments, Inc. http://www.ti.com/ > + * s/2010/2012 > + * 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. > + */ > +#ifndef _MACH_DAVINCI_AEMIF_H > +#define _MACH_DAVINCI_AEMIF_H Fix the header guard as per new directory Regards Santosh
Santhosh, Thanks for the review. Some of the comments below applies to the original code as this driver is moved from mach-davinci to memory. I will fix them though. See below my response. >> + >> + curr_cs_data = get_cs_data(chip_cs); >> + if (curr_cs_data) { >> + /* for configuration we use cs since it is used to index ACR */ >> + ret = davinci_aemif_config_abus(chip_cs, aemif->base, data); >> + if (!ret) { >> + *curr_cs_data = *data; >> + return 0; >> + } >> + } >> + >> + return ret; >> +} >> +EXPORT_SYMBOL(davinci_aemif_set_abus_params); >> + > Who is the user of this EXPORT ? Mostly not needed, but there is a use case for connecting FPGA that needs to do the configuration dynamically I guess. Want to hear from others if this is needed? Same for below comment. May be we can remove this now and add it later if needed. > >> +/** >> + * davinci_aemif_get_abus_params - Get bus configuration data for a >> given cs >> + * @cs: chip-select, values 2-5 >> + * returns: ptr to a struct having the current configuration data >> + */ >> +struct davinci_aemif_cs_data *davinci_aemif_get_abus_params(unsigned >> int cs) >> +{ >> + /* translate to chip CS which starts at 2 */ >> + return get_cs_data(cs + 2); >> +} >> +EXPORT_SYMBOL(davinci_aemif_get_abus_params); >> + > This one too. > > [...] > >> +static int __devinit davinci_aemif_probe(struct platform_device *pdev) >> +{ >> + struct davinci_aemif_pdata *cfg; >> + int ret = -ENODEV, i; >> + struct resource *res; >> + >> + aemif = devm_kzalloc(&pdev->dev, sizeof(*aemif), GFP_KERNEL); >> + >> + if (!aemif) >> + return -ENOMEM; >> + >> + aemif->clk = clk_get(NULL, "aemif"); > Please use dev attributes. Above usage of clk_get isn't recommonded > in drivers. You might have to add alias entries in your clock data for > it to work though. > Need to investigate. >> + if (IS_ERR(aemif->clk)) >> + return PTR_ERR(aemif->clk); >> + >> + clk_prepare_enable(aemif->clk); >> + aemif->clk_rate = clk_get_rate(aemif->clk) / 1000; >> + > /1000 for what ? Converting it into KHz ? > Yes. WIll add a comment. >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + pr_err("No IO memory address defined\n"); >> + goto error; >> + } >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + >> + aemif->base = devm_request_and_ioremap(&pdev->dev, res); >> + if (!aemif->base) { >> + ret = -EBUSY; >> + pr_err("ioremap failed\n"); >> + goto error; >> + } >> + >> + if (pdev->dev.platform_data == NULL) { >> + /* Not platform data, we get the cs data from the cs nodes */ >> + cfg = devm_kzalloc(&pdev->dev, sizeof(*cfg), GFP_KERNEL); >> + if (cfg == NULL) >> + return -ENOMEM; >> + >> + aemif->cfg = cfg; >> + if (of_davinci_aemif_cs_init(pdev->dev.of_node) < 0) { >> + pr_err("No platform data or cs of node present\n"); >> + goto error; >> + } >> + } else { >> + cfg = pdev->dev.platform_data; >> + aemif->cfg = cfg; >> + } >> + >> + for (i = 0; i < cfg->num_cs; i++) { >> + /* cs is from 2-5. Internally we use cs-2 to access ACR */ >> + ret = davinci_aemif_config_abus(cfg->cs_data[i].cs - 2, >> + aemif->base, &cfg->cs_data[i]); >> + if (ret < 0) { >> + pr_err("Error configuring chip select %d\n", >> + cfg->cs_data[i].cs); >> + goto error; >> + } >> + } >> + return 0; >> +error: >> + clk_disable_unprepare(aemif->clk); >> + clk_put(aemif->clk); > Alos unmap 'aemif->base' here.. > I thought devm_ API do this automatically. I will check. >> + return ret; >> +} >> + >> +static struct platform_driver davinci_aemif_driver = { >> + .probe = davinci_aemif_probe, >> + .driver = { >> + .name = DRV_NAME, >> + .owner = THIS_MODULE, >> + .of_match_table = davinci_aemif_of_match, >> + }, >> +}; >> + >> +static int __init davinci_aemif_init(void) >> +{ >> + return platform_driver_register(&davinci_aemif_driver); >> +} >> +subsys_initcall(davinci_aemif_init); >> + >> +static void __exit davinci_aemif_exit(void) >> +{ >> + clk_disable_unprepare(aemif->clk); >> + clk_put(aemif->clk); >> + platform_driver_unregister(&davinci_aemif_driver); >> +} >> +module_exit(davinci_aemif_exit); >> + >> +MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>"); >> +MODULE_DESCRIPTION("Texas Instruments AEMIF driver"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_ALIAS("platform:" DRV_NAME); >> diff --git a/include/linux/platform_data/davinci-aemif.h >> b/include/linux/platform_data/davinci-aemif.h >> new file mode 100644 >> index 0000000..03f3ad0 >> --- /dev/null >> +++ b/include/linux/platform_data/davinci-aemif.h >> @@ -0,0 +1,47 @@ >> +/* >> + * TI DaVinci AEMIF support >> + * >> + * Copyright 2010 (C) Texas Instruments, Inc. http://www.ti.com/ >> + * > s/2010/2012 >> + * 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. >> + */ >> +#ifndef _MACH_DAVINCI_AEMIF_H >> +#define _MACH_DAVINCI_AEMIF_H > Fix the header guard as per new directory > Ok. > Regards > Santosh > >
On 11/06/2012 02:47 PM, Murali Karicheri wrote: > This is a platform driver for asynchronous external memory interface > available on TI SoCs. This driver was previously located inside the > mach-davinci folder. As this DaVinci IP is re-used across multiple > family of devices such as c6x, keystone etc, the driver is moved to drivers. > The driver configures async bus parameters associated with a particular > chip select. For DaVinci NAND controller driver and driver for other async > devices such as NOR flash, ASRAM etc, the bus confuguration is > done by this driver at probe. A set of APIs (set/get) are provided to > update the values based on specific driver usage. > diff --git a/Documentation/devicetree/bindings/memory/davinci-aemif.txt b/Documentation/devicetree/bindings/memory/davinci-aemif.txt > +* Texas Instruments Davinci AEMIF bus interface > + > +This file provides information for the davinci-emif device and > +async bus bindings. > + > +Required properties:= > +- compatible: "ti,davinci-aemif"; > +- #address-cells : Should be either two or three. The first cell is the > + chipselect number, and the remaining cells are the > + offset into the chipselect. > +- #size-cells : Either one or two, depending on how large each chipselect > + can be. What about "how large each chipselect can be" determines 2-vs-3 (address) or 1-vs-2 (size) cells? I assume it's 32-vs-64-bit bus? It might be best to make that explicit. > +- reg : contains offset/length value for AEMIF control registers space > +- ranges : Each range corresponds to a single chipselect, and cover add "s" at the end of that line. > + the entire access window as configured. > + > +Child device nodes describe the devices connected to IFC such as NOR (e.g. > +cfi-flash) and NAND (ti,davinci-nand, see Documentation/devicetree/bindings/ > +mtd/davinci-nand.txt). There might be board specific devices like FPGAs. > + > +In addition, optional child sub nodes contains bindings for the async bus > +interface for a given chip select. Hmmm. That's two different kinds of children, which appear to use different addressing schemes given the examples below; more on that below. > +Optional cs node properties:- > +- compatible: "ti,davinci-cs" > + > + All of the params below in nanoseconds and are optional > + > +- ti,davinci-cs-asize - asynchronous data bus width (0 - 8bit, 1 - 16 bit) Perhaps s/asize/bus-width/? Also, directly using values 8 and 16 would be a lot more obvious to read than 0 and 1. > +- ti,davinci-cs-ta - Minimum turn around time > +- ti,davinci-cs-rhold - read hold width > +- ti,davinci-cs-rstobe - read strobe width > +- ti,davinci-cs-rsetup - read setup width > +- ti,davinci-cs-whold - write hold width > +- ti,davinci-cs-wstrobe - write strobe width > +- ti,davinci-cs-wsetup - write setup width The comments in the examples indicate these are in nS. This should be mentioned here instead I think. Does there need to be a specification of bus clock rate? How does the driver convert nS into number-of-clock-cycles, which I assume is what the HW would be programmed with? > +- ti,davinci-cs-ss - enable/disable select strobe (0 - disable, 1 - enable) > +- ti,davinci-cs-ew - enable/disable extended wait cycles (0 - disable, 1 - enable) Boolean properties are usually represented as present (on/enabled/1) or missing (off/disabled/0). Shouldn't these two properties work that way? I see the comment below: > +if any of the above parameters are absent, hardware register default or that > +set by a boot loader are used. implied they're really tri-state (explicitly off or on, or just not programmed). However, I think it'd be better if the DT always represented the complete configuration, since the DT and binding should be a full description of the HW rather than just the data you expect a particular Linux driver to need after a particular boot loader has executed first. > +Example for aemif, davinci nand and nor flash chip select shown below. > + > +aemif@60000000 { > + compatible = "ti,davinci-aemif"; > + #address-cells = <2>; > + #size-cells = <1>; > + reg = <0x68000000 0x80000>; > + ranges = <2 0 0x60000000 0x02000000 > + 3 0 0x62000000 0x02000000 > + 4 0 0x64000000 0x02000000 > + 5 0 0x66000000 0x02000000 > + 6 0 0x68000000 0x02000000>; > + > + nand_cs:cs2@60000000 { This node has no reg property, but it needs one if you use a unit address ("@60000000") in the node name. The numbering scheme unit address above doesn't seem to match the addresses provided to child nodes by the ranges property. Perhaps the node layout you want is: aemif { chip-selects { nand_cs:cs2@60000000 { }; }; devices { nand@3,0 { } }; }; so that the chip-selects and devices nodes can both set appropriate and different #address-cells and #size-cells? That does feel a bit wierd though, and I imagine writing the ranges property in the aemif node would be hard. Perhaps the chip-select timings should just be written as properties in the aemif controller, rather than child nodes? ti,cs-timings = < ... > /* 0 */ < ... > /* 1 */ < ... > /* 2 */ ; with each <...> being a list of n cells in a specific order? > + compatible = "ti,davinci-cs"; > + #address-cells = <1>; > + #size-cells = <1>; > + /* all timings in nanoseconds */ > + ti,davinci-cs-ta = <0>; > + ti,davinci-cs-rhold = <7>; > + ti,davinci-cs-rstrobe = <42>; > + ti,davinci-cs-rsetup = <14>; > + ti,davinci-cs-whold = <7>; > + ti,davinci-cs-wstrobe = <42>; > + ti,davinci-cs-wsetup = <14>; > + }; ... > + nand@3,0 { > + compatible = "ti,davinci-nand"; > + reg = <3 0x0 0x807ff > + 6 0x0 0x8000>; > + #address-cells = <1>; > + #size-cells = <1>; > + > + .. See Documentation/devicetree/bindings/mtd/davinci-nand.txt > + };
Stephen, Thanks for reviewing this. See my responses below On 11/07/2012 03:05 PM, Stephen Warren wrote: > On 11/06/2012 02:47 PM, Murali Karicheri wrote: >> This is a platform driver for asynchronous external memory interface >> available on TI SoCs. This driver was previously located inside the >> mach-davinci folder. As this DaVinci IP is re-used across multiple >> family of devices such as c6x, keystone etc, the driver is moved to drivers. >> The driver configures async bus parameters associated with a particular >> chip select. For DaVinci NAND controller driver and driver for other async >> devices such as NOR flash, ASRAM etc, the bus confuguration is >> done by this driver at probe. A set of APIs (set/get) are provided to >> update the values based on specific driver usage. >> diff --git a/Documentation/devicetree/bindings/memory/davinci-aemif.txt b/Documentation/devicetree/bindings/memory/davinci-aemif.txt >> +* Texas Instruments Davinci AEMIF bus interface >> + >> +This file provides information for the davinci-emif device and >> +async bus bindings. >> + >> +Required properties:= >> +- compatible: "ti,davinci-aemif"; >> +- #address-cells : Should be either two or three. The first cell is the >> + chipselect number, and the remaining cells are the >> + offset into the chipselect. >> +- #size-cells : Either one or two, depending on how large each chipselect >> + can be. > What about "how large each chipselect can be" determines 2-vs-3 > (address) or 1-vs-2 (size) cells? I assume it's 32-vs-64-bit bus? It > might be best to make that explicit. I have re-used the bindings from another patch and don't know why the above description is used. I am using a value of 2 for address cells and 1 for size-cells which I believe is how these will be used as shown below. ranges = <2 0 0x70000000 0x08000000 3 0 0x74000000 0x04000000 4 0 0x78000000 0x04000000 5 0 0x7C000000 0x04000000 6 0 0x20c00000 0x100>; So I will change the description as below. - compatible: must be "ti,davinci-aemif" - reg: AEMIF control registers base and size - #address-cells: must be 2 (chip-select + offset) - #size-cells: must be 1 - ranges: mapping from EMIF space to parent space. Each range corresponds to a single chip select, and covers the entire access window as configured. >> +- reg : contains offset/length value for AEMIF control registers space >> +- ranges : Each range corresponds to a single chipselect, and cover > add "s" at the end of that line. Ok >> + the entire access window as configured. >> + >> +Child device nodes describe the devices connected to IFC such as NOR (e.g. >> +cfi-flash) and NAND (ti,davinci-nand, see Documentation/devicetree/bindings/ >> +mtd/davinci-nand.txt). There might be board specific devices like FPGAs. >> + >> +In addition, optional child sub nodes contains bindings for the async bus >> +interface for a given chip select. > Hmmm. That's two different kinds of children, which appear to use > different addressing schemes given the examples below; more on that below. > >> +Optional cs node properties:- >> +- compatible: "ti,davinci-cs" >> + >> + All of the params below in nanoseconds and are optional >> + >> +- ti,davinci-cs-asize - asynchronous data bus width (0 - 8bit, 1 - 16 bit) > Perhaps s/asize/bus-width/? Also, directly using values 8 and 16 would > be a lot more obvious to read than 0 and 1. Ok. Will fix, > >> +- ti,davinci-cs-ta - Minimum turn around time >> +- ti,davinci-cs-rhold - read hold width >> +- ti,davinci-cs-rstobe - read strobe width >> +- ti,davinci-cs-rsetup - read setup width >> +- ti,davinci-cs-whold - write hold width >> +- ti,davinci-cs-wstrobe - write strobe width >> +- ti,davinci-cs-wsetup - write setup width > The comments in the examples indicate these are in nS. This should be > mentioned here instead I think. Ok. > > Does there need to be a specification of bus clock rate? How does the > driver convert nS into number-of-clock-cycles, which I assume is what > the HW would be programmed with? aemif driver clk is either specified as a clk device node or as a device bindings. Aemif driver gets the clk rate and do the conversion of ns to emif clk cycles internally in the driver. >> +- ti,davinci-cs-ss - enable/disable select strobe (0 - disable, 1 - enable) >> +- ti,davinci-cs-ew - enable/disable extended wait cycles (0 - disable, 1 - enable) > Boolean properties are usually represented as present (on/enabled/1) or > missing (off/disabled/0). Shouldn't these two properties work that way? > I see the comment below: >> +if any of the above parameters are absent, hardware register default or that >> +set by a boot loader are used. > implied they're really tri-state (explicitly off or on, or just not > programmed). However, I think it'd be better if the DT always > represented the complete configuration, since the DT and binding should > be a full description of the HW rather than just the data you expect a > particular Linux driver to need after a particular boot loader has > executed first. Actually this driver has to work on various platforms some of them set values in boot loader, others explicitly specify it in bindings or platform data. If set in boot loader, these bindings are not required (so that it is compatible with old implementation) to be configured in Linux kernel. So I believe I should be using something like ti,davinci-cs-enable-ss - "on", "off" As cs node is optional, If not present, hw values will be used. I don't think it make sense to make it tri-state based on my explanation above. >> +Example for aemif, davinci nand and nor flash chip select shown below. >> + >> +aemif@60000000 { >> + compatible = "ti,davinci-aemif"; >> + #address-cells = <2>; >> + #size-cells = <1>; >> + reg = <0x68000000 0x80000>; >> + ranges = <2 0 0x60000000 0x02000000 >> + 3 0 0x62000000 0x02000000 >> + 4 0 0x64000000 0x02000000 >> + 5 0 0x66000000 0x02000000 >> + 6 0 0x68000000 0x02000000>; >> + >> + nand_cs:cs2@60000000 { > This node has no reg property, but it needs one if you use a unit > address ("@60000000") in the node name. So let me drop the unit address as it is not needed for the cs node. The AEMIF control register address is specified by the parent reg property and is common to all chip selects. > The numbering scheme unit address above doesn't seem to match the > addresses provided to child nodes by the ranges property. Perhaps the > node layout you want is: > > > aemif { > chip-selects { > nand_cs:cs2@60000000 { > }; > }; > devices { > nand@3,0 { > } > }; > }; > > so that the chip-selects and devices nodes can both set appropriate and > different #address-cells and #size-cells? > > That does feel a bit wierd though, and I imagine writing the ranges > property in the aemif node would be hard. > > Perhaps the chip-select timings should just be written as properties in > the aemif controller, rather than child nodes? > > ti,cs-timings = > < ... > /* 0 */ > < ... > /* 1 */ > < ... > /* 2 */ > ; > > with each <...> being a list of n cells in a specific order? > To make this simple, I will drop the unit address from cs node and represent it as nand_cs:cs2 { }; nor_cs:cs3 { }; + compatible = "ti,davinci-cs"; + #address-cells = <1>; + #size-cells = <1>; + /* all timings in nanoseconds */ + ti,davinci-cs-ta = <0>; + ti,davinci-cs-rhold = <7>; + ti,davinci-cs-rstrobe = <42>; + ti,davinci-cs-rsetup = <14>; + ti,davinci-cs-whold = <7>; + ti,davinci-cs-wstrobe = <42>; + ti,davinci-cs-wsetup = <14>; + }; > ... >> + nand@3,0 { >> + compatible = "ti,davinci-nand"; >> + reg = <3 0x0 0x807ff >> + 6 0x0 0x8000>; >> + #address-cells = <1>; >> + #size-cells = <1>; >> + >> + .. See Documentation/devicetree/bindings/mtd/davinci-nand.txt >> + }; > > >
diff --git a/Documentation/devicetree/bindings/memory/davinci-aemif.txt b/Documentation/devicetree/bindings/memory/davinci-aemif.txt new file mode 100644 index 0000000..a79b3ed --- /dev/null +++ b/Documentation/devicetree/bindings/memory/davinci-aemif.txt @@ -0,0 +1,103 @@ +* Texas Instruments Davinci AEMIF bus interface + +This file provides information for the davinci-emif device and +async bus bindings. + +Required properties:= +- compatible: "ti,davinci-aemif"; +- #address-cells : Should be either two or three. The first cell is the + chipselect number, and the remaining cells are the + offset into the chipselect. +- #size-cells : Either one or two, depending on how large each chipselect + can be. +- reg : contains offset/length value for AEMIF control registers space +- ranges : Each range corresponds to a single chipselect, and cover + the entire access window as configured. + +Child device nodes describe the devices connected to IFC such as NOR (e.g. +cfi-flash) and NAND (ti,davinci-nand, see Documentation/devicetree/bindings/ +mtd/davinci-nand.txt). There might be board specific devices like FPGAs. + +In addition, optional child sub nodes contains bindings for the async bus +interface for a given chip select. + +Optional cs node properties:- +- compatible: "ti,davinci-cs" + + All of the params below in nanoseconds and are optional + +- ti,davinci-cs-asize - asynchronous data bus width (0 - 8bit, 1 - 16 bit) +- ti,davinci-cs-ta - Minimum turn around time +- ti,davinci-cs-rhold - read hold width +- ti,davinci-cs-rstobe - read strobe width +- ti,davinci-cs-rsetup - read setup width +- ti,davinci-cs-whold - write hold width +- ti,davinci-cs-wstrobe - write strobe width +- ti,davinci-cs-wsetup - write setup width +- ti,davinci-cs-ss - enable/disable select strobe (0 - disable, 1 - enable) +- ti,davinci-cs-ew - enable/disable extended wait cycles (0 - disable, 1 - enable) + +if any of the above parameters are absent, hardware register default or that +set by a boot loader are used. + +Example for aemif, davinci nand and nor flash chip select shown below. + +aemif@60000000 { + compatible = "ti,davinci-aemif"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x68000000 0x80000>; + ranges = <2 0 0x60000000 0x02000000 + 3 0 0x62000000 0x02000000 + 4 0 0x64000000 0x02000000 + 5 0 0x66000000 0x02000000 + 6 0 0x68000000 0x02000000>; + + nand_cs:cs2@60000000 { + compatible = "ti,davinci-cs"; + #address-cells = <1>; + #size-cells = <1>; + /* all timings in nanoseconds */ + ti,davinci-cs-ta = <0>; + ti,davinci-cs-rhold = <7>; + ti,davinci-cs-rstrobe = <42>; + ti,davinci-cs-rsetup = <14>; + ti,davinci-cs-whold = <7>; + ti,davinci-cs-wstrobe = <42>; + ti,davinci-cs-wsetup = <14>; + }; + + nor_cs:cs3@62000000 { + compatible = "ti,davinci-cs"; + #address-cells = <1>; + #size-cells = <1>; + /* all timings in nanoseconds */ + ti,davinci-cs-ta = <0>; + ti,davinci-cs-rhold = <7>; + ti,davinci-cs-rstrobe = <42>; + ti,davinci-cs-rsetup = <14>; + ti,davinci-cs-whold = <7>; + ti,davinci-cs-wstrobe = <42>; + ti,davinci-cs-wsetup = <14>; + ti,davinci-cs-asize = <1>; + }; + + nand@3,0 { + compatible = "ti,davinci-nand"; + reg = <3 0x0 0x807ff + 6 0x0 0x8000>; + #address-cells = <1>; + #size-cells = <1>; + + .. See Documentation/devicetree/bindings/mtd/davinci-nand.txt + }; + + flash@2,0 { + compatible = "cfi-flash"; + reg = <2 0x0 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + bank-width = <2>; + device-width = <2>; + }; +}; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 067f311..2636a95 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -40,4 +40,14 @@ config TEGRA30_MC analysis, especially for IOMMU/SMMU(System Memory Management Unit) module. +config TI_DAVINCI_AEMIF + bool "Texas Instruments DaVinci AEMIF driver" + help + This driver is for the AEMIF module available in Texas Instruments + SoCs. AEMIF stands for Asynchronous External Memory Interface and + is intended to provide a glue-less interface to a variety of + asynchronuous memory devices like ASRAM, NOR and NAND memory. A total + of 256M bytes of any of these memories can be accessed at a given + time via four chip selects with 64M byte access per chip select. + endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 42b3ce9..246aa61 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_TI_EMIF) += emif.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o +obj-$(CONFIG_TI_DAVINCI_AEMIF) += davinci-aemif.o diff --git a/drivers/memory/davinci-aemif.c b/drivers/memory/davinci-aemif.c new file mode 100644 index 0000000..27a6995 --- /dev/null +++ b/drivers/memory/davinci-aemif.c @@ -0,0 +1,479 @@ +/* + * AEMIF support for DaVinci SoCs + * + * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/ + * Copyright (C) Heiko Schocher <hs@denx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_data/davinci-aemif.h> +#include <linux/platform_device.h> +#include <linux/time.h> + +#define TA_SHIFT 2 +#define RHOLD_SHIFT 4 +#define RSTROBE_SHIFT 7 +#define RSETUP_SHIFT 13 +#define WHOLD_SHIFT 17 +#define WSTROBE_SHIFT 20 +#define WSETUP_SHIFT 26 +#define EW_SHIFT 30 +#define SS_SHIFT 31 + +#define TA(x) ((x) << TA_SHIFT) +#define RHOLD(x) ((x) << RHOLD_SHIFT) +#define RSTROBE(x) ((x) << RSTROBE_SHIFT) +#define RSETUP(x) ((x) << RSETUP_SHIFT) +#define WHOLD(x) ((x) << WHOLD_SHIFT) +#define WSTROBE(x) ((x) << WSTROBE_SHIFT) +#define WSETUP(x) ((x) << WSETUP_SHIFT) +#define EW(x) ((x) << EW_SHIFT) +#define SS(x) ((x) << SS_SHIFT) + +#define ASIZE_MAX 0x1 +#define TA_MAX 0x3 +#define RHOLD_MAX 0x7 +#define RSTROBE_MAX 0x3f +#define RSETUP_MAX 0xf +#define WHOLD_MAX 0x7 +#define WSTROBE_MAX 0x3f +#define WSETUP_MAX 0xf +#define EW_MAX 0x1 +#define SS_MAX 0x1 +#define NUM_CS 4 + +#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT) +#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT) +#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT) +#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT) +#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT) +#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT) +#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT) +#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT) +#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT) + + +#define CONFIG_MASK (TA(TA_MAX) | \ + RHOLD(RHOLD_MAX) | \ + RSTROBE(RSTROBE_MAX) | \ + RSETUP(RSETUP_MAX) | \ + WHOLD(WHOLD_MAX) | \ + WSTROBE(WSTROBE_MAX) | \ + WSETUP(WSETUP_MAX) | \ + EW(EW_MAX) | SS(SS_MAX) | \ + ASIZE_MAX) + +#define DRV_NAME "davinci-aemif" + +struct aemif_device { + struct davinci_aemif_pdata *cfg; + void __iomem *base; + struct clk *clk; + /* clock rate in KHz */ + unsigned long clk_rate; +}; + +static struct aemif_device *aemif; +/** + * aemif_calc_rate - calculate timing data. + * @wanted: The cycle time needed in nanoseconds. + * @clk: The input clock rate in kHz. + * @max: The maximum divider value that can be programmed. + * + * On success, returns the calculated timing value minus 1 for easy + * programming into AEMIF timing registers, else negative errno. + */ +static int aemif_calc_rate(int wanted, unsigned long clk, int max) +{ + int result; + + result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1; + + pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted); + + /* It is generally OK to have a more relaxed timing than requested... */ + if (result < 0) + result = 0; + + /* ... But configuring tighter timings is not an option. */ + else if (result > max) + result = -EINVAL; + + return result; +} + +/** + * davinci_aemif_config_abus - configure async bus parameters given + * AEMIF interface + * @cs: chip-select to program the timing values for + * @data: aemif chip select configuration + * @base: aemif io mapped configuration base + * + * This function programs the given timing values (in real clock) into the + * AEMIF registers taking the AEMIF clock into account. + * + * This function does not use any locking while programming the AEMIF + * because it is expected that there is only one user of a given + * chip-select. + * + * Returns 0 on success, else negative errno. + */ +static int davinci_aemif_config_abus(unsigned int cs, + void __iomem *base, + struct davinci_aemif_cs_data *data) +{ + int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; + unsigned offset = A1CR_OFFSET + cs * 4; + u32 set, val; + + if (!data) + return -EINVAL; + + ta = aemif_calc_rate(data->ta, aemif->clk_rate, TA_MAX); + rhold = aemif_calc_rate(data->rhold, aemif->clk_rate, RHOLD_MAX); + rstrobe = aemif_calc_rate(data->rstrobe, aemif->clk_rate, RSTROBE_MAX); + rsetup = aemif_calc_rate(data->rsetup, aemif->clk_rate, RSETUP_MAX); + whold = aemif_calc_rate(data->whold, aemif->clk_rate, WHOLD_MAX); + wstrobe = aemif_calc_rate(data->wstrobe, aemif->clk_rate, WSTROBE_MAX); + wsetup = aemif_calc_rate(data->wsetup, aemif->clk_rate, WSETUP_MAX); + + if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 || + whold < 0 || wstrobe < 0 || wsetup < 0) { + pr_err("%s: cannot get suitable timings\n", __func__); + return -EINVAL; + } + + set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) | + WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup); + + set |= (data->asize & ACR_ASIZE_MASK); + if (data->enable_ew) + set |= ACR_EW_MASK; + if (data->enable_ss) + set |= ACR_SS_MASK; + + val = readl(aemif->base + offset); + val &= ~CONFIG_MASK; + val |= set; + writel(val, aemif->base + offset); + + return 0; +} + +inline int aemif_cycles_to_nsec(int val) +{ + return (val * NSEC_PER_MSEC) / aemif->clk_rate; +} + +/** + * davinci_aemif_get_hw_params - function to read hw register values + * @cs: chip select + * @data: ptr to cs data + * + * This function reads the defaults from the registers and update + * the timing values. Required for get/set commands and also for + * the case when driver needs to use defaults in hardware. + */ +static void davinci_aemif_get_hw_params(int cs, + struct davinci_aemif_cs_data *data) +{ + u32 val, offset = A1CR_OFFSET + cs * 4; + + val = readl(aemif->base + offset); + data->ta = aemif_cycles_to_nsec(TA_VAL(val)); + data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val)); + data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val)); + data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val)); + data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val)); + data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val)); + data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val)); + data->enable_ew = EW_VAL(val); + data->enable_ss = SS_VAL(val); + data->asize = val & ASIZE_MAX; +} + +/** + * get_cs_data - helper function to get bus configuration data for a given cs + * @cs: chip-select, values 2-5 + */ +static struct davinci_aemif_cs_data *get_cs_data(int cs) +{ + int i; + + for (i = 0; i < aemif->cfg->num_cs; i++) { + if (cs == aemif->cfg->cs_data[i].cs) + break; + } + + if (i == aemif->cfg->num_cs) + return NULL; + + return &aemif->cfg->cs_data[i]; +} + +/** + * davinci_aemif_set_abus_params - Set bus configuration data for a given cs + * @cs: chip-select, values 2-5 + * @data: ptr to a struct to hold the configuration data to be set + * + * This function is called to configure emif bus parameters for a given cs. + * Users call this function after calling davinci_aemif_get_abus_params() + * to get current parameters, modify and call this function + */ +int davinci_aemif_set_abus_params(unsigned int cs, + struct davinci_aemif_cs_data *data) +{ + struct davinci_aemif_cs_data *curr_cs_data; + int ret = -EINVAL, chip_cs; + + if (data == NULL) + return ret; + + if (aemif->base == NULL) + return ret; + + /* translate to chip CS which starts at 2 */ + chip_cs = cs + 2; + + curr_cs_data = get_cs_data(chip_cs); + if (curr_cs_data) { + /* for configuration we use cs since it is used to index ACR */ + ret = davinci_aemif_config_abus(chip_cs, aemif->base, data); + if (!ret) { + *curr_cs_data = *data; + return 0; + } + } + + return ret; +} +EXPORT_SYMBOL(davinci_aemif_set_abus_params); + +/** + * davinci_aemif_get_abus_params - Get bus configuration data for a given cs + * @cs: chip-select, values 2-5 + * returns: ptr to a struct having the current configuration data + */ +struct davinci_aemif_cs_data *davinci_aemif_get_abus_params(unsigned int cs) +{ + /* translate to chip CS which starts at 2 */ + return get_cs_data(cs + 2); +} +EXPORT_SYMBOL(davinci_aemif_get_abus_params); + +#if defined(CONFIG_OF) +/** + * dv_get_value - helper function to read a attribute valye + * @np: device node ptr + * @name: name of the attribute + * returns: value of the attribute + */ +static int dv_get_value(struct device_node *np, const char *name) +{ + u32 data; + int ret = -EINVAL; + + ret = of_property_read_u32(np, name, &data); + if (ret != 0) + return ret; + + return data; +} + +/** + * of_davinci_aemif_parse_abus_config - parse bus config data from a cs node + * @np: device node ptr + * + * This function update the emif async bus configuration based on the values + * configured in a cs device binding node. + */ +static int of_davinci_aemif_parse_abus_config(struct device_node *np) +{ + struct davinci_aemif_cs_data *data; + unsigned long cs; + int val; + + if (kstrtoul(np->name + 2, 10, &cs) < 0) + return -EINVAL; + + if (cs < 2 || cs >= NUM_CS) + return -EINVAL; + + if (aemif->cfg->num_cs >= NUM_CS) + return -EINVAL; + + data = &aemif->cfg->cs_data[aemif->cfg->num_cs++]; + data->cs = cs; + + /* read the current value in the hw register */ + davinci_aemif_get_hw_params(cs - 2, data); + + /* override the values from device node */ + val = dv_get_value(np, "davinci-cs-ta"); + if (val >= 0) + data->ta = val; + val = dv_get_value(np, "davinci-cs-rhold"); + if (val >= 0) + data->rhold = val; + val = dv_get_value(np, "davinci-cs-rstrobe"); + if (val >= 0) + data->rstrobe = val; + val = dv_get_value(np, "davinci-cs-rsetup"); + if (val >= 0) + data->rsetup = val; + val = dv_get_value(np, "davinci-cs-whold"); + if (val >= 0) + data->whold = val; + val = dv_get_value(np, "davinci-cs-wstrobe"); + if (val >= 0) + data->wstrobe = val; + val = dv_get_value(np, "davinci-cs-wsetup"); + if (val >= 0) + data->wsetup = val; + val = dv_get_value(np, "davinci-cs-asize"); + if (val >= 0) + data->asize = val; + val = dv_get_value(np, "davinci-cs-ew"); + if (val >= 0) + data->enable_ew = val; + val = dv_get_value(np, "davinci-cs-ss"); + if (val >= 0) + data->enable_ss = val; + return 0; +} +#endif + +static const struct of_device_id davinci_aemif_of_match[] __devinitconst = { + { .compatible = "ti,davinci-aemif", }, + {}, +}; + +static const struct of_device_id davinci_cs_of_match[] __devinitconst = { + { .compatible = "ti,davinci-cs", }, + {}, +}; + +/** + * of_davinci_aemif_cs_init - init cs data based on cs device nodes + * @np: device node ptr + * + * For every controller device node, there is a cs device node that + * describe the bus configuration parameters. This functions iterate + * over these nodes and update the cs data array. + */ +static int of_davinci_aemif_cs_init(struct device_node *aemif_np) +{ + struct device_node *np = aemif_np; + + /* cs nodes are optional. So just return success */ + if (!np) + return 0; + + for_each_matching_node(np, davinci_cs_of_match) + of_davinci_aemif_parse_abus_config(np); + return 0; +} + +static int __devinit davinci_aemif_probe(struct platform_device *pdev) +{ + struct davinci_aemif_pdata *cfg; + int ret = -ENODEV, i; + struct resource *res; + + aemif = devm_kzalloc(&pdev->dev, sizeof(*aemif), GFP_KERNEL); + + if (!aemif) + return -ENOMEM; + + aemif->clk = clk_get(NULL, "aemif"); + if (IS_ERR(aemif->clk)) + return PTR_ERR(aemif->clk); + + clk_prepare_enable(aemif->clk); + aemif->clk_rate = clk_get_rate(aemif->clk) / 1000; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("No IO memory address defined\n"); + goto error; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + aemif->base = devm_request_and_ioremap(&pdev->dev, res); + if (!aemif->base) { + ret = -EBUSY; + pr_err("ioremap failed\n"); + goto error; + } + + if (pdev->dev.platform_data == NULL) { + /* Not platform data, we get the cs data from the cs nodes */ + cfg = devm_kzalloc(&pdev->dev, sizeof(*cfg), GFP_KERNEL); + if (cfg == NULL) + return -ENOMEM; + + aemif->cfg = cfg; + if (of_davinci_aemif_cs_init(pdev->dev.of_node) < 0) { + pr_err("No platform data or cs of node present\n"); + goto error; + } + } else { + cfg = pdev->dev.platform_data; + aemif->cfg = cfg; + } + + for (i = 0; i < cfg->num_cs; i++) { + /* cs is from 2-5. Internally we use cs-2 to access ACR */ + ret = davinci_aemif_config_abus(cfg->cs_data[i].cs - 2, + aemif->base, &cfg->cs_data[i]); + if (ret < 0) { + pr_err("Error configuring chip select %d\n", + cfg->cs_data[i].cs); + goto error; + } + } + return 0; +error: + clk_disable_unprepare(aemif->clk); + clk_put(aemif->clk); + return ret; +} + +static struct platform_driver davinci_aemif_driver = { + .probe = davinci_aemif_probe, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = davinci_aemif_of_match, + }, +}; + +static int __init davinci_aemif_init(void) +{ + return platform_driver_register(&davinci_aemif_driver); +} +subsys_initcall(davinci_aemif_init); + +static void __exit davinci_aemif_exit(void) +{ + clk_disable_unprepare(aemif->clk); + clk_put(aemif->clk); + platform_driver_unregister(&davinci_aemif_driver); +} +module_exit(davinci_aemif_exit); + +MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>"); +MODULE_DESCRIPTION("Texas Instruments AEMIF driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/platform_data/davinci-aemif.h b/include/linux/platform_data/davinci-aemif.h new file mode 100644 index 0000000..03f3ad0 --- /dev/null +++ b/include/linux/platform_data/davinci-aemif.h @@ -0,0 +1,47 @@ +/* + * TI DaVinci AEMIF support + * + * Copyright 2010 (C) Texas Instruments, Inc. http://www.ti.com/ + * + * 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. + */ +#ifndef _MACH_DAVINCI_AEMIF_H +#define _MACH_DAVINCI_AEMIF_H + +#define NRCSR_OFFSET 0x00 +#define AWCCR_OFFSET 0x04 +#define A1CR_OFFSET 0x10 + +#define ACR_ASIZE_MASK 0x3 +#define ACR_EW_MASK BIT(30) +#define ACR_SS_MASK BIT(31) +#define ASIZE_16BIT 1 + +struct davinci_aemif_cs_data { + u8 cs; + u16 wstrobe; + u16 rstrobe; + u8 wsetup; + u8 whold; + u8 rsetup; + u8 rhold; + u8 ta; + u8 enable_ss; + u8 enable_ew; + u8 asize; +}; + +struct davinci_aemif_pdata { + u8 num_cs; + struct davinci_aemif_cs_data cs_data[4]; +}; + +/* API to Get current Asynchrnous emif bus parameters */ +struct davinci_aemif_cs_data *davinci_aemif_get_abus_params(unsigned int cs); + +/* API to Set current Asynchrnous emif bus parameters */ +int davinci_aemif_set_abus_params(unsigned int cs, + struct davinci_aemif_cs_data *data); +#endif
This is a platform driver for asynchronous external memory interface available on TI SoCs. This driver was previously located inside the mach-davinci folder. As this DaVinci IP is re-used across multiple family of devices such as c6x, keystone etc, the driver is moved to drivers. The driver configures async bus parameters associated with a particular chip select. For DaVinci NAND controller driver and driver for other async devices such as NOR flash, ASRAM etc, the bus confuguration is done by this driver at probe. A set of APIs (set/get) are provided to update the values based on specific driver usage. This supports configuration of the bus either through platform_data or through DT bindings. Signed-off-by: Murali Karicheri <m-karicheri2@ti.com> --- .../devicetree/bindings/memory/davinci-aemif.txt | 103 +++++ drivers/memory/Kconfig | 10 + drivers/memory/Makefile | 1 + drivers/memory/davinci-aemif.c | 479 ++++++++++++++++++++ include/linux/platform_data/davinci-aemif.h | 47 ++ 5 files changed, 640 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory/davinci-aemif.txt create mode 100644 drivers/memory/davinci-aemif.c create mode 100644 include/linux/platform_data/davinci-aemif.h