diff options
Diffstat (limited to 'drivers/memory')
-rw-r--r-- | drivers/memory/Kconfig | 11 | ||||
-rw-r--r-- | drivers/memory/Makefile | 1 | ||||
-rw-r--r-- | drivers/memory/atmel-ebi.c | 766 | ||||
-rw-r--r-- | drivers/memory/atmel-sdramc.c | 11 | ||||
-rw-r--r-- | drivers/memory/omap-gpmc.c | 136 | ||||
-rw-r--r-- | drivers/memory/samsung/exynos-srom.c | 41 | ||||
-rw-r--r-- | drivers/memory/tegra/mc.c | 10 | ||||
-rw-r--r-- | drivers/memory/tegra/tegra124-emc.c | 8 |
8 files changed, 867 insertions, 117 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 81ddb17575a9..133712346911 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -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 diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index cb0b7a1df11a..b20ae38b5bfb 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -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 diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c new file mode 100644 index 000000000000..f87ad6f5d2dc --- /dev/null +++ b/drivers/memory/atmel-ebi.c @@ -0,0 +1,766 @@ +/* + * 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/init.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 */ } +}; + +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, + }, +}; +builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c index a3ebc8a87479..53a341f3b305 100644 --- a/drivers/memory/atmel-sdramc.c +++ b/drivers/memory/atmel-sdramc.c @@ -1,6 +1,8 @@ /* * Atmel (Multi-port DDR-)SDRAM Controller driver * + * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com> + * * Copyright (C) 2014 Atmel * * This program is free software: you can redistribute it and/or modify @@ -20,7 +22,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -48,7 +50,6 @@ static const struct of_device_id atmel_ramc_of_match[] = { { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, }, {}, }; -MODULE_DEVICE_TABLE(of, atmel_ramc_of_match); static int atmel_ramc_probe(struct platform_device *pdev) { @@ -90,8 +91,4 @@ static int __init atmel_ramc_init(void) { return platform_driver_register(&atmel_ramc_driver); } -module_init(atmel_ramc_init); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); -MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller"); +device_initcall(atmel_ramc_init); diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 4721b591994f..869c83fb3c5d 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -20,7 +20,6 @@ #include <linux/ioport.h> #include <linux/spinlock.h> #include <linux/io.h> -#include <linux/module.h> #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> @@ -1807,7 +1806,6 @@ static const struct of_device_id gpmc_dt_ids[] = { { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ { } }; -MODULE_DEVICE_TABLE(of, gpmc_dt_ids); /** * gpmc_read_settings_dt - read gpmc settings from device-tree @@ -2154,68 +2152,6 @@ err: return ret; } -static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -{ - return 1; /* we're input only */ -} - -static int gpmc_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - return 0; /* we're input only */ -} - -static int gpmc_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - return -EINVAL; /* we're input only */ -} - -static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ -} - -static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - u32 reg; - - offset += 8; - - reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); - - return !!reg; -} - -static int gpmc_gpio_init(struct gpmc_device *gpmc) -{ - int ret; - - gpmc->gpio_chip.parent = gpmc->dev; - gpmc->gpio_chip.owner = THIS_MODULE; - gpmc->gpio_chip.label = DEVICE_NAME; - gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; - gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; - gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; - gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; - gpmc->gpio_chip.set = gpmc_gpio_set; - gpmc->gpio_chip.get = gpmc_gpio_get; - gpmc->gpio_chip.base = -1; - - ret = gpiochip_add(&gpmc->gpio_chip); - if (ret < 0) { - dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); - return ret; - } - - return 0; -} - -static void gpmc_gpio_exit(struct gpmc_device *gpmc) -{ - gpiochip_remove(&gpmc->gpio_chip); -} - static int gpmc_probe_dt(struct platform_device *pdev) { int ret; @@ -2280,7 +2216,69 @@ static int gpmc_probe_dt_children(struct platform_device *pdev) { return 0; } -#endif +#endif /* CONFIG_OF */ + +static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + return 1; /* we're input only */ +} + +static int gpmc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; /* we're input only */ +} + +static int gpmc_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + return -EINVAL; /* we're input only */ +} + +static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ +} + +static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + u32 reg; + + offset += 8; + + reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); + + return !!reg; +} + +static int gpmc_gpio_init(struct gpmc_device *gpmc) +{ + int ret; + + gpmc->gpio_chip.parent = gpmc->dev; + gpmc->gpio_chip.owner = THIS_MODULE; + gpmc->gpio_chip.label = DEVICE_NAME; + gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; + gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; + gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; + gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; + gpmc->gpio_chip.set = gpmc_gpio_set; + gpmc->gpio_chip.get = gpmc_gpio_get; + gpmc->gpio_chip.base = -1; + + ret = gpiochip_add(&gpmc->gpio_chip); + if (ret < 0) { + dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); + return ret; + } + + return 0; +} + +static void gpmc_gpio_exit(struct gpmc_device *gpmc) +{ + gpiochip_remove(&gpmc->gpio_chip); +} static int gpmc_probe(struct platform_device *pdev) { @@ -2436,15 +2434,7 @@ static __init int gpmc_init(void) { return platform_driver_register(&gpmc_driver); } - -static __exit void gpmc_exit(void) -{ - platform_driver_unregister(&gpmc_driver); - -} - postcore_initcall(gpmc_init); -module_exit(gpmc_exit); static struct omap3_gpmc_regs gpmc_context; diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 96756fb4d6bd..bf827a666694 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -11,7 +11,7 @@ */ #include <linux/io.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -91,17 +91,17 @@ static int exynos_srom_configure_bank(struct exynos_srom *srom, if (width == 2) cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; - bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW); + bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW); bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); - __raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW); + writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW); - __raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | - (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | - (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | - (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | - (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | - (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), - srom->reg_base + EXYNOS_SROM_BC0 + bank); + writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | + (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | + (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | + (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | + (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | + (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), + srom->reg_base + EXYNOS_SROM_BC0 + bank); return 0; } @@ -134,7 +134,7 @@ static int exynos_srom_probe(struct platform_device *pdev) platform_set_drvdata(pdev, srom); srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, - sizeof(exynos_srom_offsets)); + ARRAY_SIZE(exynos_srom_offsets)); if (!srom->reg_offset) { iounmap(srom->reg_base); return -ENOMEM; @@ -159,16 +159,6 @@ static int exynos_srom_probe(struct platform_device *pdev) return of_platform_populate(np, NULL, NULL, dev); } -static int exynos_srom_remove(struct platform_device *pdev) -{ - struct exynos_srom *srom = platform_get_drvdata(pdev); - - kfree(srom->reg_offset); - iounmap(srom->reg_base); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static void exynos_srom_save(void __iomem *base, struct exynos_srom_reg_dump *rd, @@ -211,21 +201,16 @@ static const struct of_device_id of_exynos_srom_ids[] = { }, {}, }; -MODULE_DEVICE_TABLE(of, of_exynos_srom_ids); static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); static struct platform_driver exynos_srom_driver = { .probe = exynos_srom_probe, - .remove = exynos_srom_remove, .driver = { .name = "exynos-srom", .of_match_table = of_exynos_srom_ids, .pm = &exynos_srom_pm_ops, + .suppress_bind_attrs = true, }, }; -module_platform_driver(exynos_srom_driver); - -MODULE_AUTHOR("Pankaj Dubey <pankaj.dubey@samsung.com>"); -MODULE_DESCRIPTION("Exynos SROM Controller Driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(exynos_srom_driver); diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a1ae0cc2b86d..a4803ac192bb 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -186,8 +186,10 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node) timing = &mc->timings[i++]; err = load_one_timing(mc, timing, child); - if (err) + if (err) { + of_node_put(child); return err; + } } return 0; @@ -206,15 +208,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc) for_each_child_of_node(mc->dev->of_node, node) { err = of_property_read_u32(node, "nvidia,ram-code", &node_ram_code); - if (err || (node_ram_code != ram_code)) { - of_node_put(node); + if (err || (node_ram_code != ram_code)) continue; - } err = load_timings(mc, node); + of_node_put(node); if (err) return err; - of_node_put(node); break; } diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 3dac7be39654..06cc781ebac1 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -970,8 +970,10 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, timing = &emc->timings[i++]; err = load_one_timing_from_dt(emc, timing, child); - if (err) + if (err) { + of_node_put(child); return err; + } } sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, @@ -995,10 +997,8 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code) u32 value; err = of_property_read_u32(np, "nvidia,ram-code", &value); - if (err || (value != ram_code)) { - of_node_put(np); + if (err || (value != ram_code)) continue; - } return np; } |