Drivers for 4.8:

- Add a driver for the EBI
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCgAGBQJXW0oNAAoJENiigzvaE+LCzBIP/1Cx7rGEELldxsy9pG++aNF1
 DpDjBrrG7qkuMRv7VJe2oIHcQUzZe1OOkRrDVKcngfe2QojfCgN1XLWZiCvvGpAM
 fCJGwj6aVQNOyCx+uxAxaqAYA+PmCPYJYpiDTVr7aJj1tXYp3RCdnrucIkrPstpb
 qPik+owFwJdHYWwprqKP2V5c+ShiCminKjJFei37hpbgEJYYNwCfEKqQOi5MDudA
 dfnfbg4KRFaEWfplID35tloTCIk2LWSSbQuW7+jp2Pd6ISq8qj++YMqUwRTkR4kl
 /2fwvkWYUsvc1eNok6W0j5sylO/I9Rp5EhAGE8wpWuSZOjQZ/2IAI83bZLdbyX4D
 GdrmelwKCHcag9ENTI961wS+nY6IyZhOu8kRYHoYgTb5f+wqB7cA8L8we91rJ0gg
 DLv/KPE1MqnpC3ck0au2smqTwNHt93B9JEXU9A1krov1kGsIl6q4IOZX/WM27gLv
 2FYKG6VDSfCD+XnWcgfBvXOOgdhU71z1MqqluiWgH2jphxPkT5tKr4OVUmeUPfKO
 XUBXRIobeicOiqjbNav8SXoJ5ne5wQx5O8ktr+XJOfLPG8smdePCmT8zJrGPplV6
 x7yMMIe6u/ds15mDlpfi91AQoGtoEJyiqKhof3LXFPs60GRJwiANFWj0FuqWkHMe
 DuM/NlOWVsIUSDGaPfwN
 =bK4Z
 -----END PGP SIGNATURE-----

Merge tag 'at91-ab-4.8-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux into next/drivers

Drivers for 4.8:
 - Add a driver for the EBI

* tag 'at91-ab-4.8-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
  memory: atmel-ebi: add DT bindings documentation
  memory: add Atmel EBI (External Bus Interface) driver

Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2016-06-14 10:18:54 -07:00
commit 9c643eda00
4 changed files with 919 additions and 0 deletions

View File

@ -0,0 +1,136 @@
* Device tree bindings for Atmel EBI
The External Bus Interface (EBI) controller is a bus where you can connect
asynchronous (NAND, NOR, SRAM, ....) and synchronous memories (SDR/DDR SDRAMs).
The EBI provides a glue-less interface to asynchronous memories through the SMC
(Static Memory Controller).
Required properties:
- compatible: "atmel,at91sam9260-ebi"
"atmel,at91sam9261-ebi"
"atmel,at91sam9263-ebi0"
"atmel,at91sam9263-ebi1"
"atmel,at91sam9rl-ebi"
"atmel,at91sam9g45-ebi"
"atmel,at91sam9x5-ebi"
"atmel,sama5d3-ebi"
- reg: Contains offset/length value for EBI memory mapping.
This property might contain several entries if the EBI
memory range is not contiguous
- #address-cells: Must be 2.
The first cell encodes the CS.
The second cell encode the offset into the CS memory
range.
- #size-cells: Must be set to 1.
- ranges: Encodes CS to memory region association.
- clocks: Clock feeding the EBI controller.
See clock-bindings.txt
Children device nodes are representing device connected to the EBI bus.
Required device node properties:
- reg: Contains the chip-select id, the offset and the length
of the memory region requested by the device.
EBI bus configuration will be defined directly in the device subnode.
Optional EBI/SMC properties:
- atmel,smc-bus-width: width of the asynchronous device's data bus
8, 16 or 32.
Default to 8 when undefined.
- atmel,smc-byte-access-type "write" or "select" (see Atmel datasheet).
Default to "select" when undefined.
- atmel,smc-read-mode "nrd" or "ncs".
Default to "ncs" when undefined.
- atmel,smc-write-mode "nwe" or "ncs".
Default to "ncs" when undefined.
- atmel,smc-exnw-mode "disabled", "frozen" or "ready".
Default to "disabled" when undefined.
- atmel,smc-page-mode enable page mode if present. The provided value
defines the page size (supported values: 4, 8,
16 and 32).
- atmel,smc-tdf-mode: "normal" or "optimized". When set to
"optimized" the data float time is optimized
depending on the next device being accessed
(next device setup time is subtracted to the
current device data float time).
Default to "normal" when undefined.
If at least one atmel,smc- property is defined the following SMC timing
properties become mandatory. In the other hand, if none of the atmel,smc-
properties are specified, we assume that the EBI bus configuration will be
handled by the sub-device driver, and none of those properties should be
defined.
All the timings are expressed in nanoseconds (see Atmel datasheet for a full
description).
- atmel,smc-ncs-rd-setup-ns
- atmel,smc-nrd-setup-ns
- atmel,smc-ncs-wr-setup-ns
- atmel,smc-nwe-setup-ns
- atmel,smc-ncs-rd-pulse-ns
- atmel,smc-nrd-pulse-ns
- atmel,smc-ncs-wr-pulse-ns
- atmel,smc-nwe-pulse-ns
- atmel,smc-nwe-cycle-ns
- atmel,smc-nrd-cycle-ns
- atmel,smc-tdf-ns
Example:
ebi: ebi@10000000 {
compatible = "atmel,sama5d3-ebi";
#address-cells = <2>;
#size-cells = <1>;
atmel,smc = <&hsmc>;
atmel,matrix = <&matrix>;
reg = <0x10000000 0x10000000
0x40000000 0x30000000>;
ranges = <0x0 0x0 0x10000000 0x10000000
0x1 0x0 0x40000000 0x10000000
0x2 0x0 0x50000000 0x10000000
0x3 0x0 0x60000000 0x10000000>;
clocks = <&mck>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ebi_addr>;
nor: flash@0,0 {
compatible = "cfi-flash";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x0 0x0 0x1000000>;
bank-width = <2>;
atmel,smc-read-mode = "nrd";
atmel,smc-write-mode = "nwe";
atmel,smc-bus-width = <16>;
atmel,smc-ncs-rd-setup-ns = <0>;
atmel,smc-ncs-wr-setup-ns = <0>;
atmel,smc-nwe-setup-ns = <8>;
atmel,smc-nrd-setup-ns = <16>;
atmel,smc-ncs-rd-pulse-ns = <84>;
atmel,smc-ncs-wr-pulse-ns = <84>;
atmel,smc-nrd-pulse-ns = <76>;
atmel,smc-nwe-pulse-ns = <76>;
atmel,smc-nrd-cycle-ns = <107>;
atmel,smc-nwe-cycle-ns = <84>;
atmel,smc-tdf-ns = <16>;
};
};

View File

@ -25,6 +25,17 @@ config ATMEL_SDRAMC
Starting with the at91sam9g45, this controller supports SDR, DDR and
LP-DDR memories.
config ATMEL_EBI
bool "Atmel EBI driver"
default y
depends on ARCH_AT91 && OF
select MFD_SYSCON
help
Driver for Atmel 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, etc.
config TI_AEMIF
tristate "Texas Instruments AEMIF driver"
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o
endif
obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o

771
drivers/memory/atmel-ebi.c Normal file
View File

@ -0,0 +1,771 @@
/*
* EBI driver for Atmel chips
* inspired by the fsl weim bus driver
*
* Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.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.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/atmel-matrix.h>
#include <linux/mfd/syscon/atmel-smc.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
struct at91sam9_smc_timings {
u32 ncs_rd_setup_ns;
u32 nrd_setup_ns;
u32 ncs_wr_setup_ns;
u32 nwe_setup_ns;
u32 ncs_rd_pulse_ns;
u32 nrd_pulse_ns;
u32 ncs_wr_pulse_ns;
u32 nwe_pulse_ns;
u32 nrd_cycle_ns;
u32 nwe_cycle_ns;
u32 tdf_ns;
};
struct at91sam9_smc_generic_fields {
struct regmap_field *setup;
struct regmap_field *pulse;
struct regmap_field *cycle;
struct regmap_field *mode;
};
struct at91sam9_ebi_dev_config {
struct at91sam9_smc_timings timings;
u32 mode;
};
struct at91_ebi_dev_config {
int cs;
union {
struct at91sam9_ebi_dev_config sam9;
};
};
struct at91_ebi;
struct at91_ebi_dev {
struct list_head node;
struct at91_ebi *ebi;
u32 mode;
int numcs;
struct at91_ebi_dev_config configs[];
};
struct at91_ebi_caps {
unsigned int available_cs;
const struct reg_field *ebi_csa;
void (*get_config)(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf);
int (*xlate_config)(struct at91_ebi_dev *ebid,
struct device_node *configs_np,
struct at91_ebi_dev_config *conf);
int (*apply_config)(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf);
int (*init)(struct at91_ebi *ebi);
};
struct at91_ebi {
struct clk *clk;
struct regmap *smc;
struct regmap *matrix;
struct regmap_field *ebi_csa;
struct device *dev;
const struct at91_ebi_caps *caps;
struct list_head devs;
union {
struct at91sam9_smc_generic_fields sam9;
};
};
static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf)
{
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
struct at91sam9_ebi_dev_config *config = &conf->sam9;
struct at91sam9_smc_timings *timings = &config->timings;
unsigned int val;
regmap_fields_read(fields->mode, conf->cs, &val);
config->mode = val & ~AT91_SMC_TDF;
val = (val & AT91_SMC_TDF) >> 16;
timings->tdf_ns = clk_rate * val;
regmap_fields_read(fields->setup, conf->cs, &val);
timings->ncs_rd_setup_ns = (val >> 24) & 0x1f;
timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128;
timings->ncs_rd_setup_ns *= clk_rate;
timings->nrd_setup_ns = (val >> 16) & 0x1f;
timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128;
timings->nrd_setup_ns *= clk_rate;
timings->ncs_wr_setup_ns = (val >> 8) & 0x1f;
timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128;
timings->ncs_wr_setup_ns *= clk_rate;
timings->nwe_setup_ns = val & 0x1f;
timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128;
timings->nwe_setup_ns *= clk_rate;
regmap_fields_read(fields->pulse, conf->cs, &val);
timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f;
timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256;
timings->ncs_rd_pulse_ns *= clk_rate;
timings->nrd_pulse_ns = (val >> 16) & 0x3f;
timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256;
timings->nrd_pulse_ns *= clk_rate;
timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f;
timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256;
timings->ncs_wr_pulse_ns *= clk_rate;
timings->nwe_pulse_ns = val & 0x3f;
timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256;
timings->nwe_pulse_ns *= clk_rate;
regmap_fields_read(fields->cycle, conf->cs, &val);
timings->nrd_cycle_ns = (val >> 16) & 0x7f;
timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256;
timings->nrd_cycle_ns *= clk_rate;
timings->nwe_cycle_ns = val & 0x7f;
timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256;
timings->nwe_cycle_ns *= clk_rate;
}
static int at91_xlate_timing(struct device_node *np, const char *prop,
u32 *val, bool *required)
{
if (!of_property_read_u32(np, prop, val)) {
*required = true;
return 0;
}
if (*required)
return -EINVAL;
return 0;
}
static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid,
struct device_node *np,
struct at91sam9_smc_timings *timings,
bool *required)
{
int ret;
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns",
&timings->ncs_rd_setup_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns",
&timings->nrd_setup_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns",
&timings->ncs_wr_setup_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns",
&timings->nwe_setup_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns",
&timings->ncs_rd_pulse_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns",
&timings->nrd_pulse_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns",
&timings->ncs_wr_pulse_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns",
&timings->nwe_pulse_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns",
&timings->nwe_cycle_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns",
&timings->nrd_cycle_ns, required);
if (ret)
goto out;
ret = at91_xlate_timing(np, "atmel,smc-tdf-ns",
&timings->tdf_ns, required);
out:
if (ret)
dev_err(ebid->ebi->dev,
"missing or invalid timings definition in %s",
np->full_name);
return ret;
}
static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
struct device_node *np,
struct at91_ebi_dev_config *conf)
{
struct at91sam9_ebi_dev_config *config = &conf->sam9;
bool required = false;
const char *tmp_str;
u32 tmp;
int ret;
ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp);
if (!ret) {
switch (tmp) {
case 8:
config->mode |= AT91_SMC_DBW_8;
break;
case 16:
config->mode |= AT91_SMC_DBW_16;
break;
case 32:
config->mode |= AT91_SMC_DBW_32;
break;
default:
return -EINVAL;
}
required = true;
}
if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
config->mode |= AT91_SMC_TDFMODE_OPTIMIZED;
required = true;
}
tmp_str = NULL;
of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str);
if (tmp_str && !strcmp(tmp_str, "write")) {
config->mode |= AT91_SMC_BAT_WRITE;
required = true;
}
tmp_str = NULL;
of_property_read_string(np, "atmel,smc-read-mode", &tmp_str);
if (tmp_str && !strcmp(tmp_str, "nrd")) {
config->mode |= AT91_SMC_READMODE_NRD;
required = true;
}
tmp_str = NULL;
of_property_read_string(np, "atmel,smc-write-mode", &tmp_str);
if (tmp_str && !strcmp(tmp_str, "nwe")) {
config->mode |= AT91_SMC_WRITEMODE_NWE;
required = true;
}
tmp_str = NULL;
of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str);
if (tmp_str) {
if (!strcmp(tmp_str, "frozen"))
config->mode |= AT91_SMC_EXNWMODE_FROZEN;
else if (!strcmp(tmp_str, "ready"))
config->mode |= AT91_SMC_EXNWMODE_READY;
else if (strcmp(tmp_str, "disabled"))
return -EINVAL;
required = true;
}
ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp);
if (!ret) {
switch (tmp) {
case 4:
config->mode |= AT91_SMC_PS_4;
break;
case 8:
config->mode |= AT91_SMC_PS_8;
break;
case 16:
config->mode |= AT91_SMC_PS_16;
break;
case 32:
config->mode |= AT91_SMC_PS_32;
break;
default:
return -EINVAL;
}
config->mode |= AT91_SMC_PMEN;
required = true;
}
ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings,
&required);
if (ret)
return ret;
return required;
}
static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
struct at91_ebi_dev_config *conf)
{
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
struct at91sam9_ebi_dev_config *config = &conf->sam9;
struct at91sam9_smc_timings *timings = &config->timings;
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
u32 coded_val;
u32 val;
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
timings->ncs_rd_setup_ns);
val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val);
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
timings->nrd_setup_ns);
val |= AT91SAM9_SMC_NRDSETUP(coded_val);
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
timings->ncs_wr_setup_ns);
val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val);
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
timings->nwe_setup_ns);
val |= AT91SAM9_SMC_NWESETUP(coded_val);
regmap_fields_write(fields->setup, conf->cs, val);
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
timings->ncs_rd_pulse_ns);
val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val);
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
timings->nrd_pulse_ns);
val |= AT91SAM9_SMC_NRDPULSE(coded_val);
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
timings->ncs_wr_pulse_ns);
val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val);
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
timings->nwe_pulse_ns);
val |= AT91SAM9_SMC_NWEPULSE(coded_val);
regmap_fields_write(fields->pulse, conf->cs, val);
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
timings->nrd_cycle_ns);
val = AT91SAM9_SMC_NRDCYCLE(coded_val);
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
timings->nwe_cycle_ns);
val |= AT91SAM9_SMC_NWECYCLE(coded_val);
regmap_fields_write(fields->cycle, conf->cs, val);
val = DIV_ROUND_UP(timings->tdf_ns, clk_rate);
if (val > AT91_SMC_TDF_MAX)
val = AT91_SMC_TDF_MAX;
regmap_fields_write(fields->mode, conf->cs,
config->mode | AT91_SMC_TDF_(val));
return 0;
}
static int at91sam9_ebi_init(struct at91_ebi *ebi)
{
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
struct reg_field field = REG_FIELD(0, 0, 31);
field.id_size = fls(ebi->caps->available_cs);
field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ;
field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC);
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->setup))
return PTR_ERR(fields->setup);
field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC);
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->pulse))
return PTR_ERR(fields->pulse);
field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC);
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->cycle))
return PTR_ERR(fields->cycle);
field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->mode))
return PTR_ERR(fields->mode);
return 0;
}
static int sama5d3_ebi_init(struct at91_ebi *ebi)
{
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
struct reg_field field = REG_FIELD(0, 0, 31);
field.id_size = fls(ebi->caps->available_cs);
field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ;
field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC);
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->setup))
return PTR_ERR(fields->setup);
field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC);
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->pulse))
return PTR_ERR(fields->pulse);
field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC);
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->cycle))
return PTR_ERR(fields->cycle);
field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
if (IS_ERR(fields->mode))
return PTR_ERR(fields->mode);
return 0;
}
static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
int reg_cells)
{
const struct at91_ebi_caps *caps = ebi->caps;
struct at91_ebi_dev_config conf = { };
struct device *dev = ebi->dev;
struct at91_ebi_dev *ebid;
int ret, numcs = 0, i;
bool apply = false;
numcs = of_property_count_elems_of_size(np, "reg",
reg_cells * sizeof(u32));
if (numcs <= 0) {
dev_err(dev, "invalid reg property in %s\n", np->full_name);
return -EINVAL;
}
ebid = devm_kzalloc(ebi->dev,
sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
GFP_KERNEL);
if (!ebid)
return -ENOMEM;
ebid->ebi = ebi;
ret = caps->xlate_config(ebid, np, &conf);
if (ret < 0)
return ret;
else if (ret)
apply = true;
for (i = 0; i < numcs; i++) {
u32 cs;
ret = of_property_read_u32_index(np, "reg", i * reg_cells,
&cs);
if (ret)
return ret;
if (cs > AT91_MATRIX_EBI_NUM_CS ||
!(ebi->caps->available_cs & BIT(cs))) {
dev_err(dev, "invalid reg property in %s\n",
np->full_name);
return -EINVAL;
}
ebid->configs[i].cs = cs;
if (apply) {
conf.cs = cs;
ret = caps->apply_config(ebid, &conf);
if (ret)
return ret;
}
caps->get_config(ebid, &ebid->configs[i]);
/*
* Attach the EBI device to the generic SMC logic if at least
* one "atmel,smc-" property is present.
*/
if (ebi->ebi_csa && ret)
regmap_field_update_bits(ebi->ebi_csa,
BIT(cs), 0);
}
list_add_tail(&ebid->node, &ebi->devs);
return 0;
}
static const struct reg_field at91sam9260_ebi_csa =
REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9260_ebi_caps = {
.available_cs = 0xff,
.ebi_csa = &at91sam9260_ebi_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct reg_field at91sam9261_ebi_csa =
REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9261_ebi_caps = {
.available_cs = 0xff,
.ebi_csa = &at91sam9261_ebi_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct reg_field at91sam9263_ebi0_csa =
REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9263_ebi0_caps = {
.available_cs = 0x3f,
.ebi_csa = &at91sam9263_ebi0_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct reg_field at91sam9263_ebi1_csa =
REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9263_ebi1_caps = {
.available_cs = 0x7,
.ebi_csa = &at91sam9263_ebi1_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct reg_field at91sam9rl_ebi_csa =
REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9rl_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa = &at91sam9rl_ebi_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct reg_field at91sam9g45_ebi_csa =
REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0,
AT91_MATRIX_EBI_NUM_CS - 1);
static const struct at91_ebi_caps at91sam9g45_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa = &at91sam9g45_ebi_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct at91_ebi_caps at91sam9x5_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa = &at91sam9263_ebi0_csa,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = at91sam9_ebi_init,
};
static const struct at91_ebi_caps sama5d3_ebi_caps = {
.available_cs = 0xf,
.get_config = at91sam9_ebi_get_config,
.xlate_config = at91sam9_ebi_xslate_config,
.apply_config = at91sam9_ebi_apply_config,
.init = sama5d3_ebi_init,
};
static const struct of_device_id at91_ebi_id_table[] = {
{
.compatible = "atmel,at91sam9260-ebi",
.data = &at91sam9260_ebi_caps,
},
{
.compatible = "atmel,at91sam9261-ebi",
.data = &at91sam9261_ebi_caps,
},
{
.compatible = "atmel,at91sam9263-ebi0",
.data = &at91sam9263_ebi0_caps,
},
{
.compatible = "atmel,at91sam9263-ebi1",
.data = &at91sam9263_ebi1_caps,
},
{
.compatible = "atmel,at91sam9rl-ebi",
.data = &at91sam9rl_ebi_caps,
},
{
.compatible = "atmel,at91sam9g45-ebi",
.data = &at91sam9g45_ebi_caps,
},
{
.compatible = "atmel,at91sam9x5-ebi",
.data = &at91sam9x5_ebi_caps,
},
{
.compatible = "atmel,sama5d3-ebi",
.data = &sama5d3_ebi_caps,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_ebi_id_table);
static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np)
{
struct device *dev = ebi->dev;
struct property *newprop;
newprop = devm_kzalloc(dev, sizeof(*newprop), GFP_KERNEL);
if (!newprop)
return -ENOMEM;
newprop->name = devm_kstrdup(dev, "status", GFP_KERNEL);
if (!newprop->name)
return -ENOMEM;
newprop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL);
if (!newprop->name)
return -ENOMEM;
newprop->length = sizeof("disabled");
return of_update_property(np, newprop);
}
static int at91_ebi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
const struct of_device_id *match;
struct at91_ebi *ebi;
int ret, reg_cells;
struct clk *clk;
u32 val;
match = of_match_device(at91_ebi_id_table, dev);
if (!match || !match->data)
return -EINVAL;
ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
if (!ebi)
return -ENOMEM;
INIT_LIST_HEAD(&ebi->devs);
ebi->caps = match->data;
ebi->dev = dev;
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
ebi->clk = clk;
ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc");
if (IS_ERR(ebi->smc))
return PTR_ERR(ebi->smc);
/*
* The sama5d3 does not provide an EBICSA register and thus does need
* to access the matrix registers.
*/
if (ebi->caps->ebi_csa) {
ebi->matrix =
syscon_regmap_lookup_by_phandle(np, "atmel,matrix");
if (IS_ERR(ebi->matrix))
return PTR_ERR(ebi->matrix);
ebi->ebi_csa = regmap_field_alloc(ebi->matrix,
*ebi->caps->ebi_csa);
if (IS_ERR(ebi->ebi_csa))
return PTR_ERR(ebi->ebi_csa);
}
ret = ebi->caps->init(ebi);
if (ret)
return ret;
ret = of_property_read_u32(np, "#address-cells", &val);
if (ret) {
dev_err(dev, "missing #address-cells property\n");
return ret;
}
reg_cells = val;
ret = of_property_read_u32(np, "#size-cells", &val);
if (ret) {
dev_err(dev, "missing #address-cells property\n");
return ret;
}
reg_cells += val;
for_each_available_child_of_node(np, child) {
if (!of_find_property(child, "reg", NULL))
continue;
ret = at91_ebi_dev_setup(ebi, child, reg_cells);
if (ret) {
dev_err(dev, "failed to configure EBI bus for %s, disabling the device",
child->full_name);
ret = at91_ebi_dev_disable(ebi, child);
if (ret)
return ret;
}
}
return of_platform_populate(np, NULL, NULL, dev);
}
static struct platform_driver at91_ebi_driver = {
.driver = {
.name = "atmel-ebi",
.of_match_table = at91_ebi_id_table,
},
};
module_platform_driver_probe(at91_ebi_driver, at91_ebi_probe);
MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
MODULE_DESCRIPTION("Atmel EBI driver");
MODULE_LICENSE("GPL");