Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+152
View File
@@ -0,0 +1,152 @@
#
# Memory devices
#
menuconfig MEMORY
bool "Memory Controller drivers"
if MEMORY
config ARM_PL172_MPMC
tristate "ARM PL172 MPMC driver"
depends on ARM_AMBA && OF
help
This selects the ARM PrimeCell PL172 MultiPort Memory Controller.
If you have an embedded system with an AMBA bus and a PL172
controller, say Y or M here.
config ATMEL_SDRAMC
bool "Atmel (Multi-port DDR-)SDRAM Controller"
default y
depends on ARCH_AT91 && OF
help
This driver is for Atmel SDRAM Controller or Atmel Multi-port
DDR-SDRAM Controller available on Atmel AT91SAM9 and SAMA5 SoCs.
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
select MFD_ATMEL_SMC
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
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.
config TI_EMIF
tristate "Texas Instruments EMIF driver"
depends on ARCH_OMAP2PLUS
select DDR
help
This driver is for the EMIF module available in Texas Instruments
SoCs. EMIF is an SDRAM controller that, based on its revision,
supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
This driver takes care of only LPDDR2 memories presently. The
functions of the driver includes re-configuring AC timing
parameters and other settings during frequency, voltage and
temperature changes
config OMAP_GPMC
bool
select GPIOLIB
help
This driver is for the General Purpose Memory Controller (GPMC)
present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
interfacing to a variety of asynchronous as well as synchronous
memory drives like NOR, NAND, OneNAND, SRAM.
config OMAP_GPMC_DEBUG
bool "Enable GPMC debug output and skip reset of GPMC during init"
depends on OMAP_GPMC
help
Enables verbose debugging mostly to decode the bootloader provided
timings. To preserve the bootloader provided timings, the reset
of GPMC is skipped during init. Enable this during development to
configure devices connected to the GPMC bus.
NOTE: In addition to matching the register setup with the bootloader
you also need to match the GPMC FCLK frequency used by the
bootloader or else the GPMC timings won't be identical with the
bootloader timings.
config TI_EMIF_SRAM
tristate "Texas Instruments EMIF SRAM driver"
depends on (SOC_AM33XX || SOC_AM43XX) && SRAM
help
This driver is for the EMIF module available on Texas Instruments
AM33XX and AM43XX SoCs and is required for PM. Certain parts of
the EMIF PM code must run from on-chip SRAM late in the suspend
sequence so this driver provides several relocatable PM functions
for the SoC PM code to use.
config MVEBU_DEVBUS
bool "Marvell EBU Device Bus Controller"
default y
depends on PLAT_ORION && OF
help
This driver is for the Device Bus controller available in some
Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and
Armada 370 and Armada XP. This controller allows to handle flash
devices such as NOR, NAND, SRAM, and FPGA.
config FSL_CORENET_CF
tristate "Freescale CoreNet Error Reporting"
depends on FSL_SOC_BOOKE
help
Say Y for reporting of errors from the Freescale CoreNet
Coherency Fabric. Errors reported include accesses to
physical addresses that mapped by no local access window
(LAW) or an invalid LAW, as well as bad cache state that
represents a coherency violation.
config FSL_IFC
bool
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
depends on HAS_IOMEM
config JZ4780_NEMC
bool "Ingenic JZ4780 SoC NEMC driver"
default y
depends on MACH_JZ4780 || COMPILE_TEST
depends on HAS_IOMEM && OF
help
This driver is for the NAND/External Memory Controller (NEMC) in
the Ingenic JZ4780. This controller is used to handle external
memory devices such as NAND and SRAM.
config MTK_SMI
bool "Support Memory Control for Multimedia"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
This driver is for the Memory Controller module in MediaTek SoCs,
mainly help enable/disable iommu and control the power domain and
clocks for each local arbiter.
config DA8XX_DDRCTL
bool "Texas Instruments da8xx DDR2/mDDR driver"
depends on ARCH_DAVINCI_DA8XX
help
This driver is for the DDR2/mDDR Memory Controller present on
Texas Instruments da8xx SoCs. It's used to tweak various memory
controller configuration options.
source "drivers/memory/samsung/Kconfig"
source "drivers/memory/tegra/Kconfig"
source "drivers/memory/mediatek/Kconfig"
endif
+33
View File
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for memory devices
#
ifeq ($(CONFIG_DDR),y)
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_ARCH_BRCMSTB) += brcmstb_dpfe.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o
obj-$(CONFIG_MTK_SMI) += mtk-smi.o
obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
obj-$(CONFIG_HAVE_MTK_MC) += mediatek/
obj-$(CONFIG_MEDIATEK_MC) += mediatek/
obj-$(CONFIG_SAMSUNG_MC) += samsung/
obj-$(CONFIG_TEGRA_MC) += tegra/
obj-$(CONFIG_TI_EMIF_SRAM) += ti-emif-sram.o
ti-emif-sram-objs := ti-emif-pm.o ti-emif-sram-pm.o
AFLAGS_ti-emif-sram-pm.o :=-Wa,-march=armv7-a
include drivers/memory/Makefile.asm-offsets
drivers/memory/ti-emif-sram-pm.o: include/generated/ti-emif-asm-offsets.h
+5
View File
@@ -0,0 +1,5 @@
drivers/memory/emif-asm-offsets.s: drivers/memory/emif-asm-offsets.c
$(call if_changed_dep,cc_s_c)
include/generated/ti-emif-asm-offsets.h: drivers/memory/emif-asm-offsets.s FORCE
$(call filechk,offsets,__TI_EMIF_ASM_OFFSETS_H__)
+614
View File
@@ -0,0 +1,614 @@
/*
* 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 atmel_ebi_dev_config {
int cs;
struct atmel_smc_cs_conf smcconf;
};
struct atmel_ebi;
struct atmel_ebi_dev {
struct list_head node;
struct atmel_ebi *ebi;
u32 mode;
int numcs;
struct atmel_ebi_dev_config configs[];
};
struct atmel_ebi_caps {
unsigned int available_cs;
unsigned int ebi_csa_offs;
void (*get_config)(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf);
int (*xlate_config)(struct atmel_ebi_dev *ebid,
struct device_node *configs_np,
struct atmel_ebi_dev_config *conf);
void (*apply_config)(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf);
};
struct atmel_ebi {
struct clk *clk;
struct regmap *matrix;
struct {
struct regmap *regmap;
struct clk *clk;
const struct atmel_hsmc_reg_layout *layout;
} smc;
struct device *dev;
const struct atmel_ebi_caps *caps;
struct list_head devs;
};
struct atmel_smc_timing_xlate {
const char *name;
int (*converter)(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int nycles);
unsigned int shift;
};
#define ATMEL_SMC_SETUP_XLATE(nm, pos) \
{ .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos}
#define ATMEL_SMC_PULSE_XLATE(nm, pos) \
{ .name = nm, .converter = atmel_smc_cs_conf_set_pulse, .shift = pos}
#define ATMEL_SMC_CYCLE_XLATE(nm, pos) \
{ .name = nm, .converter = atmel_smc_cs_conf_set_cycle, .shift = pos}
static void at91sam9_ebi_get_config(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf)
{
atmel_smc_cs_conf_get(ebid->ebi->smc.regmap, conf->cs,
&conf->smcconf);
}
static void sama5_ebi_get_config(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf)
{
atmel_hsmc_cs_conf_get(ebid->ebi->smc.regmap, ebid->ebi->smc.layout,
conf->cs, &conf->smcconf);
}
static const struct atmel_smc_timing_xlate timings_xlate_table[] = {
ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-rd-setup-ns",
ATMEL_SMC_NCS_RD_SHIFT),
ATMEL_SMC_SETUP_XLATE("atmel,smc-ncs-wr-setup-ns",
ATMEL_SMC_NCS_WR_SHIFT),
ATMEL_SMC_SETUP_XLATE("atmel,smc-nrd-setup-ns", ATMEL_SMC_NRD_SHIFT),
ATMEL_SMC_SETUP_XLATE("atmel,smc-nwe-setup-ns", ATMEL_SMC_NWE_SHIFT),
ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-rd-pulse-ns",
ATMEL_SMC_NCS_RD_SHIFT),
ATMEL_SMC_PULSE_XLATE("atmel,smc-ncs-wr-pulse-ns",
ATMEL_SMC_NCS_WR_SHIFT),
ATMEL_SMC_PULSE_XLATE("atmel,smc-nrd-pulse-ns", ATMEL_SMC_NRD_SHIFT),
ATMEL_SMC_PULSE_XLATE("atmel,smc-nwe-pulse-ns", ATMEL_SMC_NWE_SHIFT),
ATMEL_SMC_CYCLE_XLATE("atmel,smc-nrd-cycle-ns", ATMEL_SMC_NRD_SHIFT),
ATMEL_SMC_CYCLE_XLATE("atmel,smc-nwe-cycle-ns", ATMEL_SMC_NWE_SHIFT),
};
static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid,
struct device_node *np,
struct atmel_smc_cs_conf *smcconf)
{
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
unsigned int clk_period_ns = NSEC_PER_SEC / clk_rate;
bool required = false;
unsigned int ncycles;
int ret, i;
u32 val;
ret = of_property_read_u32(np, "atmel,smc-tdf-ns", &val);
if (!ret) {
required = true;
ncycles = DIV_ROUND_UP(val, clk_period_ns);
if (ncycles > ATMEL_SMC_MODE_TDF_MAX) {
ret = -EINVAL;
goto out;
}
if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
ncycles = ATMEL_SMC_MODE_TDF_MIN;
smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles);
}
for (i = 0; i < ARRAY_SIZE(timings_xlate_table); i++) {
const struct atmel_smc_timing_xlate *xlate;
xlate = &timings_xlate_table[i];
ret = of_property_read_u32(np, xlate->name, &val);
if (ret) {
if (!required)
continue;
else
break;
}
if (!required) {
ret = -EINVAL;
break;
}
ncycles = DIV_ROUND_UP(val, clk_period_ns);
ret = xlate->converter(smcconf, xlate->shift, ncycles);
if (ret)
goto out;
}
out:
if (ret) {
dev_err(ebid->ebi->dev,
"missing or invalid timings definition in %pOF",
np);
return ret;
}
return required;
}
static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid,
struct device_node *np,
struct atmel_ebi_dev_config *conf)
{
struct atmel_smc_cs_conf *smcconf = &conf->smcconf;
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:
smcconf->mode |= ATMEL_SMC_MODE_DBW_8;
break;
case 16:
smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
break;
case 32:
smcconf->mode |= ATMEL_SMC_MODE_DBW_32;
break;
default:
return -EINVAL;
}
required = true;
}
if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
smcconf->mode |= ATMEL_SMC_MODE_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")) {
smcconf->mode |= ATMEL_SMC_MODE_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")) {
smcconf->mode |= ATMEL_SMC_MODE_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")) {
smcconf->mode |= ATMEL_SMC_MODE_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"))
smcconf->mode |= ATMEL_SMC_MODE_EXNWMODE_FROZEN;
else if (!strcmp(tmp_str, "ready"))
smcconf->mode |= ATMEL_SMC_MODE_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:
smcconf->mode |= ATMEL_SMC_MODE_PS_4;
break;
case 8:
smcconf->mode |= ATMEL_SMC_MODE_PS_8;
break;
case 16:
smcconf->mode |= ATMEL_SMC_MODE_PS_16;
break;
case 32:
smcconf->mode |= ATMEL_SMC_MODE_PS_32;
break;
default:
return -EINVAL;
}
smcconf->mode |= ATMEL_SMC_MODE_PMEN;
required = true;
}
ret = atmel_ebi_xslate_smc_timings(ebid, np, &conf->smcconf);
if (ret < 0)
return -EINVAL;
if ((ret > 0 && !required) || (!ret && required)) {
dev_err(ebid->ebi->dev, "missing atmel,smc- properties in %pOF",
np);
return -EINVAL;
}
return required;
}
static void at91sam9_ebi_apply_config(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf)
{
atmel_smc_cs_conf_apply(ebid->ebi->smc.regmap, conf->cs,
&conf->smcconf);
}
static void sama5_ebi_apply_config(struct atmel_ebi_dev *ebid,
struct atmel_ebi_dev_config *conf)
{
atmel_hsmc_cs_conf_apply(ebid->ebi->smc.regmap, ebid->ebi->smc.layout,
conf->cs, &conf->smcconf);
}
static int atmel_ebi_dev_setup(struct atmel_ebi *ebi, struct device_node *np,
int reg_cells)
{
const struct atmel_ebi_caps *caps = ebi->caps;
struct atmel_ebi_dev_config conf = { };
struct device *dev = ebi->dev;
struct atmel_ebi_dev *ebid;
unsigned long cslines = 0;
int ret, numcs = 0, nentries, i;
bool apply = false;
u32 cs;
nentries = of_property_count_elems_of_size(np, "reg",
reg_cells * sizeof(u32));
for (i = 0; i < nentries; i++) {
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 %pOF\n", np);
return -EINVAL;
}
if (!test_and_set_bit(cs, &cslines))
numcs++;
}
if (!numcs) {
dev_err(dev, "invalid reg property in %pOF\n", np);
return -EINVAL;
}
ebid = devm_kzalloc(ebi->dev,
sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
GFP_KERNEL);
if (!ebid)
return -ENOMEM;
ebid->ebi = ebi;
ebid->numcs = numcs;
ret = caps->xlate_config(ebid, np, &conf);
if (ret < 0)
return ret;
else if (ret)
apply = true;
i = 0;
for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) {
ebid->configs[i].cs = cs;
if (apply) {
conf.cs = cs;
caps->apply_config(ebid, &conf);
}
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->caps->ebi_csa_offs && apply)
regmap_update_bits(ebi->matrix,
ebi->caps->ebi_csa_offs,
BIT(cs), 0);
i++;
}
list_add_tail(&ebid->node, &ebi->devs);
return 0;
}
static const struct atmel_ebi_caps at91sam9260_ebi_caps = {
.available_cs = 0xff,
.ebi_csa_offs = AT91SAM9260_MATRIX_EBICSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9261_ebi_caps = {
.available_cs = 0xff,
.ebi_csa_offs = AT91SAM9261_MATRIX_EBICSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9263_ebi0_caps = {
.available_cs = 0x3f,
.ebi_csa_offs = AT91SAM9263_MATRIX_EBI0CSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9263_ebi1_caps = {
.available_cs = 0x7,
.ebi_csa_offs = AT91SAM9263_MATRIX_EBI1CSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9rl_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa_offs = AT91SAM9RL_MATRIX_EBICSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9g45_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa_offs = AT91SAM9G45_MATRIX_EBICSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps at91sam9x5_ebi_caps = {
.available_cs = 0x3f,
.ebi_csa_offs = AT91SAM9X5_MATRIX_EBICSA,
.get_config = at91sam9_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = at91sam9_ebi_apply_config,
};
static const struct atmel_ebi_caps sama5d3_ebi_caps = {
.available_cs = 0xf,
.get_config = sama5_ebi_get_config,
.xlate_config = atmel_ebi_xslate_smc_config,
.apply_config = sama5_ebi_apply_config,
};
static const struct of_device_id atmel_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 atmel_ebi_dev_disable(struct atmel_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->value)
return -ENOMEM;
newprop->length = sizeof("disabled");
return of_update_property(np, newprop);
}
static int atmel_ebi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node, *smc_np;
const struct of_device_id *match;
struct atmel_ebi *ebi;
int ret, reg_cells;
struct clk *clk;
u32 val;
match = of_match_device(atmel_ebi_id_table, dev);
if (!match || !match->data)
return -EINVAL;
ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
if (!ebi)
return -ENOMEM;
platform_set_drvdata(pdev, ebi);
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;
smc_np = of_parse_phandle(dev->of_node, "atmel,smc", 0);
ebi->smc.regmap = syscon_node_to_regmap(smc_np);
if (IS_ERR(ebi->smc.regmap))
return PTR_ERR(ebi->smc.regmap);
ebi->smc.layout = atmel_hsmc_get_reg_layout(smc_np);
if (IS_ERR(ebi->smc.layout))
return PTR_ERR(ebi->smc.layout);
ebi->smc.clk = of_clk_get(smc_np, 0);
if (IS_ERR(ebi->smc.clk)) {
if (PTR_ERR(ebi->smc.clk) != -ENOENT)
return PTR_ERR(ebi->smc.clk);
ebi->smc.clk = NULL;
}
ret = clk_prepare_enable(ebi->smc.clk);
if (ret)
return ret;
/*
* The sama5d3 does not provide an EBICSA register and thus does need
* to access the matrix registers.
*/
if (ebi->caps->ebi_csa_offs) {
ebi->matrix =
syscon_regmap_lookup_by_phandle(np, "atmel,matrix");
if (IS_ERR(ebi->matrix))
return PTR_ERR(ebi->matrix);
}
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 = atmel_ebi_dev_setup(ebi, child, reg_cells);
if (ret) {
dev_err(dev, "failed to configure EBI bus for %pOF, disabling the device",
child);
ret = atmel_ebi_dev_disable(ebi, child);
if (ret)
return ret;
}
}
return of_platform_populate(np, NULL, NULL, dev);
}
static __maybe_unused int atmel_ebi_resume(struct device *dev)
{
struct atmel_ebi *ebi = dev_get_drvdata(dev);
struct atmel_ebi_dev *ebid;
list_for_each_entry(ebid, &ebi->devs, node) {
int i;
for (i = 0; i < ebid->numcs; i++)
ebid->ebi->caps->apply_config(ebid, &ebid->configs[i]);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume);
static struct platform_driver atmel_ebi_driver = {
.driver = {
.name = "atmel-ebi",
.of_match_table = atmel_ebi_id_table,
.pm = &atmel_ebi_pm_ops,
},
};
builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe);
+88
View File
@@ -0,0 +1,88 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
struct at91_ramc_caps {
bool has_ddrck;
bool has_mpddr_clk;
};
static const struct at91_ramc_caps at91rm9200_caps = { };
static const struct at91_ramc_caps at91sam9g45_caps = {
.has_ddrck = 1,
.has_mpddr_clk = 0,
};
static const struct at91_ramc_caps sama5d3_caps = {
.has_ddrck = 1,
.has_mpddr_clk = 1,
};
static const struct of_device_id atmel_ramc_of_match[] = {
{ .compatible = "atmel,at91rm9200-sdramc", .data = &at91rm9200_caps, },
{ .compatible = "atmel,at91sam9260-sdramc", .data = &at91rm9200_caps, },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = &at91sam9g45_caps, },
{ .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
{},
};
static int atmel_ramc_probe(struct platform_device *pdev)
{
const struct at91_ramc_caps *caps;
struct clk *clk;
caps = of_device_get_match_data(&pdev->dev);
if (caps->has_ddrck) {
clk = devm_clk_get(&pdev->dev, "ddrck");
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_prepare_enable(clk);
}
if (caps->has_mpddr_clk) {
clk = devm_clk_get(&pdev->dev, "mpddr");
if (IS_ERR(clk)) {
pr_err("AT91 RAMC: couldn't get mpddr clock\n");
return PTR_ERR(clk);
}
clk_prepare_enable(clk);
}
return 0;
}
static struct platform_driver atmel_ramc_driver = {
.probe = atmel_ramc_probe,
.driver = {
.name = "atmel-ramc",
.of_match_table = atmel_ramc_of_match,
},
};
builtin_platform_driver(atmel_ramc_driver);
+746
View File
@@ -0,0 +1,746 @@
/*
* DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
*
* Copyright (c) 2017 Broadcom
*
* Released under the GPLv2 only.
* SPDX-License-Identifier: GPL-2.0
*/
/*
* This driver provides access to the DPFE interface of Broadcom STB SoCs.
* The firmware running on the DCPU inside the DDR PHY can provide current
* information about the system's RAM, for instance the DRAM refresh rate.
* This can be used as an indirect indicator for the DRAM's temperature.
* Slower refresh rate means cooler RAM, higher refresh rate means hotter
* RAM.
*
* Throughout the driver, we use readl_relaxed() and writel_relaxed(), which
* already contain the appropriate le32_to_cpu()/cpu_to_le32() calls.
*
* Note regarding the loading of the firmware image: we use be32_to_cpu()
* and le_32_to_cpu(), so we can support the following four cases:
* - LE kernel + LE firmware image (the most common case)
* - LE kernel + BE firmware image
* - BE kernel + LE firmware image
* - BE kernel + BE firmware image
*
* The DPCU always runs in big endian mode. The firwmare image, however, can
* be in either format. Also, communication between host CPU and DCPU is
* always in little endian.
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#define DRVNAME "brcmstb-dpfe"
#define FIRMWARE_NAME "dpfe.bin"
/* DCPU register offsets */
#define REG_DCPU_RESET 0x0
#define REG_TO_DCPU_MBOX 0x10
#define REG_TO_HOST_MBOX 0x14
/* Macros to process offsets returned by the DCPU */
#define DRAM_MSG_ADDR_OFFSET 0x0
#define DRAM_MSG_TYPE_OFFSET 0x1c
#define DRAM_MSG_ADDR_MASK ((1UL << DRAM_MSG_TYPE_OFFSET) - 1)
#define DRAM_MSG_TYPE_MASK ((1UL << \
(BITS_PER_LONG - DRAM_MSG_TYPE_OFFSET)) - 1)
/* Message RAM */
#define DCPU_MSG_RAM_START 0x100
#define DCPU_MSG_RAM(x) (DCPU_MSG_RAM_START + (x) * sizeof(u32))
/* DRAM Info Offsets & Masks */
#define DRAM_INFO_INTERVAL 0x0
#define DRAM_INFO_MR4 0x4
#define DRAM_INFO_ERROR 0x8
#define DRAM_INFO_MR4_MASK 0xff
/* DRAM MR4 Offsets & Masks */
#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */
#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */
#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */
#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */
#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */
#define DRAM_MR4_REFRESH_MASK 0x7
#define DRAM_MR4_SR_ABORT_MASK 0x1
#define DRAM_MR4_PPRE_MASK 0x1
#define DRAM_MR4_TH_OFFS_MASK 0x3
#define DRAM_MR4_TUF_MASK 0x1
/* DRAM Vendor Offsets & Masks */
#define DRAM_VENDOR_MR5 0x0
#define DRAM_VENDOR_MR6 0x4
#define DRAM_VENDOR_MR7 0x8
#define DRAM_VENDOR_MR8 0xc
#define DRAM_VENDOR_ERROR 0x10
#define DRAM_VENDOR_MASK 0xff
/* Reset register bits & masks */
#define DCPU_RESET_SHIFT 0x0
#define DCPU_RESET_MASK 0x1
#define DCPU_CLK_DISABLE_SHIFT 0x2
/* DCPU return codes */
#define DCPU_RET_ERROR_BIT BIT(31)
#define DCPU_RET_SUCCESS 0x1
#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0))
#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1))
#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2))
#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3))
/* This error code is not firmware defined and only used in the driver. */
#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4))
/* Firmware magic */
#define DPFE_BE_MAGIC 0xfe1010fe
#define DPFE_LE_MAGIC 0xfe0101fe
/* Error codes */
#define ERR_INVALID_MAGIC -1
#define ERR_INVALID_SIZE -2
#define ERR_INVALID_CHKSUM -3
/* Message types */
#define DPFE_MSG_TYPE_COMMAND 1
#define DPFE_MSG_TYPE_RESPONSE 2
#define DELAY_LOOP_MAX 200000
enum dpfe_msg_fields {
MSG_HEADER,
MSG_COMMAND,
MSG_ARG_COUNT,
MSG_ARG0,
MSG_CHKSUM,
MSG_FIELD_MAX /* Last entry */
};
enum dpfe_commands {
DPFE_CMD_GET_INFO,
DPFE_CMD_GET_REFRESH,
DPFE_CMD_GET_VENDOR,
DPFE_CMD_MAX /* Last entry */
};
struct dpfe_msg {
u32 header;
u32 command;
u32 arg_count;
u32 arg0;
u32 chksum; /* This is the sum of all other entries. */
};
/*
* Format of the binary firmware file:
*
* entry
* 0 header
* value: 0xfe0101fe <== little endian
* 0xfe1010fe <== big endian
* 1 sequence:
* [31:16] total segments on this build
* [15:0] this segment sequence.
* 2 FW version
* 3 IMEM byte size
* 4 DMEM byte size
* IMEM
* DMEM
* last checksum ==> sum of everything
*/
struct dpfe_firmware_header {
u32 magic;
u32 sequence;
u32 version;
u32 imem_size;
u32 dmem_size;
};
/* Things we only need during initialization. */
struct init_data {
unsigned int dmem_len;
unsigned int imem_len;
unsigned int chksum;
bool is_big_endian;
};
/* Things we need for as long as we are active. */
struct private_data {
void __iomem *regs;
void __iomem *dmem;
void __iomem *imem;
struct device *dev;
struct mutex lock;
};
static const char *error_text[] = {
"Success", "Header code incorrect", "Unknown command or argument",
"Incorrect checksum", "Malformed command", "Timed out",
};
/* List of supported firmware commands */
static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = {
[DPFE_CMD_GET_INFO] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 1,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 4,
},
[DPFE_CMD_GET_REFRESH] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 5,
},
[DPFE_CMD_GET_VENDOR] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 2,
[MSG_CHKSUM] = 6,
},
};
static bool is_dcpu_enabled(void __iomem *regs)
{
u32 val;
val = readl_relaxed(regs + REG_DCPU_RESET);
return !(val & DCPU_RESET_MASK);
}
static void __disable_dcpu(void __iomem *regs)
{
u32 val;
if (!is_dcpu_enabled(regs))
return;
/* Put DCPU in reset if it's running. */
val = readl_relaxed(regs + REG_DCPU_RESET);
val |= (1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
}
static void __enable_dcpu(void __iomem *regs)
{
u32 val;
/* Clear mailbox registers. */
writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
writel_relaxed(0, regs + REG_TO_HOST_MBOX);
/* Disable DCPU clock gating */
val = readl_relaxed(regs + REG_DCPU_RESET);
val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
/* Take DCPU out of reset */
val = readl_relaxed(regs + REG_DCPU_RESET);
val &= ~(1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
}
static unsigned int get_msg_chksum(const u32 msg[])
{
unsigned int sum = 0;
unsigned int i;
/* Don't include the last field in the checksum. */
for (i = 0; i < MSG_FIELD_MAX - 1; i++)
sum += msg[i];
return sum;
}
static void __iomem *get_msg_ptr(struct private_data *priv, u32 response,
char *buf, ssize_t *size)
{
unsigned int msg_type;
unsigned int offset;
void __iomem *ptr = NULL;
msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK;
offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK;
/*
* msg_type == 1: the offset is relative to the message RAM
* msg_type == 0: the offset is relative to the data RAM (this is the
* previous way of passing data)
* msg_type is anything else: there's critical hardware problem
*/
switch (msg_type) {
case 1:
ptr = priv->regs + DCPU_MSG_RAM_START + offset;
break;
case 0:
ptr = priv->dmem + offset;
break;
default:
dev_emerg(priv->dev, "invalid message reply from DCPU: %#x\n",
response);
if (buf && size)
*size = sprintf(buf,
"FATAL: communication error with DCPU\n");
}
return ptr;
}
static int __send_command(struct private_data *priv, unsigned int cmd,
u32 result[])
{
const u32 *msg = dpfe_commands[cmd];
void __iomem *regs = priv->regs;
unsigned int i, chksum;
int ret = 0;
u32 resp;
if (cmd >= DPFE_CMD_MAX)
return -1;
mutex_lock(&priv->lock);
/* Write command and arguments to message area */
for (i = 0; i < MSG_FIELD_MAX; i++)
writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
/* Tell DCPU there is a command waiting */
writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
/* Wait for DCPU to process the command */
for (i = 0; i < DELAY_LOOP_MAX; i++) {
/* Read response code */
resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
if (resp > 0)
break;
udelay(5);
}
if (i == DELAY_LOOP_MAX) {
resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
ret = -ffs(resp);
} else {
/* Read response data */
for (i = 0; i < MSG_FIELD_MAX; i++)
result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
}
/* Tell DCPU we are done */
writel_relaxed(0, regs + REG_TO_HOST_MBOX);
mutex_unlock(&priv->lock);
if (ret)
return ret;
/* Verify response */
chksum = get_msg_chksum(result);
if (chksum != result[MSG_CHKSUM])
resp = DCPU_RET_ERR_CHKSUM;
if (resp != DCPU_RET_SUCCESS) {
resp &= ~DCPU_RET_ERROR_BIT;
ret = -ffs(resp);
}
return ret;
}
/* Ensure that the firmware file loaded meets all the requirements. */
static int __verify_firmware(struct init_data *init,
const struct firmware *fw)
{
const struct dpfe_firmware_header *header = (void *)fw->data;
unsigned int dmem_size, imem_size, total_size;
bool is_big_endian = false;
const u32 *chksum_ptr;
if (header->magic == DPFE_BE_MAGIC)
is_big_endian = true;
else if (header->magic != DPFE_LE_MAGIC)
return ERR_INVALID_MAGIC;
if (is_big_endian) {
dmem_size = be32_to_cpu(header->dmem_size);
imem_size = be32_to_cpu(header->imem_size);
} else {
dmem_size = le32_to_cpu(header->dmem_size);
imem_size = le32_to_cpu(header->imem_size);
}
/* Data and instruction sections are 32 bit words. */
if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
return ERR_INVALID_SIZE;
/*
* The header + the data section + the instruction section + the
* checksum must be equal to the total firmware size.
*/
total_size = dmem_size + imem_size + sizeof(*header) +
sizeof(*chksum_ptr);
if (total_size != fw->size)
return ERR_INVALID_SIZE;
/* The checksum comes at the very end. */
chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
init->is_big_endian = is_big_endian;
init->dmem_len = dmem_size;
init->imem_len = imem_size;
init->chksum = (is_big_endian)
? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
return 0;
}
/* Verify checksum by reading back the firmware from co-processor RAM. */
static int __verify_fw_checksum(struct init_data *init,
struct private_data *priv,
const struct dpfe_firmware_header *header,
u32 checksum)
{
u32 magic, sequence, version, sum;
u32 __iomem *dmem = priv->dmem;
u32 __iomem *imem = priv->imem;
unsigned int i;
if (init->is_big_endian) {
magic = be32_to_cpu(header->magic);
sequence = be32_to_cpu(header->sequence);
version = be32_to_cpu(header->version);
} else {
magic = le32_to_cpu(header->magic);
sequence = le32_to_cpu(header->sequence);
version = le32_to_cpu(header->version);
}
sum = magic + sequence + version + init->dmem_len + init->imem_len;
for (i = 0; i < init->dmem_len / sizeof(u32); i++)
sum += readl_relaxed(dmem + i);
for (i = 0; i < init->imem_len / sizeof(u32); i++)
sum += readl_relaxed(imem + i);
return (sum == checksum) ? 0 : -1;
}
static int __write_firmware(u32 __iomem *mem, const u32 *fw,
unsigned int size, bool is_big_endian)
{
unsigned int i;
/* Convert size to 32-bit words. */
size /= sizeof(u32);
/* It is recommended to clear the firmware area first. */
for (i = 0; i < size; i++)
writel_relaxed(0, mem + i);
/* Now copy it. */
if (is_big_endian) {
for (i = 0; i < size; i++)
writel_relaxed(be32_to_cpu(fw[i]), mem + i);
} else {
for (i = 0; i < size; i++)
writel_relaxed(le32_to_cpu(fw[i]), mem + i);
}
return 0;
}
static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
struct init_data *init)
{
const struct dpfe_firmware_header *header;
unsigned int dmem_size, imem_size;
struct device *dev = &pdev->dev;
bool is_big_endian = false;
struct private_data *priv;
const struct firmware *fw;
const u32 *dmem, *imem;
const void *fw_blob;
int ret;
priv = platform_get_drvdata(pdev);
/*
* Skip downloading the firmware if the DCPU is already running and
* responding to commands.
*/
if (is_dcpu_enabled(priv->regs)) {
u32 response[MSG_FIELD_MAX];
ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
if (!ret)
return 0;
}
ret = request_firmware(&fw, FIRMWARE_NAME, dev);
/* request_firmware() prints its own error messages. */
if (ret)
return ret;
ret = __verify_firmware(init, fw);
if (ret)
return -EFAULT;
__disable_dcpu(priv->regs);
is_big_endian = init->is_big_endian;
dmem_size = init->dmem_len;
imem_size = init->imem_len;
/* At the beginning of the firmware blob is a header. */
header = (struct dpfe_firmware_header *)fw->data;
/* Void pointer to the beginning of the actual firmware. */
fw_blob = fw->data + sizeof(*header);
/* IMEM comes right after the header. */
imem = fw_blob;
/* DMEM follows after IMEM. */
dmem = fw_blob + imem_size;
ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
if (ret)
return ret;
ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
if (ret)
return ret;
ret = __verify_fw_checksum(init, priv, header, init->chksum);
if (ret)
return ret;
__enable_dcpu(priv->regs);
return 0;
}
static ssize_t generic_show(unsigned int command, u32 response[],
struct device *dev, char *buf)
{
struct private_data *priv;
int ret;
priv = dev_get_drvdata(dev);
if (!priv)
return sprintf(buf, "ERROR: driver private data not set\n");
ret = __send_command(priv, command, response);
if (ret < 0)
return sprintf(buf, "ERROR: %s\n", error_text[-ret]);
return 0;
}
static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
unsigned int info;
ssize_t ret;
ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf);
if (ret)
return ret;
info = response[MSG_ARG0];
return sprintf(buf, "%u.%u.%u.%u\n",
(info >> 24) & 0xff,
(info >> 16) & 0xff,
(info >> 8) & 0xff,
info & 0xff);
}
static ssize_t show_refresh(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 response[MSG_FIELD_MAX];
void __iomem *info;
struct private_data *priv;
u8 refresh, sr_abort, ppre, thermal_offs, tuf;
u32 mr4;
ssize_t ret;
ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf);
if (ret)
return ret;
priv = dev_get_drvdata(dev);
info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
if (!info)
return ret;
mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK;
refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
readl_relaxed(info + DRAM_INFO_INTERVAL),
refresh, sr_abort, ppre, thermal_offs, tuf,
readl_relaxed(info + DRAM_INFO_ERROR));
}
static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
void __iomem *info;
unsigned long val;
int ret;
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
priv = dev_get_drvdata(dev);
ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
if (ret)
return ret;
info = get_msg_ptr(priv, response[MSG_ARG0], NULL, NULL);
if (!info)
return -EIO;
writel_relaxed(val, info + DRAM_INFO_INTERVAL);
return count;
}
static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
void __iomem *info;
ssize_t ret;
ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf);
if (ret)
return ret;
priv = dev_get_drvdata(dev);
info = get_msg_ptr(priv, response[MSG_ARG0], buf, &ret);
if (!info)
return ret;
return sprintf(buf, "%#x %#x %#x %#x %#x\n",
readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_ERROR) &
DRAM_VENDOR_MASK);
}
static int brcmstb_dpfe_resume(struct platform_device *pdev)
{
struct init_data init;
return brcmstb_dpfe_download_firmware(pdev, &init);
}
static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
static struct attribute *dpfe_attrs[] = {
&dev_attr_dpfe_info.attr,
&dev_attr_dpfe_refresh.attr,
&dev_attr_dpfe_vendor.attr,
NULL
};
ATTRIBUTE_GROUPS(dpfe);
static int brcmstb_dpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct private_data *priv;
struct init_data init;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
priv->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->regs)) {
dev_err(dev, "couldn't map DCPU registers\n");
return -ENODEV;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
priv->dmem = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->dmem)) {
dev_err(dev, "Couldn't map DCPU data memory\n");
return -ENOENT;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
priv->imem = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->imem)) {
dev_err(dev, "Couldn't map DCPU instruction memory\n");
return -ENOENT;
}
ret = brcmstb_dpfe_download_firmware(pdev, &init);
if (ret)
return ret;
ret = sysfs_create_groups(&pdev->dev.kobj, dpfe_groups);
if (!ret)
dev_info(dev, "registered.\n");
return ret;
}
static int brcmstb_dpfe_remove(struct platform_device *pdev)
{
sysfs_remove_groups(&pdev->dev.kobj, dpfe_groups);
return 0;
}
static const struct of_device_id brcmstb_dpfe_of_match[] = {
{ .compatible = "brcm,dpfe-cpu", },
{}
};
MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
static struct platform_driver brcmstb_dpfe_driver = {
.driver = {
.name = DRVNAME,
.of_match_table = brcmstb_dpfe_of_match,
},
.probe = brcmstb_dpfe_probe,
.remove = brcmstb_dpfe_remove,
.resume = brcmstb_dpfe_resume,
};
module_platform_driver(brcmstb_dpfe_driver);
MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
MODULE_LICENSE("GPL");
+173
View File
@@ -0,0 +1,173 @@
/*
* TI da8xx DDR2/mDDR controller driver
*
* Copyright (C) 2016 BayLibre SAS
*
* Author:
* Bartosz Golaszewski <bgolaszewski@baylibre.com>
*
* 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/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
/*
* REVISIT: Linux doesn't have a good framework for the kind of performance
* knobs this driver controls. We can't use device tree properties as it deals
* with hardware configuration rather than description. We also don't want to
* commit to maintaining some random sysfs attributes.
*
* For now we just hardcode the register values for the boards that need
* some changes (as is the case for the LCD controller on da850-lcdk - the
* first board we support here). When linux gets an appropriate framework,
* we'll easily convert the driver to it.
*/
struct da8xx_ddrctl_config_knob {
const char *name;
u32 reg;
u32 mask;
u32 shift;
};
static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
{
.name = "da850-pbbpr",
.reg = 0x20,
.mask = 0xffffff00,
.shift = 0,
},
};
struct da8xx_ddrctl_setting {
const char *name;
u32 val;
};
struct da8xx_ddrctl_board_settings {
const char *board;
const struct da8xx_ddrctl_setting *settings;
};
static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
{
.name = "da850-pbbpr",
.val = 0x20,
},
{ }
};
static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
{
.board = "ti,da850-lcdk",
.settings = da850_lcdk_ddrctl_settings,
},
};
static const struct da8xx_ddrctl_config_knob *
da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
{
const struct da8xx_ddrctl_config_knob *knob;
int i;
for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
knob = &da8xx_ddrctl_knobs[i];
if (strcmp(knob->name, setting->name) == 0)
return knob;
}
return NULL;
}
static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
{
const struct da8xx_ddrctl_board_settings *board_settings;
int i;
for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
board_settings = &da8xx_ddrctl_board_confs[i];
if (of_machine_is_compatible(board_settings->board))
return board_settings->settings;
}
return NULL;
}
static int da8xx_ddrctl_probe(struct platform_device *pdev)
{
const struct da8xx_ddrctl_config_knob *knob;
const struct da8xx_ddrctl_setting *setting;
struct device_node *node;
struct resource *res;
void __iomem *ddrctl;
struct device *dev;
u32 reg;
dev = &pdev->dev;
node = dev->of_node;
setting = da8xx_ddrctl_get_board_settings();
if (!setting) {
dev_err(dev, "no settings defined for this board\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ddrctl = devm_ioremap_resource(dev, res);
if (IS_ERR(ddrctl)) {
dev_err(dev, "unable to map memory controller registers\n");
return PTR_ERR(ddrctl);
}
for (; setting->name; setting++) {
knob = da8xx_ddrctl_match_knob(setting);
if (!knob) {
dev_warn(dev,
"no such config option: %s\n", setting->name);
continue;
}
if (knob->reg + sizeof(u32) > resource_size(res)) {
dev_warn(dev,
"register offset of '%s' exceeds mapped memory size\n",
knob->name);
continue;
}
reg = readl(ddrctl + knob->reg);
reg &= knob->mask;
reg |= setting->val << knob->shift;
dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
writel(reg, ddrctl + knob->reg);
}
return 0;
}
static const struct of_device_id da8xx_ddrctl_of_match[] = {
{ .compatible = "ti,da850-ddr-controller", },
{ },
};
static struct platform_driver da8xx_ddrctl_driver = {
.probe = da8xx_ddrctl_probe,
.driver = {
.name = "da850-ddr-controller",
.of_match_table = da8xx_ddrctl_of_match,
},
};
module_platform_driver(da8xx_ddrctl_driver);
MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");
MODULE_LICENSE("GPL v2");
+22
View File
@@ -0,0 +1,22 @@
/*
* TI AM33XX EMIF PM Assembly Offsets
*
* Copyright (C) 2016-2017 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/ti-emif-sram.h>
int main(void)
{
ti_emif_asm_offsets();
return 0;
}
File diff suppressed because it is too large. Load diff
+606
View File
@@ -0,0 +1,606 @@
/*
* Defines for the EMIF driver
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* Benoit Cousson (b-cousson@ti.com)
*
* 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.
*/
#ifndef __EMIF_H
#define __EMIF_H
/*
* Maximum number of different frequencies supported by EMIF driver
* Determines the number of entries in the pointer array for register
* cache
*/
#define EMIF_MAX_NUM_FREQUENCIES 6
/* State of the core voltage */
#define DDR_VOLTAGE_STABLE 0
#define DDR_VOLTAGE_RAMPING 1
/* Defines for timing De-rating */
#define EMIF_NORMAL_TIMINGS 0
#define EMIF_DERATED_TIMINGS 1
/* Length of the forced read idle period in terms of cycles */
#define EMIF_READ_IDLE_LEN_VAL 5
/*
* forced read idle interval to be used when voltage
* is changed as part of DVFS/DPS - 1ms
*/
#define READ_IDLE_INTERVAL_DVFS (1*1000000)
/*
* Forced read idle interval to be used when voltage is stable
* 50us - or maximum value will do
*/
#define READ_IDLE_INTERVAL_NORMAL (50*1000000)
/* DLL calibration interval when voltage is NOT stable - 1us */
#define DLL_CALIB_INTERVAL_DVFS (1*1000000)
#define DLL_CALIB_ACK_WAIT_VAL 5
/* Interval between ZQCS commands - hw team recommended value */
#define EMIF_ZQCS_INTERVAL_US (50*1000)
/* Enable ZQ Calibration on exiting Self-refresh */
#define ZQ_SFEXITEN_ENABLE 1
/*
* ZQ Calibration simultaneously on both chip-selects:
* Needs one calibration resistor per CS
*/
#define ZQ_DUALCALEN_DISABLE 0
#define ZQ_DUALCALEN_ENABLE 1
#define T_ZQCS_DEFAULT_NS 90
#define T_ZQCL_DEFAULT_NS 360
#define T_ZQINIT_DEFAULT_NS 1000
/* DPD_EN */
#define DPD_DISABLE 0
#define DPD_ENABLE 1
/*
* Default values for the low-power entry to be used if not provided by user.
* OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
* Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
*/
#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE 2048
#define EMIF_LP_MODE_TIMEOUT_POWER 512
#define EMIF_LP_MODE_FREQ_THRESHOLD 400000000
/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY 0x049FF000
#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY 0x41
#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY 0x80
#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY 0x0E084200
#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS 10000
/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS 360
#define EMIF_T_CSTA 3
#define EMIF_T_PDLL_UL 128
/* External PHY control registers magic values */
#define EMIF_EXT_PHY_CTRL_1_VAL 0x04020080
#define EMIF_EXT_PHY_CTRL_5_VAL 0x04010040
#define EMIF_EXT_PHY_CTRL_6_VAL 0x01004010
#define EMIF_EXT_PHY_CTRL_7_VAL 0x00001004
#define EMIF_EXT_PHY_CTRL_8_VAL 0x04010040
#define EMIF_EXT_PHY_CTRL_9_VAL 0x01004010
#define EMIF_EXT_PHY_CTRL_10_VAL 0x00001004
#define EMIF_EXT_PHY_CTRL_11_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_12_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_13_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_14_VAL 0x80080080
#define EMIF_EXT_PHY_CTRL_15_VAL 0x00800800
#define EMIF_EXT_PHY_CTRL_16_VAL 0x08102040
#define EMIF_EXT_PHY_CTRL_17_VAL 0x00000001
#define EMIF_EXT_PHY_CTRL_18_VAL 0x540A8150
#define EMIF_EXT_PHY_CTRL_19_VAL 0xA81502A0
#define EMIF_EXT_PHY_CTRL_20_VAL 0x002A0540
#define EMIF_EXT_PHY_CTRL_21_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_22_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_23_VAL 0x00000000
#define EMIF_EXT_PHY_CTRL_24_VAL 0x00000077
#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS 1200
/* Registers offset */
#define EMIF_MODULE_ID_AND_REVISION 0x0000
#define EMIF_STATUS 0x0004
#define EMIF_SDRAM_CONFIG 0x0008
#define EMIF_SDRAM_CONFIG_2 0x000c
#define EMIF_SDRAM_REFRESH_CONTROL 0x0010
#define EMIF_SDRAM_REFRESH_CTRL_SHDW 0x0014
#define EMIF_SDRAM_TIMING_1 0x0018
#define EMIF_SDRAM_TIMING_1_SHDW 0x001c
#define EMIF_SDRAM_TIMING_2 0x0020
#define EMIF_SDRAM_TIMING_2_SHDW 0x0024
#define EMIF_SDRAM_TIMING_3 0x0028
#define EMIF_SDRAM_TIMING_3_SHDW 0x002c
#define EMIF_LPDDR2_NVM_TIMING 0x0030
#define EMIF_LPDDR2_NVM_TIMING_SHDW 0x0034
#define EMIF_POWER_MANAGEMENT_CONTROL 0x0038
#define EMIF_POWER_MANAGEMENT_CTRL_SHDW 0x003c
#define EMIF_LPDDR2_MODE_REG_DATA 0x0040
#define EMIF_LPDDR2_MODE_REG_CONFIG 0x0050
#define EMIF_OCP_CONFIG 0x0054
#define EMIF_OCP_CONFIG_VALUE_1 0x0058
#define EMIF_OCP_CONFIG_VALUE_2 0x005c
#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL 0x0060
#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT 0x0064
#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT 0x0068
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1 0x006c
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2 0x0070
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3 0x0074
#define EMIF_PERFORMANCE_COUNTER_1 0x0080
#define EMIF_PERFORMANCE_COUNTER_2 0x0084
#define EMIF_PERFORMANCE_COUNTER_CONFIG 0x0088
#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT 0x008c
#define EMIF_PERFORMANCE_COUNTER_TIME 0x0090
#define EMIF_MISC_REG 0x0094
#define EMIF_DLL_CALIB_CTRL 0x0098
#define EMIF_DLL_CALIB_CTRL_SHDW 0x009c
#define EMIF_END_OF_INTERRUPT 0x00a0
#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS 0x00a4
#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS 0x00a8
#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS 0x00ac
#define EMIF_LL_OCP_INTERRUPT_STATUS 0x00b0
#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET 0x00b4
#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET 0x00b8
#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR 0x00bc
#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR 0x00c0
#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG 0x00c8
#define EMIF_TEMPERATURE_ALERT_CONFIG 0x00cc
#define EMIF_OCP_ERROR_LOG 0x00d0
#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW 0x00d4
#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL 0x00d8
#define EMIF_READ_WRITE_LEVELING_CONTROL 0x00dc
#define EMIF_DDR_PHY_CTRL_1 0x00e4
#define EMIF_DDR_PHY_CTRL_1_SHDW 0x00e8
#define EMIF_DDR_PHY_CTRL_2 0x00ec
#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING 0x0100
#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
#define EMIF_READ_WRITE_EXECUTION_THRESHOLD 0x0120
#define EMIF_COS_CONFIG 0x0124
#define EMIF_PHY_STATUS_1 0x0140
#define EMIF_PHY_STATUS_2 0x0144
#define EMIF_PHY_STATUS_3 0x0148
#define EMIF_PHY_STATUS_4 0x014c
#define EMIF_PHY_STATUS_5 0x0150
#define EMIF_PHY_STATUS_6 0x0154
#define EMIF_PHY_STATUS_7 0x0158
#define EMIF_PHY_STATUS_8 0x015c
#define EMIF_PHY_STATUS_9 0x0160
#define EMIF_PHY_STATUS_10 0x0164
#define EMIF_PHY_STATUS_11 0x0168
#define EMIF_PHY_STATUS_12 0x016c
#define EMIF_PHY_STATUS_13 0x0170
#define EMIF_PHY_STATUS_14 0x0174
#define EMIF_PHY_STATUS_15 0x0178
#define EMIF_PHY_STATUS_16 0x017c
#define EMIF_PHY_STATUS_17 0x0180
#define EMIF_PHY_STATUS_18 0x0184
#define EMIF_PHY_STATUS_19 0x0188
#define EMIF_PHY_STATUS_20 0x018c
#define EMIF_PHY_STATUS_21 0x0190
#define EMIF_EXT_PHY_CTRL_1 0x0200
#define EMIF_EXT_PHY_CTRL_1_SHDW 0x0204
#define EMIF_EXT_PHY_CTRL_2 0x0208
#define EMIF_EXT_PHY_CTRL_2_SHDW 0x020c
#define EMIF_EXT_PHY_CTRL_3 0x0210
#define EMIF_EXT_PHY_CTRL_3_SHDW 0x0214
#define EMIF_EXT_PHY_CTRL_4 0x0218
#define EMIF_EXT_PHY_CTRL_4_SHDW 0x021c
#define EMIF_EXT_PHY_CTRL_5 0x0220
#define EMIF_EXT_PHY_CTRL_5_SHDW 0x0224
#define EMIF_EXT_PHY_CTRL_6 0x0228
#define EMIF_EXT_PHY_CTRL_6_SHDW 0x022c
#define EMIF_EXT_PHY_CTRL_7 0x0230
#define EMIF_EXT_PHY_CTRL_7_SHDW 0x0234
#define EMIF_EXT_PHY_CTRL_8 0x0238
#define EMIF_EXT_PHY_CTRL_8_SHDW 0x023c
#define EMIF_EXT_PHY_CTRL_9 0x0240
#define EMIF_EXT_PHY_CTRL_9_SHDW 0x0244
#define EMIF_EXT_PHY_CTRL_10 0x0248
#define EMIF_EXT_PHY_CTRL_10_SHDW 0x024c
#define EMIF_EXT_PHY_CTRL_11 0x0250
#define EMIF_EXT_PHY_CTRL_11_SHDW 0x0254
#define EMIF_EXT_PHY_CTRL_12 0x0258
#define EMIF_EXT_PHY_CTRL_12_SHDW 0x025c
#define EMIF_EXT_PHY_CTRL_13 0x0260
#define EMIF_EXT_PHY_CTRL_13_SHDW 0x0264
#define EMIF_EXT_PHY_CTRL_14 0x0268
#define EMIF_EXT_PHY_CTRL_14_SHDW 0x026c
#define EMIF_EXT_PHY_CTRL_15 0x0270
#define EMIF_EXT_PHY_CTRL_15_SHDW 0x0274
#define EMIF_EXT_PHY_CTRL_16 0x0278
#define EMIF_EXT_PHY_CTRL_16_SHDW 0x027c
#define EMIF_EXT_PHY_CTRL_17 0x0280
#define EMIF_EXT_PHY_CTRL_17_SHDW 0x0284
#define EMIF_EXT_PHY_CTRL_18 0x0288
#define EMIF_EXT_PHY_CTRL_18_SHDW 0x028c
#define EMIF_EXT_PHY_CTRL_19 0x0290
#define EMIF_EXT_PHY_CTRL_19_SHDW 0x0294
#define EMIF_EXT_PHY_CTRL_20 0x0298
#define EMIF_EXT_PHY_CTRL_20_SHDW 0x029c
#define EMIF_EXT_PHY_CTRL_21 0x02a0
#define EMIF_EXT_PHY_CTRL_21_SHDW 0x02a4
#define EMIF_EXT_PHY_CTRL_22 0x02a8
#define EMIF_EXT_PHY_CTRL_22_SHDW 0x02ac
#define EMIF_EXT_PHY_CTRL_23 0x02b0
#define EMIF_EXT_PHY_CTRL_23_SHDW 0x02b4
#define EMIF_EXT_PHY_CTRL_24 0x02b8
#define EMIF_EXT_PHY_CTRL_24_SHDW 0x02bc
#define EMIF_EXT_PHY_CTRL_25 0x02c0
#define EMIF_EXT_PHY_CTRL_25_SHDW 0x02c4
#define EMIF_EXT_PHY_CTRL_26 0x02c8
#define EMIF_EXT_PHY_CTRL_26_SHDW 0x02cc
#define EMIF_EXT_PHY_CTRL_27 0x02d0
#define EMIF_EXT_PHY_CTRL_27_SHDW 0x02d4
#define EMIF_EXT_PHY_CTRL_28 0x02d8
#define EMIF_EXT_PHY_CTRL_28_SHDW 0x02dc
#define EMIF_EXT_PHY_CTRL_29 0x02e0
#define EMIF_EXT_PHY_CTRL_29_SHDW 0x02e4
#define EMIF_EXT_PHY_CTRL_30 0x02e8
#define EMIF_EXT_PHY_CTRL_30_SHDW 0x02ec
/* Registers shifts and masks */
/* EMIF_MODULE_ID_AND_REVISION */
#define SCHEME_SHIFT 30
#define SCHEME_MASK (0x3 << 30)
#define MODULE_ID_SHIFT 16
#define MODULE_ID_MASK (0xfff << 16)
#define RTL_VERSION_SHIFT 11
#define RTL_VERSION_MASK (0x1f << 11)
#define MAJOR_REVISION_SHIFT 8
#define MAJOR_REVISION_MASK (0x7 << 8)
#define MINOR_REVISION_SHIFT 0
#define MINOR_REVISION_MASK (0x3f << 0)
/* STATUS */
#define BE_SHIFT 31
#define BE_MASK (1 << 31)
#define DUAL_CLK_MODE_SHIFT 30
#define DUAL_CLK_MODE_MASK (1 << 30)
#define FAST_INIT_SHIFT 29
#define FAST_INIT_MASK (1 << 29)
#define RDLVLGATETO_SHIFT 6
#define RDLVLGATETO_MASK (1 << 6)
#define RDLVLTO_SHIFT 5
#define RDLVLTO_MASK (1 << 5)
#define WRLVLTO_SHIFT 4
#define WRLVLTO_MASK (1 << 4)
#define PHY_DLL_READY_SHIFT 2
#define PHY_DLL_READY_MASK (1 << 2)
/* SDRAM_CONFIG */
#define SDRAM_TYPE_SHIFT 29
#define SDRAM_TYPE_MASK (0x7 << 29)
#define IBANK_POS_SHIFT 27
#define IBANK_POS_MASK (0x3 << 27)
#define DDR_TERM_SHIFT 24
#define DDR_TERM_MASK (0x7 << 24)
#define DDR2_DDQS_SHIFT 23
#define DDR2_DDQS_MASK (1 << 23)
#define DYN_ODT_SHIFT 21
#define DYN_ODT_MASK (0x3 << 21)
#define DDR_DISABLE_DLL_SHIFT 20
#define DDR_DISABLE_DLL_MASK (1 << 20)
#define SDRAM_DRIVE_SHIFT 18
#define SDRAM_DRIVE_MASK (0x3 << 18)
#define CWL_SHIFT 16
#define CWL_MASK (0x3 << 16)
#define NARROW_MODE_SHIFT 14
#define NARROW_MODE_MASK (0x3 << 14)
#define CL_SHIFT 10
#define CL_MASK (0xf << 10)
#define ROWSIZE_SHIFT 7
#define ROWSIZE_MASK (0x7 << 7)
#define IBANK_SHIFT 4
#define IBANK_MASK (0x7 << 4)
#define EBANK_SHIFT 3
#define EBANK_MASK (1 << 3)
#define PAGESIZE_SHIFT 0
#define PAGESIZE_MASK (0x7 << 0)
/* SDRAM_CONFIG_2 */
#define CS1NVMEN_SHIFT 30
#define CS1NVMEN_MASK (1 << 30)
#define EBANK_POS_SHIFT 27
#define EBANK_POS_MASK (1 << 27)
#define RDBNUM_SHIFT 4
#define RDBNUM_MASK (0x3 << 4)
#define RDBSIZE_SHIFT 0
#define RDBSIZE_MASK (0x7 << 0)
/* SDRAM_REFRESH_CONTROL */
#define INITREF_DIS_SHIFT 31
#define INITREF_DIS_MASK (1 << 31)
#define SRT_SHIFT 29
#define SRT_MASK (1 << 29)
#define ASR_SHIFT 28
#define ASR_MASK (1 << 28)
#define PASR_SHIFT 24
#define PASR_MASK (0x7 << 24)
#define REFRESH_RATE_SHIFT 0
#define REFRESH_RATE_MASK (0xffff << 0)
/* SDRAM_TIMING_1 */
#define T_RTW_SHIFT 29
#define T_RTW_MASK (0x7 << 29)
#define T_RP_SHIFT 25
#define T_RP_MASK (0xf << 25)
#define T_RCD_SHIFT 21
#define T_RCD_MASK (0xf << 21)
#define T_WR_SHIFT 17
#define T_WR_MASK (0xf << 17)
#define T_RAS_SHIFT 12
#define T_RAS_MASK (0x1f << 12)
#define T_RC_SHIFT 6
#define T_RC_MASK (0x3f << 6)
#define T_RRD_SHIFT 3
#define T_RRD_MASK (0x7 << 3)
#define T_WTR_SHIFT 0
#define T_WTR_MASK (0x7 << 0)
/* SDRAM_TIMING_2 */
#define T_XP_SHIFT 28
#define T_XP_MASK (0x7 << 28)
#define T_ODT_SHIFT 25
#define T_ODT_MASK (0x7 << 25)
#define T_XSNR_SHIFT 16
#define T_XSNR_MASK (0x1ff << 16)
#define T_XSRD_SHIFT 6
#define T_XSRD_MASK (0x3ff << 6)
#define T_RTP_SHIFT 3
#define T_RTP_MASK (0x7 << 3)
#define T_CKE_SHIFT 0
#define T_CKE_MASK (0x7 << 0)
/* SDRAM_TIMING_3 */
#define T_PDLL_UL_SHIFT 28
#define T_PDLL_UL_MASK (0xf << 28)
#define T_CSTA_SHIFT 24
#define T_CSTA_MASK (0xf << 24)
#define T_CKESR_SHIFT 21
#define T_CKESR_MASK (0x7 << 21)
#define ZQ_ZQCS_SHIFT 15
#define ZQ_ZQCS_MASK (0x3f << 15)
#define T_TDQSCKMAX_SHIFT 13
#define T_TDQSCKMAX_MASK (0x3 << 13)
#define T_RFC_SHIFT 4
#define T_RFC_MASK (0x1ff << 4)
#define T_RAS_MAX_SHIFT 0
#define T_RAS_MAX_MASK (0xf << 0)
/* POWER_MANAGEMENT_CONTROL */
#define PD_TIM_SHIFT 12
#define PD_TIM_MASK (0xf << 12)
#define DPD_EN_SHIFT 11
#define DPD_EN_MASK (1 << 11)
#define LP_MODE_SHIFT 8
#define LP_MODE_MASK (0x7 << 8)
#define SR_TIM_SHIFT 4
#define SR_TIM_MASK (0xf << 4)
#define CS_TIM_SHIFT 0
#define CS_TIM_MASK (0xf << 0)
/* LPDDR2_MODE_REG_DATA */
#define VALUE_0_SHIFT 0
#define VALUE_0_MASK (0x7f << 0)
/* LPDDR2_MODE_REG_CONFIG */
#define CS_SHIFT 31
#define CS_MASK (1 << 31)
#define REFRESH_EN_SHIFT 30
#define REFRESH_EN_MASK (1 << 30)
#define ADDRESS_SHIFT 0
#define ADDRESS_MASK (0xff << 0)
/* OCP_CONFIG */
#define SYS_THRESH_MAX_SHIFT 24
#define SYS_THRESH_MAX_MASK (0xf << 24)
#define MPU_THRESH_MAX_SHIFT 20
#define MPU_THRESH_MAX_MASK (0xf << 20)
#define LL_THRESH_MAX_SHIFT 16
#define LL_THRESH_MAX_MASK (0xf << 16)
/* PERFORMANCE_COUNTER_1 */
#define COUNTER1_SHIFT 0
#define COUNTER1_MASK (0xffffffff << 0)
/* PERFORMANCE_COUNTER_2 */
#define COUNTER2_SHIFT 0
#define COUNTER2_MASK (0xffffffff << 0)
/* PERFORMANCE_COUNTER_CONFIG */
#define CNTR2_MCONNID_EN_SHIFT 31
#define CNTR2_MCONNID_EN_MASK (1 << 31)
#define CNTR2_REGION_EN_SHIFT 30
#define CNTR2_REGION_EN_MASK (1 << 30)
#define CNTR2_CFG_SHIFT 16
#define CNTR2_CFG_MASK (0xf << 16)
#define CNTR1_MCONNID_EN_SHIFT 15
#define CNTR1_MCONNID_EN_MASK (1 << 15)
#define CNTR1_REGION_EN_SHIFT 14
#define CNTR1_REGION_EN_MASK (1 << 14)
#define CNTR1_CFG_SHIFT 0
#define CNTR1_CFG_MASK (0xf << 0)
/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
#define MCONNID2_SHIFT 24
#define MCONNID2_MASK (0xff << 24)
#define REGION_SEL2_SHIFT 16
#define REGION_SEL2_MASK (0x3 << 16)
#define MCONNID1_SHIFT 8
#define MCONNID1_MASK (0xff << 8)
#define REGION_SEL1_SHIFT 0
#define REGION_SEL1_MASK (0x3 << 0)
/* PERFORMANCE_COUNTER_TIME */
#define TOTAL_TIME_SHIFT 0
#define TOTAL_TIME_MASK (0xffffffff << 0)
/* DLL_CALIB_CTRL */
#define ACK_WAIT_SHIFT 16
#define ACK_WAIT_MASK (0xf << 16)
#define DLL_CALIB_INTERVAL_SHIFT 0
#define DLL_CALIB_INTERVAL_MASK (0x1ff << 0)
/* END_OF_INTERRUPT */
#define EOI_SHIFT 0
#define EOI_MASK (1 << 0)
/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
#define DNV_SYS_SHIFT 2
#define DNV_SYS_MASK (1 << 2)
#define TA_SYS_SHIFT 1
#define TA_SYS_MASK (1 << 1)
#define ERR_SYS_SHIFT 0
#define ERR_SYS_MASK (1 << 0)
/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
#define DNV_LL_SHIFT 2
#define DNV_LL_MASK (1 << 2)
#define TA_LL_SHIFT 1
#define TA_LL_MASK (1 << 1)
#define ERR_LL_SHIFT 0
#define ERR_LL_MASK (1 << 0)
/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
#define EN_DNV_SYS_SHIFT 2
#define EN_DNV_SYS_MASK (1 << 2)
#define EN_TA_SYS_SHIFT 1
#define EN_TA_SYS_MASK (1 << 1)
#define EN_ERR_SYS_SHIFT 0
#define EN_ERR_SYS_MASK (1 << 0)
/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
#define EN_DNV_LL_SHIFT 2
#define EN_DNV_LL_MASK (1 << 2)
#define EN_TA_LL_SHIFT 1
#define EN_TA_LL_MASK (1 << 1)
#define EN_ERR_LL_SHIFT 0
#define EN_ERR_LL_MASK (1 << 0)
/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
#define ZQ_CS1EN_SHIFT 31
#define ZQ_CS1EN_MASK (1 << 31)
#define ZQ_CS0EN_SHIFT 30
#define ZQ_CS0EN_MASK (1 << 30)
#define ZQ_DUALCALEN_SHIFT 29
#define ZQ_DUALCALEN_MASK (1 << 29)
#define ZQ_SFEXITEN_SHIFT 28
#define ZQ_SFEXITEN_MASK (1 << 28)
#define ZQ_ZQINIT_MULT_SHIFT 18
#define ZQ_ZQINIT_MULT_MASK (0x3 << 18)
#define ZQ_ZQCL_MULT_SHIFT 16
#define ZQ_ZQCL_MULT_MASK (0x3 << 16)
#define ZQ_REFINTERVAL_SHIFT 0
#define ZQ_REFINTERVAL_MASK (0xffff << 0)
/* TEMPERATURE_ALERT_CONFIG */
#define TA_CS1EN_SHIFT 31
#define TA_CS1EN_MASK (1 << 31)
#define TA_CS0EN_SHIFT 30
#define TA_CS0EN_MASK (1 << 30)
#define TA_SFEXITEN_SHIFT 28
#define TA_SFEXITEN_MASK (1 << 28)
#define TA_DEVWDT_SHIFT 26
#define TA_DEVWDT_MASK (0x3 << 26)
#define TA_DEVCNT_SHIFT 24
#define TA_DEVCNT_MASK (0x3 << 24)
#define TA_REFINTERVAL_SHIFT 0
#define TA_REFINTERVAL_MASK (0x3fffff << 0)
/* OCP_ERROR_LOG */
#define MADDRSPACE_SHIFT 14
#define MADDRSPACE_MASK (0x3 << 14)
#define MBURSTSEQ_SHIFT 11
#define MBURSTSEQ_MASK (0x7 << 11)
#define MCMD_SHIFT 8
#define MCMD_MASK (0x7 << 8)
#define MCONNID_SHIFT 0
#define MCONNID_MASK (0xff << 0)
/* DDR_PHY_CTRL_1 - EMIF4D */
#define DLL_SLAVE_DLY_CTRL_SHIFT_4D 4
#define DLL_SLAVE_DLY_CTRL_MASK_4D (0xFF << 4)
#define READ_LATENCY_SHIFT_4D 0
#define READ_LATENCY_MASK_4D (0xf << 0)
/* DDR_PHY_CTRL_1 - EMIF4D5 */
#define DLL_HALF_DELAY_SHIFT_4D5 21
#define DLL_HALF_DELAY_MASK_4D5 (1 << 21)
#define READ_LATENCY_SHIFT_4D5 0
#define READ_LATENCY_MASK_4D5 (0x1f << 0)
/* DDR_PHY_CTRL_1_SHDW */
#define DDR_PHY_CTRL_1_SHDW_SHIFT 5
#define DDR_PHY_CTRL_1_SHDW_MASK (0x7ffffff << 5)
#define READ_LATENCY_SHDW_SHIFT 0
#define READ_LATENCY_SHDW_MASK (0x1f << 0)
#define EMIF_SRAM_AM33_REG_LAYOUT 0x00000000
#define EMIF_SRAM_AM43_REG_LAYOUT 0x00000001
#ifndef __ASSEMBLY__
/*
* Structure containing shadow of important registers in EMIF
* The calculation function fills in this structure to be later used for
* initialisation and DVFS
*/
struct emif_regs {
u32 freq;
u32 ref_ctrl_shdw;
u32 ref_ctrl_shdw_derated;
u32 sdram_tim1_shdw;
u32 sdram_tim1_shdw_derated;
u32 sdram_tim2_shdw;
u32 sdram_tim3_shdw;
u32 sdram_tim3_shdw_derated;
u32 pwr_mgmt_ctrl_shdw;
union {
u32 read_idle_ctrl_shdw_normal;
u32 dll_calib_ctrl_shdw_normal;
};
union {
u32 read_idle_ctrl_shdw_volt_ramp;
u32 dll_calib_ctrl_shdw_volt_ramp;
};
u32 phy_ctrl_1_shdw;
u32 ext_phy_ctrl_2_shdw;
u32 ext_phy_ctrl_3_shdw;
u32 ext_phy_ctrl_4_shdw;
};
struct ti_emif_pm_functions;
extern unsigned int ti_emif_sram;
extern unsigned int ti_emif_sram_sz;
extern struct ti_emif_pm_data ti_emif_pm_sram_data;
extern struct emif_regs_amx3 ti_emif_regs_amx3;
void ti_emif_save_context(void);
void ti_emif_restore_context(void);
void ti_emif_enter_sr(void);
void ti_emif_exit_sr(void);
void ti_emif_abort_sr(void);
#endif /* __ASSEMBLY__ */
#endif /* __EMIF_H */
+281
View File
@@ -0,0 +1,281 @@
/*
* CoreNet Coherency Fabric error reporting
*
* Copyright 2014 Freescale Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
enum ccf_version {
CCF1,
CCF2,
};
struct ccf_info {
enum ccf_version version;
int err_reg_offs;
bool has_brr;
};
static const struct ccf_info ccf1_info = {
.version = CCF1,
.err_reg_offs = 0xa00,
.has_brr = false,
};
static const struct ccf_info ccf2_info = {
.version = CCF2,
.err_reg_offs = 0xe40,
.has_brr = true,
};
/*
* This register is present but not documented, with different values for
* IP_ID, on other chips with fsl,corenet2-cf such as t4240 and b4860.
*/
#define CCF_BRR 0xbf8
#define CCF_BRR_IPID 0xffff0000
#define CCF_BRR_IPID_T1040 0x09310000
static const struct of_device_id ccf_matches[] = {
{
.compatible = "fsl,corenet1-cf",
.data = &ccf1_info,
},
{
.compatible = "fsl,corenet2-cf",
.data = &ccf2_info,
},
{}
};
MODULE_DEVICE_TABLE(of, ccf_matches);
struct ccf_err_regs {
u32 errdet; /* 0x00 Error Detect Register */
/* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */
u32 errdis;
/* 0x08 Error Interrupt Enable Register (ccf2 only) */
u32 errinten;
u32 cecar; /* 0x0c Error Capture Attribute Register */
u32 cecaddrh; /* 0x10 Error Capture Address High */
u32 cecaddrl; /* 0x14 Error Capture Address Low */
u32 cecar2; /* 0x18 Error Capture Attribute Register 2 */
};
/* LAE/CV also valid for errdis and errinten */
#define ERRDET_LAE (1 << 0) /* Local Access Error */
#define ERRDET_CV (1 << 1) /* Coherency Violation */
#define ERRDET_UTID (1 << 2) /* Unavailable Target ID (t1040) */
#define ERRDET_MCST (1 << 3) /* Multicast Stash (t1040) */
#define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */
#define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT)
#define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */
#define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */
#define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */
#define CECAR_SRCID_SHIFT_CCF1 24
#define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1)
#define CECAR_SRCID_SHIFT_CCF2 18
#define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2)
#define CECADDRH_ADDRH 0xff
struct ccf_private {
const struct ccf_info *info;
struct device *dev;
void __iomem *regs;
struct ccf_err_regs __iomem *err_regs;
bool t1040;
};
static irqreturn_t ccf_irq(int irq, void *dev_id)
{
struct ccf_private *ccf = dev_id;
static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
u32 errdet, cecar, cecar2;
u64 addr;
u32 src_id;
bool uvt = false;
bool cap_valid = false;
errdet = ioread32be(&ccf->err_regs->errdet);
cecar = ioread32be(&ccf->err_regs->cecar);
cecar2 = ioread32be(&ccf->err_regs->cecar2);
addr = ioread32be(&ccf->err_regs->cecaddrl);
addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) &
CECADDRH_ADDRH)) << 32;
if (!__ratelimit(&ratelimit))
goto out;
switch (ccf->info->version) {
case CCF1:
if (cecar & CECAR_VAL) {
if (cecar & CECAR_UVT)
uvt = true;
src_id = (cecar & CECAR_SRCID_MASK_CCF1) >>
CECAR_SRCID_SHIFT_CCF1;
cap_valid = true;
}
break;
case CCF2:
if (errdet & ERRDET_CAP) {
src_id = (cecar & CECAR_SRCID_MASK_CCF2) >>
CECAR_SRCID_SHIFT_CCF2;
cap_valid = true;
}
break;
}
dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n",
errdet, cecar, cecar2);
if (errdet & ERRDET_LAE) {
if (uvt)
dev_crit(ccf->dev, "LAW Unavailable Target ID\n");
else
dev_crit(ccf->dev, "Local Access Window Error\n");
}
if (errdet & ERRDET_CV)
dev_crit(ccf->dev, "Coherency Violation\n");
if (errdet & ERRDET_UTID)
dev_crit(ccf->dev, "Unavailable Target ID\n");
if (errdet & ERRDET_MCST)
dev_crit(ccf->dev, "Multicast Stash\n");
if (cap_valid) {
dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n",
addr, src_id);
}
out:
iowrite32be(errdet, &ccf->err_regs->errdet);
return errdet ? IRQ_HANDLED : IRQ_NONE;
}
static int ccf_probe(struct platform_device *pdev)
{
struct ccf_private *ccf;
struct resource *r;
const struct of_device_id *match;
u32 errinten;
int ret, irq;
match = of_match_device(ccf_matches, &pdev->dev);
if (WARN_ON(!match))
return -ENODEV;
ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL);
if (!ccf)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
return -ENXIO;
}
ccf->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(ccf->regs)) {
dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__);
return PTR_ERR(ccf->regs);
}
ccf->dev = &pdev->dev;
ccf->info = match->data;
ccf->err_regs = ccf->regs + ccf->info->err_reg_offs;
if (ccf->info->has_brr) {
u32 brr = ioread32be(ccf->regs + CCF_BRR);
if ((brr & CCF_BRR_IPID) == CCF_BRR_IPID_T1040)
ccf->t1040 = true;
}
dev_set_drvdata(&pdev->dev, ccf);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf);
if (ret) {
dev_err(&pdev->dev, "%s: can't request irq\n", __func__);
return ret;
}
errinten = ERRDET_LAE | ERRDET_CV;
if (ccf->t1040)
errinten |= ERRDET_UTID | ERRDET_MCST;
switch (ccf->info->version) {
case CCF1:
/* On CCF1 this register enables rather than disables. */
iowrite32be(errinten, &ccf->err_regs->errdis);
break;
case CCF2:
iowrite32be(0, &ccf->err_regs->errdis);
iowrite32be(errinten, &ccf->err_regs->errinten);
break;
}
return 0;
}
static int ccf_remove(struct platform_device *pdev)
{
struct ccf_private *ccf = dev_get_drvdata(&pdev->dev);
switch (ccf->info->version) {
case CCF1:
iowrite32be(0, &ccf->err_regs->errdis);
break;
case CCF2:
/*
* We clear errdis on ccf1 because that's the only way to
* disable interrupts, but on ccf2 there's no need to disable
* detection.
*/
iowrite32be(0, &ccf->err_regs->errinten);
break;
}
return 0;
}
static struct platform_driver ccf_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ccf_matches,
},
.probe = ccf_probe,
.remove = ccf_remove,
};
module_platform_driver(ccf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting");
+338
View File
@@ -0,0 +1,338 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc
*
* Freescale Integrated Flash Controller
*
* Author: Dipen Dudhat <Dipen.Dudhat@freescale.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/fsl_ifc.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
EXPORT_SYMBOL(fsl_ifc_ctrl_dev);
/*
* convert_ifc_address - convert the base address
* @addr_base: base address of the memory bank
*/
unsigned int convert_ifc_address(phys_addr_t addr_base)
{
return addr_base & CSPR_BA;
}
EXPORT_SYMBOL(convert_ifc_address);
/*
* fsl_ifc_find - find IFC bank
* @addr_base: base address of the memory bank
*
* This function walks IFC banks comparing "Base address" field of the CSPR
* registers with the supplied addr_base argument. When bases match this
* function returns bank number (starting with 0), otherwise it returns
* appropriate errno value.
*/
int fsl_ifc_find(phys_addr_t addr_base)
{
int i = 0;
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs)
return -ENODEV;
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base))
return i;
}
return -ENOENT;
}
EXPORT_SYMBOL(fsl_ifc_find);
static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
{
struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
/*
* Clear all the common status and event registers
*/
if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* enable all error and events */
ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en);
/* enable all error and event interrupts */
ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en);
ifc_out32(0x0, &ifc->cm_erattr0);
ifc_out32(0x0, &ifc->cm_erattr1);
return 0;
}
static int fsl_ifc_ctrl_remove(struct platform_device *dev)
{
struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(&dev->dev);
free_irq(ctrl->nand_irq, ctrl);
free_irq(ctrl->irq, ctrl);
irq_dispose_mapping(ctrl->nand_irq);
irq_dispose_mapping(ctrl->irq);
iounmap(ctrl->gregs);
dev_set_drvdata(&dev->dev, NULL);
kfree(ctrl);
return 0;
}
/*
* NAND events are split between an operational interrupt which only
* receives OPC, and an error interrupt that receives everything else,
* including non-NAND errors. Whichever interrupt gets to it first
* records the status and wakes the wait queue.
*/
static DEFINE_SPINLOCK(nand_irq_lock);
static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
{
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
unsigned long flags;
u32 stat;
spin_lock_irqsave(&nand_irq_lock, flags);
stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
if (stat) {
ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat);
ctrl->nand_stat = stat;
wake_up(&ctrl->nand_wait);
}
spin_unlock_irqrestore(&nand_irq_lock, flags);
return stat;
}
static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
{
struct fsl_ifc_ctrl *ctrl = data;
if (check_nand_stat(ctrl))
return IRQ_HANDLED;
return IRQ_NONE;
}
/*
* NOTE: This interrupt is used to report ifc events of various kinds,
* such as transaction errors on the chipselects.
*/
static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
{
struct fsl_ifc_ctrl *ctrl = data;
struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
u32 err_axiid, err_srcid, status, cs_err, err_addr;
irqreturn_t ret = IRQ_NONE;
/* read for chip select error */
cs_err = ifc_in32(&ifc->cm_evter_stat);
if (cs_err) {
dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"
"any memory bank 0x%08X\n", cs_err);
/* clear the chip select error */
ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* read error attribute registers print the error information */
status = ifc_in32(&ifc->cm_erattr0);
err_addr = ifc_in32(&ifc->cm_erattr1);
if (status & IFC_CM_ERATTR0_ERTYP_READ)
dev_err(ctrl->dev, "Read transaction error"
"CM_ERATTR0 0x%08X\n", status);
else
dev_err(ctrl->dev, "Write transaction error"
"CM_ERATTR0 0x%08X\n", status);
err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
IFC_CM_ERATTR0_ERAID_SHIFT;
dev_err(ctrl->dev, "AXI ID of the error"
"transaction 0x%08X\n", err_axiid);
err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
IFC_CM_ERATTR0_ESRCID_SHIFT;
dev_err(ctrl->dev, "SRC ID of the error"
"transaction 0x%08X\n", err_srcid);
dev_err(ctrl->dev, "Transaction Address corresponding to error"
"ERADDR 0x%08X\n", err_addr);
ret = IRQ_HANDLED;
}
if (check_nand_stat(ctrl))
ret = IRQ_HANDLED;
return ret;
}
/*
* fsl_ifc_ctrl_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code allocates all of
* the resources needed for the controller only. The
* resources for the NAND banks themselves are allocated
* in the chip probe function.
*/
static int fsl_ifc_ctrl_probe(struct platform_device *dev)
{
int ret = 0;
int version, banks;
void __iomem *addr;
dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
fsl_ifc_ctrl_dev = kzalloc(sizeof(*fsl_ifc_ctrl_dev), GFP_KERNEL);
if (!fsl_ifc_ctrl_dev)
return -ENOMEM;
dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
/* IOMAP the entire IFC region */
fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
if (!fsl_ifc_ctrl_dev->gregs) {
dev_err(&dev->dev, "failed to get memory region\n");
ret = -ENODEV;
goto err;
}
if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
fsl_ifc_ctrl_dev->little_endian = true;
dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
} else {
fsl_ifc_ctrl_dev->little_endian = false;
dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
}
version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) &
FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
version >> 24, (version >> 16) & 0xf, banks);
fsl_ifc_ctrl_dev->version = version;
fsl_ifc_ctrl_dev->banks = banks;
addr = fsl_ifc_ctrl_dev->gregs;
if (version >= FSL_IFC_VERSION_2_0_0)
addr += PGOFFSET_64K;
else
addr += PGOFFSET_4K;
fsl_ifc_ctrl_dev->rregs = addr;
/* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == 0) {
dev_err(&dev->dev, "failed to get irq resource "
"for IFC\n");
ret = -ENODEV;
goto err;
}
/* get the nand machine irq */
fsl_ifc_ctrl_dev->nand_irq =
irq_of_parse_and_map(dev->dev.of_node, 1);
fsl_ifc_ctrl_dev->dev = &dev->dev;
ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
if (ret < 0)
goto err;
init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
ret = request_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_irq, IRQF_SHARED,
"fsl-ifc", fsl_ifc_ctrl_dev);
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->irq);
goto err_irq;
}
if (fsl_ifc_ctrl_dev->nand_irq) {
ret = request_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_nand_irq,
0, "fsl-ifc-nand", fsl_ifc_ctrl_dev);
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->nand_irq);
goto err_nandirq;
}
}
return 0;
err_nandirq:
free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev);
irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
err_irq:
free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
err:
return ret;
}
static const struct of_device_id fsl_ifc_match[] = {
{
.compatible = "fsl,ifc",
},
{},
};
static struct platform_driver fsl_ifc_ctrl_driver = {
.driver = {
.name = "fsl-ifc",
.of_match_table = fsl_ifc_match,
},
.probe = fsl_ifc_ctrl_probe,
.remove = fsl_ifc_ctrl_remove,
};
static int __init fsl_ifc_init(void)
{
return platform_driver_register(&fsl_ifc_ctrl_driver);
}
subsys_initcall(fsl_ifc_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("Freescale Integrated Flash Controller driver");
+391
View File
@@ -0,0 +1,391 @@
/*
* JZ4780 NAND/external memory controller (NEMC)
*
* Copyright (c) 2015 Imagination Technologies
* Author: Alex Smith <alex@alex-smith.me.uk>
*
* 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/init.h>
#include <linux/math64.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jz4780-nemc.h>
#define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4))
#define NEMC_NFCSR 0x50
#define NEMC_SMCR_SMT BIT(0)
#define NEMC_SMCR_BW_SHIFT 6
#define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT)
#define NEMC_SMCR_BW_8 (0 << 6)
#define NEMC_SMCR_TAS_SHIFT 8
#define NEMC_SMCR_TAS_MASK (0xf << NEMC_SMCR_TAS_SHIFT)
#define NEMC_SMCR_TAH_SHIFT 12
#define NEMC_SMCR_TAH_MASK (0xf << NEMC_SMCR_TAH_SHIFT)
#define NEMC_SMCR_TBP_SHIFT 16
#define NEMC_SMCR_TBP_MASK (0xf << NEMC_SMCR_TBP_SHIFT)
#define NEMC_SMCR_TAW_SHIFT 20
#define NEMC_SMCR_TAW_MASK (0xf << NEMC_SMCR_TAW_SHIFT)
#define NEMC_SMCR_TSTRV_SHIFT 24
#define NEMC_SMCR_TSTRV_MASK (0x3f << NEMC_SMCR_TSTRV_SHIFT)
#define NEMC_NFCSR_NFEn(n) BIT(((n) - 1) << 1)
#define NEMC_NFCSR_NFCEn(n) BIT((((n) - 1) << 1) + 1)
#define NEMC_NFCSR_TNFEn(n) BIT(16 + (n) - 1)
struct jz4780_nemc {
spinlock_t lock;
struct device *dev;
void __iomem *base;
struct clk *clk;
uint32_t clk_period;
unsigned long banks_present;
};
/**
* jz4780_nemc_num_banks() - count the number of banks referenced by a device
* @dev: device to count banks for, must be a child of the NEMC.
*
* Return: The number of unique NEMC banks referred to by the specified NEMC
* child device. Unique here means that a device that references the same bank
* multiple times in the its "reg" property will only count once.
*/
unsigned int jz4780_nemc_num_banks(struct device *dev)
{
const __be32 *prop;
unsigned int bank, count = 0;
unsigned long referenced = 0;
int i = 0;
while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) {
bank = of_read_number(prop, 1);
if (!(referenced & BIT(bank))) {
referenced |= BIT(bank);
count++;
}
}
return count;
}
EXPORT_SYMBOL(jz4780_nemc_num_banks);
/**
* jz4780_nemc_set_type() - set the type of device connected to a bank
* @dev: child device of the NEMC.
* @bank: bank number to configure.
* @type: type of device connected to the bank.
*/
void jz4780_nemc_set_type(struct device *dev, unsigned int bank,
enum jz4780_nemc_bank_type type)
{
struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
uint32_t nfcsr;
nfcsr = readl(nemc->base + NEMC_NFCSR);
/* TODO: Support toggle NAND devices. */
switch (type) {
case JZ4780_NEMC_BANK_SRAM:
nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank));
break;
case JZ4780_NEMC_BANK_NAND:
nfcsr &= ~NEMC_NFCSR_TNFEn(bank);
nfcsr |= NEMC_NFCSR_NFEn(bank);
break;
}
writel(nfcsr, nemc->base + NEMC_NFCSR);
}
EXPORT_SYMBOL(jz4780_nemc_set_type);
/**
* jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin
* @dev: child device of the NEMC.
* @bank: bank number of device.
* @assert: whether the chip enable pin should be asserted.
*
* (De-)asserts the chip enable pin for the NAND device connected to the
* specified bank.
*/
void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert)
{
struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
uint32_t nfcsr;
nfcsr = readl(nemc->base + NEMC_NFCSR);
if (assert)
nfcsr |= NEMC_NFCSR_NFCEn(bank);
else
nfcsr &= ~NEMC_NFCSR_NFCEn(bank);
writel(nfcsr, nemc->base + NEMC_NFCSR);
}
EXPORT_SYMBOL(jz4780_nemc_assert);
static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc)
{
unsigned long rate;
rate = clk_get_rate(nemc->clk);
if (!rate)
return 0;
/* Return in picoseconds. */
return div64_ul(1000000000000ull, rate);
}
static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns)
{
return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period;
}
static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc,
unsigned int bank,
struct device_node *node)
{
uint32_t smcr, val, cycles;
/*
* Conversion of tBP and tAW cycle counts to values supported by the
* hardware (round up to the next supported value).
*/
static const uint32_t convert_tBP_tAW[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
/* 11 - 12 -> 12 cycles */
11, 11,
/* 13 - 15 -> 15 cycles */
12, 12, 12,
/* 16 - 20 -> 20 cycles */
13, 13, 13, 13, 13,
/* 21 - 25 -> 25 cycles */
14, 14, 14, 14, 14,
/* 26 - 31 -> 31 cycles */
15, 15, 15, 15, 15, 15
};
smcr = readl(nemc->base + NEMC_SMCRn(bank));
smcr &= ~NEMC_SMCR_SMT;
if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) {
smcr &= ~NEMC_SMCR_BW_MASK;
switch (val) {
case 8:
smcr |= NEMC_SMCR_BW_8;
break;
default:
/*
* Earlier SoCs support a 16 bit bus width (the 4780
* does not), until those are properly supported, error.
*/
dev_err(nemc->dev, "unsupported bus width: %u\n", val);
return false;
}
}
if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) {
smcr &= ~NEMC_SMCR_TAS_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 15) {
dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TAS_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) {
smcr &= ~NEMC_SMCR_TAH_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 15) {
dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TAH_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) {
smcr &= ~NEMC_SMCR_TBP_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 31) {
dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) {
smcr &= ~NEMC_SMCR_TAW_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 31) {
dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT;
}
if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) {
smcr &= ~NEMC_SMCR_TSTRV_MASK;
cycles = jz4780_nemc_ns_to_cycles(nemc, val);
if (cycles > 63) {
dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n",
val, cycles);
return false;
}
smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT;
}
writel(smcr, nemc->base + NEMC_SMCRn(bank));
return true;
}
static int jz4780_nemc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct jz4780_nemc *nemc;
struct resource *res;
struct device_node *child;
const __be32 *prop;
unsigned int bank;
unsigned long referenced;
int i, ret;
nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL);
if (!nemc)
return -ENOMEM;
spin_lock_init(&nemc->lock);
nemc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nemc->base = devm_ioremap_resource(dev, res);
if (IS_ERR(nemc->base)) {
dev_err(dev, "failed to get I/O memory\n");
return PTR_ERR(nemc->base);
}
writel(0, nemc->base + NEMC_NFCSR);
nemc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(nemc->clk)) {
dev_err(dev, "failed to get clock\n");
return PTR_ERR(nemc->clk);
}
ret = clk_prepare_enable(nemc->clk);
if (ret) {
dev_err(dev, "failed to enable clock: %d\n", ret);
return ret;
}
nemc->clk_period = jz4780_nemc_clk_period(nemc);
if (!nemc->clk_period) {
dev_err(dev, "failed to calculate clock period\n");
clk_disable_unprepare(nemc->clk);
return -EINVAL;
}
/*
* Iterate over child devices, check that they do not conflict with
* each other, and register child devices for them. If a child device
* has invalid properties, it is ignored and no platform device is
* registered for it.
*/
for_each_child_of_node(nemc->dev->of_node, child) {
referenced = 0;
i = 0;
while ((prop = of_get_address(child, i++, NULL, NULL))) {
bank = of_read_number(prop, 1);
if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) {
dev_err(nemc->dev,
"%pOF requests invalid bank %u\n",
child, bank);
/* Will continue the outer loop below. */
referenced = 0;
break;
}
referenced |= BIT(bank);
}
if (!referenced) {
dev_err(nemc->dev, "%pOF has no addresses\n",
child);
continue;
} else if (nemc->banks_present & referenced) {
dev_err(nemc->dev, "%pOF conflicts with another node\n",
child);
continue;
}
/* Configure bank parameters. */
for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) {
if (!jz4780_nemc_configure_bank(nemc, bank, child)) {
referenced = 0;
break;
}
}
if (referenced) {
if (of_platform_device_create(child, NULL, nemc->dev))
nemc->banks_present |= referenced;
}
}
platform_set_drvdata(pdev, nemc);
dev_info(dev, "JZ4780 NEMC initialised\n");
return 0;
}
static int jz4780_nemc_remove(struct platform_device *pdev)
{
struct jz4780_nemc *nemc = platform_get_drvdata(pdev);
clk_disable_unprepare(nemc->clk);
return 0;
}
static const struct of_device_id jz4780_nemc_dt_match[] = {
{ .compatible = "ingenic,jz4780-nemc" },
{},
};
static struct platform_driver jz4780_nemc_driver = {
.probe = jz4780_nemc_probe,
.remove = jz4780_nemc_remove,
.driver = {
.name = "jz4780-nemc",
.of_match_table = of_match_ptr(jz4780_nemc_dt_match),
},
};
static int __init jz4780_nemc_init(void)
{
return platform_driver_register(&jz4780_nemc_driver);
}
subsys_initcall(jz4780_nemc_init);
+52
View File
@@ -0,0 +1,52 @@
config MEDIATEK_MC
bool "MediaTek Memory Controller support"
depends on MEMORY
select MEDIATEK_DRAMC
select MEDIATEK_EMI
help
This driver supports the Memory Controller (MC) hardware found on
MediaTek(R) SoCs. The config selects MEDIATEK_DRAMC and MEDIATEK_EMI
for the control of DRAMC and EMI drivers respectively. This config
will be enabled by default for MTK SoC.
config MEDIATEK_DRAMC
tristate "MediaTek DRAMC driver"
help
This selects the MediaTek(R) DRAMC driver.
Provide the API for DRAMC low power scenario, and the interface
for reporting DRAM information, e.g. DRAM mode register (MR) for
DRAM vendor ID, temperature, and density.
config HAVE_MTK_MC
bool "MediaTek Memory Controller support"
depends on ARCH_MEDIATEK
help
This driver supports the Memory Controller (MC) hardware found on
MediaTek(R) SoCs. The config enables MTK_DRAMC and MTK_EMI
for the control of DRAMC and EMI drivers respectively. This config
will be enabled by default for MTK SoC.
config MTK_DRAMC
tristate "MediaTek DRAMC driver"
depends on HAVE_MTK_MC
help
This selects the MediaTek(R) DRAMC driver.
Provide the API for DRAMC low power scenario, and the interface
for reporting DRAM information, e.g. DRAM mode register (MR) for
DRAM vendor ID, temperature, and density.
config MTK_EMI
tristate "MediaTek EMI driver"
depends on HAVE_MTK_MC
help
This selects the MediaTek(R) EMI driver.
Provide the API for MPU registration, EMI MPU violation handling,
the API for EMI information, the BWL golden setting,
and the sysfs for EMI ISU control.
config MEDIATEK_EMI
tristate "MediaTek EMI driver"
help
This selects the MediaTek(R) EMI driver.
Provide the API for MPU register and handle EMI MPU violation,
the API for EMI information, and the sysfs for EMI ISU control.
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include
obj-$(CONFIG_MEDIATEK_DRAMC) += dramc_old.o
obj-$(CONFIG_MEDIATEK_DRAMC) += dramc_bin_old.o
ifeq (,$(filter $(CONFIG_MTK_PLATFORM), "mt6768" "mt6739" "mt6785"))
obj-$(CONFIG_MTK_DRAMC) += dramc.o
obj-$(CONFIG_MTK_DRAMC) += dramc_bin.o
endif
obj-$(CONFIG_MEDIATEK_EMI) += emicen_old.o
obj-$(CONFIG_MEDIATEK_EMI) += emimpu_old.o
obj-$(CONFIG_MEDIATEK_EMI) += emiisu_old.o
ifeq (,$(filter $(CONFIG_MTK_PLATFORM), "mt6768" "mt6785"))
obj-$(CONFIG_MTK_EMI) += emicen.o
obj-$(CONFIG_MTK_EMI) += emimpu.o
obj-$(CONFIG_MTK_EMI) += emiisu.o
endif
+776
View File
@@ -0,0 +1,776 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <memory/mediatek/dramc.h>
static struct platform_device *dramc_pdev;
static int mr4_v1_init(struct platform_device *pdev,
struct mr4_dev_t *mr4_dev_ptr)
{
struct device_node *dramc_node = pdev->dev.of_node;
int ret;
mr4_dev_ptr->version = 1;
ret = of_property_read_u32_array(dramc_node,
"mr4_rg", (unsigned int *)&(mr4_dev_ptr->mr4_rg), 3);
return ret;
}
static int fmeter_v0_init(struct platform_device *pdev,
struct fmeter_dev_t *fmeter_dev_ptr)
{
struct device_node *dramc_node = pdev->dev.of_node;
int ret;
fmeter_dev_ptr->version = 0;
ret = of_property_read_u32(dramc_node,
"crystal_freq", &(fmeter_dev_ptr->crystal_freq));
ret |= of_property_read_u32(dramc_node,
"shu_of", &(fmeter_dev_ptr->shu_of));
ret |= of_property_read_u32_array(dramc_node,
"shu_lv", (unsigned int *)&(fmeter_dev_ptr->shu_lv), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_id", (unsigned int *)&(fmeter_dev_ptr->pll_id), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_md", (unsigned int *)(fmeter_dev_ptr->pll_md), 6);
ret |= of_property_read_u32_array(dramc_node,
"sdmpcw", (unsigned int *)(fmeter_dev_ptr->sdmpcw), 6);
ret |= of_property_read_u32_array(dramc_node,
"prediv", (unsigned int *)(fmeter_dev_ptr->prediv), 6);
ret |= of_property_read_u32_array(dramc_node,
"posdiv", (unsigned int *)(fmeter_dev_ptr->posdiv), 6);
ret |= of_property_read_u32_array(dramc_node,
"ckdiv4", (unsigned int *)(fmeter_dev_ptr->ckdiv4), 6);
ret |= of_property_read_u32_array(dramc_node,
"cldiv2", (unsigned int *)(fmeter_dev_ptr->cldiv2), 6);
return ret;
}
static int fmeter_v1_init(struct platform_device *pdev,
struct fmeter_dev_t *fmeter_dev_ptr)
{
struct device_node *dramc_node = pdev->dev.of_node;
int ret;
fmeter_dev_ptr->version = 1;
ret = of_property_read_u32(dramc_node,
"crystal_freq", &(fmeter_dev_ptr->crystal_freq));
ret |= of_property_read_u32(dramc_node,
"shu_of", &(fmeter_dev_ptr->shu_of));
ret |= of_property_read_u32_array(dramc_node,
"shu_lv", (unsigned int *)&(fmeter_dev_ptr->shu_lv), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_id", (unsigned int *)&(fmeter_dev_ptr->pll_id), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_md", (unsigned int *)(fmeter_dev_ptr->pll_md), 6);
ret |= of_property_read_u32_array(dramc_node,
"sdmpcw", (unsigned int *)(fmeter_dev_ptr->sdmpcw), 6);
ret |= of_property_read_u32_array(dramc_node,
"prediv", (unsigned int *)(fmeter_dev_ptr->prediv), 6);
ret |= of_property_read_u32_array(dramc_node,
"posdiv", (unsigned int *)(fmeter_dev_ptr->posdiv), 6);
ret |= of_property_read_u32_array(dramc_node,
"ckdiv4", (unsigned int *)(fmeter_dev_ptr->ckdiv4), 6);
ret |= of_property_read_u32_array(dramc_node,
"cldiv2", (unsigned int *)(fmeter_dev_ptr->cldiv2), 6);
ret |= of_property_read_u32_array(dramc_node,
"fbksel", (unsigned int *)(fmeter_dev_ptr->fbksel), 6);
ret |= of_property_read_u32_array(dramc_node,
"dqopen", (unsigned int *)(fmeter_dev_ptr->dqopen), 6);
return ret;
}
static ssize_t mr_show(struct device_driver *driver, char *buf)
{
struct dramc_dev_t *dramc_dev_ptr;
struct mr_info_t *mr_info_ptr;
unsigned int i;
ssize_t ret;
if (!dramc_pdev)
return strlen(buf);
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mr%d: 0x%x\n",
mr_info_ptr[i].mr_index, mr_info_ptr[i].mr_value);
if (ret >= PAGE_SIZE)
return strlen(buf);
}
return strlen(buf);
}
static ssize_t mr4_show(struct device_driver *driver, char *buf)
{
struct dramc_dev_t *dramc_dev_ptr;
unsigned int i;
ssize_t ret;
if (!dramc_pdev)
return strlen(buf);
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
for (ret = 0, i = 0; i < dramc_dev_ptr->ch_cnt; i++) {
ret += snprintf(buf + ret, PAGE_SIZE - ret,
"mr4: ch%d 0x%x\n", i, mtk_dramc_get_mr4(i));
if (ret >= PAGE_SIZE)
return strlen(buf);
}
return strlen(buf);
}
static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
{
return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
mtk_dramc_get_data_rate());
}
__weak int mtk_dramc_binning_test(void)
{
return 0;
}
static ssize_t binning_test_show(struct device_driver *driver, char *buf)
{
int ret;
ret = mtk_dramc_binning_test();
if (!ret)
return snprintf(buf, PAGE_SIZE, "unsupport mem test\n");
else if (ret > 0)
return snprintf(buf, PAGE_SIZE, "mem test all pass\n");
else
return snprintf(buf, PAGE_SIZE, "mem test failed %d\n", ret);
}
static DRIVER_ATTR_RO(mr);
static DRIVER_ATTR_RO(mr4);
static DRIVER_ATTR_RO(dram_data_rate);
static DRIVER_ATTR_RO(binning_test);
static int dramc_probe(struct platform_device *pdev)
{
struct device_node *dramc_node = pdev->dev.of_node;
struct dramc_dev_t *dramc_dev_ptr;
unsigned int mr4_version;
unsigned int fmeter_version;
struct resource *res;
unsigned int i, size;
int ret;
pr_info("%s: module probe.\n", __func__);
dramc_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct dramc_dev_t), GFP_KERNEL);
if (!dramc_dev_ptr)
return -ENOMEM;
ret = of_property_read_u32(dramc_node,
"dram_type", &(dramc_dev_ptr->dram_type));
if (ret) {
pr_err("%s: get dram_type fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"support_ch_cnt", &(dramc_dev_ptr->support_ch_cnt));
if (ret) {
pr_err("%s: get support_ch_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"ch_cnt", &(dramc_dev_ptr->ch_cnt));
if (ret) {
pr_err("%s: get ch_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"rk_cnt", &(dramc_dev_ptr->rk_cnt));
if (ret) {
pr_err("%s: get rk_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"mr_cnt", &(dramc_dev_ptr->mr_cnt));
if (ret) {
pr_err("%s: get mr_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"freq_cnt", &(dramc_dev_ptr->freq_cnt));
if (ret) {
pr_err("%s: get freq_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node, "mr4_version", &mr4_version);
if (ret)
pr_info("%s: not support mr4\n", __func__);
else if (mr4_version == 1) {
dramc_dev_ptr->mr4_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct mr4_dev_t), GFP_KERNEL);
if (!(dramc_dev_ptr->mr4_dev_ptr))
return -ENOMEM;
ret = mr4_v1_init(pdev,
(struct mr4_dev_t *)(dramc_dev_ptr->mr4_dev_ptr));
if (ret) {
pr_err("%s: mr4_v1_init fail\n", __func__);
return -EINVAL;
}
} else
dramc_dev_ptr->mr4_dev_ptr = NULL;
pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
__func__,
"dram_type", dramc_dev_ptr->dram_type,
"support_ch_cnt", dramc_dev_ptr->support_ch_cnt,
"ch_cnt", dramc_dev_ptr->ch_cnt,
"rk_cnt", dramc_dev_ptr->rk_cnt,
"mr_cnt", dramc_dev_ptr->mr_cnt,
"freq_cnt", dramc_dev_ptr->freq_cnt,
"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
size = sizeof(unsigned int) * dramc_dev_ptr->rk_cnt;
dramc_dev_ptr->rk_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
if (!(dramc_dev_ptr->rk_size))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node,
"rk_size", dramc_dev_ptr->rk_size, dramc_dev_ptr->rk_cnt);
if (ret) {
pr_err("%s: get rk_size fail\n", __func__);
return -EINVAL;
}
size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->mr_info_ptr))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node, "mr",
(unsigned int *)dramc_dev_ptr->mr_info_ptr, size >> 2);
if (ret) {
pr_err("%s: get mr_info fail\n", __func__);
return -EINVAL;
}
for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
pr_info("%s: mr%d(%x)\n", __func__,
dramc_dev_ptr->mr_info_ptr[i].mr_index,
dramc_dev_ptr->mr_info_ptr[i].mr_value);
size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt;
dramc_dev_ptr->freq_step = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
if (!(dramc_dev_ptr->freq_step))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node, "freq_step",
dramc_dev_ptr->freq_step, dramc_dev_ptr->freq_cnt);
if (ret) {
pr_err("%s: get freq_step fail\n", __func__);
return -EINVAL;
}
dramc_dev_ptr->sleep_base = of_iomap(dramc_node,
dramc_dev_ptr->support_ch_cnt * 4);
if (IS_ERR(dramc_dev_ptr->sleep_base)) {
pr_err("%s: unable to map sleep base\n", __func__);
return -EINVAL;
}
size = sizeof(phys_addr_t) * dramc_dev_ptr->support_ch_cnt;
dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->dramc_chn_base_ao))
return -ENOMEM;
dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->dramc_chn_base_nao))
return -ENOMEM;
dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
return -ENOMEM;
dramc_dev_ptr->ddrphy_chn_base_nao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->ddrphy_chn_base_nao))
return -ENOMEM;
for (i = 0; i < dramc_dev_ptr->support_ch_cnt; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
dramc_dev_ptr->dramc_chn_base_ao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
pr_err("%s: unable to map ch%d DRAMC AO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt);
dramc_dev_ptr->dramc_chn_base_nao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
pr_err("%s: unable to map ch%d DRAMC NAO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt * 2);
dramc_dev_ptr->ddrphy_chn_base_ao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
pr_err("%s: unable to map ch%d DDRPHY AO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt * 3);
dramc_dev_ptr->ddrphy_chn_base_nao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_nao[i])) {
pr_err("%s: unable to map ch%d DDRPHY NAO base\n",
__func__, i);
return -EINVAL;
}
}
ret = of_property_read_u32(
dramc_node, "fmeter_version", &fmeter_version);
if (ret) {
pr_err("%s: get fmeter_version fail\n", __func__);
return -EINVAL;
}
pr_info("%s: fmeter_version(%d)\n", __func__, fmeter_version);
dramc_dev_ptr->fmeter_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct fmeter_dev_t), GFP_KERNEL);
if (!(dramc_dev_ptr->fmeter_dev_ptr)) {
pr_info("%s: memory alloc fail\n", __func__);
return -ENOMEM;
}
switch (fmeter_version) {
case 0:
ret = fmeter_v0_init(pdev, dramc_dev_ptr->fmeter_dev_ptr);
if (ret) {
pr_err("%s: fmeter_v0_init fail\n", __func__);
return -EINVAL;
}
break;
case 1:
ret = fmeter_v1_init(pdev, dramc_dev_ptr->fmeter_dev_ptr);
if (ret) {
pr_err("%s: fmeter_v1_init fail\n", __func__);
return -EINVAL;
}
break;
default:
devm_kfree(&pdev->dev, dramc_dev_ptr->fmeter_dev_ptr);
dramc_dev_ptr->fmeter_dev_ptr = NULL;
}
ret = driver_create_file(
pdev->dev.driver, &driver_attr_binning_test);
if (ret) {
pr_info("%s: fail to create binning_test sysfs\n", __func__);
return ret;
}
ret = driver_create_file(
pdev->dev.driver, &driver_attr_dram_data_rate);
if (ret) {
pr_err("%s: fail to create dram_data_rate sysfs\n", __func__);
return ret;
}
ret = driver_create_file(
pdev->dev.driver, &driver_attr_mr);
if (ret) {
pr_err("%s: fail to create mr sysfs\n", __func__);
return ret;
}
if (dramc_dev_ptr->mr4_dev_ptr) {
ret = driver_create_file(
pdev->dev.driver, &driver_attr_mr4);
if (ret) {
pr_err("%s: fail to create mr4 sysfs\n", __func__);
return ret;
}
}
platform_set_drvdata(pdev, dramc_dev_ptr);
dramc_pdev = pdev;
pr_info("%s: DRAM data type = %d\n", __func__,
mtk_dramc_get_ddr_type());
pr_info("%s: DRAM data rate = %d\n", __func__,
mtk_dramc_get_data_rate());
return ret;
}
static int dramc_remove(struct platform_device *dev)
{
return 0;
}
static const struct of_device_id dramc_of_ids[] = {
{.compatible = "mediatek,common-dramc",},
{}
};
static struct platform_driver dramc_drv = {
.probe = dramc_probe,
.remove = dramc_remove,
.driver = {
.name = "dramc_drv",
.owner = THIS_MODULE,
.of_match_table = dramc_of_ids,
},
};
static int __init dramc_drv_init(void)
{
int ret;
ret = platform_driver_register(&dramc_drv);
if (ret) {
pr_err("%s: init fail, ret 0x%x\n", __func__, ret);
return ret;
}
return ret;
}
static void __exit dramc_drv_exit(void)
{
platform_driver_unregister(&dramc_drv);
}
module_init(dramc_drv_init);
module_exit(dramc_drv_exit);
/*
* mtk_dramc_get_steps_freq - get the freq of target DVFS step
* @step: the step index of DVFS
*
* Returns the DRAM freq
*/
int mtk_dramc_get_steps_freq(unsigned int step)
{
struct dramc_dev_t *dramc_dev_ptr;
if (!dramc_pdev)
return -1;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
if (step < dramc_dev_ptr->freq_cnt)
return dramc_dev_ptr->freq_step[step];
return -1;
}
EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
static unsigned int decode_freq(unsigned int vco_freq)
{
switch (vco_freq) {
case 4264:
return 4266;
case 3718:
return 3733;
case 3094:
case 3068:
return 3200;
case 2392:
case 2366:
return 2400;
case 1859:
return 1866;
case 1534:
return 1600;
case 1196:
case 1144:
return 1200;
case 819:
case 754:
return 800;
}
return vco_freq;
}
static unsigned int fmeter_v0(struct dramc_dev_t *dramc_dev_ptr)
{
struct fmeter_dev_t *fmeter_dev_ptr =
(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
unsigned int shu_lv_val;
unsigned int pll_id_val;
unsigned int pll_md_val;
unsigned int sdmpcw_val;
unsigned int prediv_val;
unsigned int posdiv_val;
unsigned int ckdiv4_val;
unsigned int cldiv2_val;
unsigned int offset;
unsigned int vco_freq;
shu_lv_val = (readl(dramc_dev_ptr->dramc_chn_base_ao[0] +
fmeter_dev_ptr->shu_lv.offset) &
fmeter_dev_ptr->shu_lv.mask) >>
fmeter_dev_ptr->shu_lv.shift;
pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
fmeter_dev_ptr->pll_id.offset) &
fmeter_dev_ptr->pll_id.mask) >>
fmeter_dev_ptr->pll_id.shift;
offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
fmeter_dev_ptr->pll_md[pll_id_val].shift;
offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->prediv[pll_id_val].mask) >>
fmeter_dev_ptr->prediv[pll_id_val].shift;
offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
fmeter_dev_ptr->posdiv[pll_id_val].shift;
offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
fmeter_dev_ptr->cldiv2[pll_id_val].shift;
vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
(sdmpcw_val >> 8)) >> posdiv_val >> ckdiv4_val >>
pll_md_val >> cldiv2_val;
return decode_freq(vco_freq);
}
static unsigned int fmeter_v1(struct dramc_dev_t *dramc_dev_ptr)
{
struct fmeter_dev_t *fmeter_dev_ptr =
(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
unsigned int shu_lv_val;
unsigned int pll_id_val;
unsigned int pll_md_val;
unsigned int sdmpcw_val;
unsigned int prediv_val;
unsigned int posdiv_val;
unsigned int ckdiv4_val;
unsigned int cldiv2_val;
unsigned int offset;
unsigned int vco_freq;
unsigned int fbksel;
unsigned int dqopen;
shu_lv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] +
fmeter_dev_ptr->shu_lv.offset) &
fmeter_dev_ptr->shu_lv.mask) >>
fmeter_dev_ptr->shu_lv.shift;
pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] +
fmeter_dev_ptr->pll_id.offset) &
fmeter_dev_ptr->pll_id.mask) >>
fmeter_dev_ptr->pll_id.shift;
offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
fmeter_dev_ptr->pll_md[pll_id_val].shift;
offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->prediv[pll_id_val].mask) >>
fmeter_dev_ptr->prediv[pll_id_val].shift;
offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
fmeter_dev_ptr->posdiv[pll_id_val].shift;
offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
fmeter_dev_ptr->cldiv2[pll_id_val].shift;
offset = fmeter_dev_ptr->fbksel[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
fbksel = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->fbksel[pll_id_val].mask) >>
fmeter_dev_ptr->fbksel[pll_id_val].shift;
offset = fmeter_dev_ptr->dqopen[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
dqopen = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->dqopen[pll_id_val].mask) >>
fmeter_dev_ptr->dqopen[pll_id_val].shift;
vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
(sdmpcw_val >> 8)) >> posdiv_val >> ckdiv4_val >>
pll_md_val >> cldiv2_val << fbksel >> (dqopen << 1);
return decode_freq(vco_freq);
}
/*
* mtk_dramc_get_data_rate - calculate DRAM data rate
*
* Returns DRAM data rate (MB/s)
*/
unsigned int mtk_dramc_get_data_rate(void)
{
struct dramc_dev_t *dramc_dev_ptr;
struct fmeter_dev_t *fmeter_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
if (!fmeter_dev_ptr)
return 0;
switch (fmeter_dev_ptr->version) {
case 0:
return fmeter_v0(dramc_dev_ptr);
case 1:
return fmeter_v1(dramc_dev_ptr);
}
return 0;
}
EXPORT_SYMBOL(mtk_dramc_get_data_rate);
static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
{
struct mr4_dev_t *mr4_dev_ptr =
(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
mr4_dev_ptr->mr4_rg.shift;
}
/*
* mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
* @ch: the channel index
*
* Returns the MR4 value
*/
unsigned int mtk_dramc_get_mr4(unsigned int ch)
{
struct dramc_dev_t *dramc_dev_ptr;
struct mr4_dev_t *mr4_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
if (!mr4_dev_ptr)
return 0;
if (ch >= dramc_dev_ptr->ch_cnt)
return 0;
if (mr4_dev_ptr->version == 1)
return mr4_v1(dramc_dev_ptr, ch);
return 0;
}
EXPORT_SYMBOL(mtk_dramc_get_mr4);
/*
* mtk_dramc_get_ddr_type - get DRAM type
*
* Returns the DRAM type
*/
unsigned int mtk_dramc_get_ddr_type(void)
{
struct dramc_dev_t *dramc_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
return dramc_dev_ptr->dram_type;
}
EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
MODULE_DESCRIPTION("MediaTek DRAMC Driver v0.1");
+373
View File
@@ -0,0 +1,373 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/printk.h>
#include <linux/vmalloc.h>
#define MEM_TEST_SIZE 0x2000
#define PATTERN1 0x5A5A5A5A
#define PATTERN2 0xA5A5A5A5
int mtk_dramc_binning_test(void)
{
unsigned char *mem8_base;
unsigned short *mem16_base;
unsigned int *mem32_base;
unsigned int *mem_base;
unsigned long mem_ptr;
unsigned char pattern8;
unsigned short pattern16;
unsigned int i, j, size, pattern32;
unsigned int value;
unsigned int len = MEM_TEST_SIZE;
void *ptr;
int ret = 1;
ptr = vmalloc(len);
if (!ptr) {
ret = -24;
goto fail;
}
mem8_base = (unsigned char *)ptr;
mem16_base = (unsigned short *)ptr;
mem32_base = (unsigned int *)ptr;
mem_base = (unsigned int *)ptr;
/* pr_info("Test DRAM start address 0x%lx\n", (unsigned long)ptr); */
pr_info("Test DRAM start address %p\n", ptr);
pr_info("Test DRAM SIZE 0x%x\n", len);
size = len >> 2;
/* === test the tied bits (tied high) === */
for (i = 0; i < size; i++)
mem32_base[i] = 0;
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0) {
/* return -1; */
ret = -1;
goto fail;
} else
mem32_base[i] = 0xffffffff;
}
/* === test the tied bits (tied low) === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffffffff) {
/* return -2; */
ret = -2;
goto fail;
} else
mem32_base[i] = 0x00;
}
/* === test pattern 1 (0x00~0xff) === */
pattern8 = 0x00;
for (i = 0; i < len; i++)
mem8_base[i] = pattern8++;
pattern8 = 0x00;
for (i = 0; i < len; i++) {
if (mem8_base[i] != pattern8++) {
/* return -3; */
ret = -3;
goto fail;
}
}
/* === test pattern 2 (0x00~0xff) === */
pattern8 = 0x00;
for (i = j = 0; i < len; i += 2, j++) {
if (mem8_base[i] == pattern8)
mem16_base[j] = pattern8;
if (mem16_base[j] != pattern8) {
/* return -4; */
ret = -4;
goto fail;
}
pattern8 += 2;
}
/* === test pattern 3 (0x00~0xffff) === */
pattern16 = 0x00;
for (i = 0; i < (len >> 1); i++)
mem16_base[i] = pattern16++;
pattern16 = 0x00;
for (i = 0; i < (len >> 1); i++) {
if (mem16_base[i] != pattern16++) {
/* return -5; */
ret = -5;
goto fail;
}
}
/* === test pattern 4 (0x00~0xffffffff) === */
pattern32 = 0x00;
for (i = 0; i < (len >> 2); i++)
mem32_base[i] = pattern32++;
pattern32 = 0x00;
for (i = 0; i < (len >> 2); i++) {
if (mem32_base[i] != pattern32++) {
/* return -6; */
ret = -6;
goto fail;
}
}
/* === Pattern 5: Filling memory range with 0x44332211 === */
for (i = 0; i < size; i++)
mem32_base[i] = 0x44332211;
/* === Read Check then Fill Memory with a5a5a5a5 Pattern === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0x44332211) {
/* return -7; */
ret = -7;
goto fail;
} else {
mem32_base[i] = 0xa5a5a5a5;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 0h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5a5a5a5) {
/* return -8; */
ret = -8;
goto fail;
} else {
mem8_base[i * 4] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 2h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5a5a500) {
/* return -9; */
ret = -9;
goto fail;
} else {
mem8_base[i * 4 + 2] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 1h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa500a500) {
/* return -10; */
ret = -10;
goto fail;
} else {
mem8_base[i * 4 + 1] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 3h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5000000) {
/* return -11; */
ret = -11;
goto fail;
} else {
mem8_base[i * 4 + 3] = 0x00;
}
}
/* === Read Check then Fill Memory with ffff */
/* Word Pattern at offset 1h == */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0x00000000) {
/* return -12; */
ret = -12;
goto fail;
} else {
mem16_base[i * 2 + 1] = 0xffff;
}
}
/* === Read Check then Fill Memory with ffff */
/* Word Pattern at offset 0h == */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffff0000) {
/* return -13; */
ret = -13;
goto fail;
} else {
mem16_base[i * 2] = 0xffff;
}
}
/*=== Read Check === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffffffff) {
/* return -14; */
ret = -14;
goto fail;
}
}
/************************************************
* Additional verification
************************************************/
/* === stage 1 => write 0 === */
for (i = 0; i < size; i++)
mem_base[i] = PATTERN1;
/* === stage 2 => read 0, write 0xF === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -15; */
ret = -15;
goto fail;
}
mem_base[i] = PATTERN2;
}
/* === stage 3 => read 0xF, write 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN2) {
/* return -16; */
ret = -16;
goto fail;
}
mem_base[i] = PATTERN1;
}
/* === stage 4 => read 0, write 0xF === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -17; */
ret = -17;
goto fail;
}
mem_base[i] = PATTERN2;
}
/* === stage 5 => read 0xF, write 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN2) {
/* return -18; */
ret = -18;
goto fail;
}
mem_base[i] = PATTERN1;
}
/* === stage 6 => read 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -19; */
ret = -19;
goto fail;
}
}
/* === 1/2/4-byte combination test === */
mem_ptr = (unsigned long)mem_base;
while (mem_ptr < ((unsigned long)mem_base + (size << 2))) {
*((unsigned char *)mem_ptr) = 0x78;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x56;
mem_ptr += 1;
*((unsigned short *)mem_ptr) = 0x1234;
mem_ptr += 2;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned short *)mem_ptr) = 0x5678;
mem_ptr += 2;
*((unsigned char *)mem_ptr) = 0x34;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x12;
mem_ptr += 1;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned char *)mem_ptr) = 0x78;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x56;
mem_ptr += 1;
*((unsigned short *)mem_ptr) = 0x1234;
mem_ptr += 2;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned short *)mem_ptr) = 0x5678;
mem_ptr += 2;
*((unsigned char *)mem_ptr) = 0x34;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x12;
mem_ptr += 1;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
}
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != 0x12345678) {
/* return -20; */
ret = -20;
goto fail;
}
}
/* === Verify pattern 1 (0x00~0xff) === */
pattern8 = 0x00;
mem8_base[0] = pattern8;
for (i = 0; i < size * 4; i++) {
unsigned char waddr8, raddr8;
waddr8 = i + 1;
raddr8 = i;
if (i < size * 4 - 1)
mem8_base[waddr8] = pattern8 + 1;
if (mem8_base[raddr8] != pattern8) {
/* return -21; */
ret = -21;
goto fail;
}
pattern8++;
}
/* === Verify pattern 2 (0x00~0xffff) === */
pattern16 = 0x00;
mem16_base[0] = pattern16;
for (i = 0; i < size * 2; i++) {
if (i < size * 2 - 1)
mem16_base[i + 1] = pattern16 + 1;
if (mem16_base[i] != pattern16) {
/* return -22; */
ret = -22;
goto fail;
}
pattern16++;
}
/* === Verify pattern 3 (0x00~0xffffffff) === */
pattern32 = 0x00;
mem32_base[0] = pattern32;
for (i = 0; i < size; i++) {
if (i < size - 1)
mem32_base[i + 1] = pattern32 + 1;
if (mem32_base[i] != pattern32) {
/* return -23; */
ret = -23;
goto fail;
}
pattern32++;
}
pr_info("complex R/W mem test pass\n");
fail:
vfree(ptr);
return ret;
}
+373
View File
@@ -0,0 +1,373 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/printk.h>
#include <linux/vmalloc.h>
#define MEM_TEST_SIZE 0x2000
#define PATTERN1 0x5A5A5A5A
#define PATTERN2 0xA5A5A5A5
int mtk_dramc_binning_test(void)
{
unsigned char *mem8_base;
unsigned short *mem16_base;
unsigned int *mem32_base;
unsigned int *mem_base;
unsigned long mem_ptr;
unsigned char pattern8;
unsigned short pattern16;
unsigned int i, j, size, pattern32;
unsigned int value;
unsigned int len = MEM_TEST_SIZE;
void *ptr;
int ret = 1;
ptr = vmalloc(len);
if (!ptr) {
ret = -24;
goto fail;
}
mem8_base = (unsigned char *)ptr;
mem16_base = (unsigned short *)ptr;
mem32_base = (unsigned int *)ptr;
mem_base = (unsigned int *)ptr;
/* pr_info("Test DRAM start address 0x%lx\n", (unsigned long)ptr); */
pr_info("Test DRAM start address %p\n", ptr);
pr_info("Test DRAM SIZE 0x%x\n", len);
size = len >> 2;
/* === test the tied bits (tied high) === */
for (i = 0; i < size; i++)
mem32_base[i] = 0;
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0) {
/* return -1; */
ret = -1;
goto fail;
} else
mem32_base[i] = 0xffffffff;
}
/* === test the tied bits (tied low) === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffffffff) {
/* return -2; */
ret = -2;
goto fail;
} else
mem32_base[i] = 0x00;
}
/* === test pattern 1 (0x00~0xff) === */
pattern8 = 0x00;
for (i = 0; i < len; i++)
mem8_base[i] = pattern8++;
pattern8 = 0x00;
for (i = 0; i < len; i++) {
if (mem8_base[i] != pattern8++) {
/* return -3; */
ret = -3;
goto fail;
}
}
/* === test pattern 2 (0x00~0xff) === */
pattern8 = 0x00;
for (i = j = 0; i < len; i += 2, j++) {
if (mem8_base[i] == pattern8)
mem16_base[j] = pattern8;
if (mem16_base[j] != pattern8) {
/* return -4; */
ret = -4;
goto fail;
}
pattern8 += 2;
}
/* === test pattern 3 (0x00~0xffff) === */
pattern16 = 0x00;
for (i = 0; i < (len >> 1); i++)
mem16_base[i] = pattern16++;
pattern16 = 0x00;
for (i = 0; i < (len >> 1); i++) {
if (mem16_base[i] != pattern16++) {
/* return -5; */
ret = -5;
goto fail;
}
}
/* === test pattern 4 (0x00~0xffffffff) === */
pattern32 = 0x00;
for (i = 0; i < (len >> 2); i++)
mem32_base[i] = pattern32++;
pattern32 = 0x00;
for (i = 0; i < (len >> 2); i++) {
if (mem32_base[i] != pattern32++) {
/* return -6; */
ret = -6;
goto fail;
}
}
/* === Pattern 5: Filling memory range with 0x44332211 === */
for (i = 0; i < size; i++)
mem32_base[i] = 0x44332211;
/* === Read Check then Fill Memory with a5a5a5a5 Pattern === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0x44332211) {
/* return -7; */
ret = -7;
goto fail;
} else {
mem32_base[i] = 0xa5a5a5a5;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 0h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5a5a5a5) {
/* return -8; */
ret = -8;
goto fail;
} else {
mem8_base[i * 4] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 2h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5a5a500) {
/* return -9; */
ret = -9;
goto fail;
} else {
mem8_base[i * 4 + 2] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 1h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa500a500) {
/* return -10; */
ret = -10;
goto fail;
} else {
mem8_base[i * 4 + 1] = 0x00;
}
}
/* === Read Check then Fill Memory with */
/* 00 Byte Pattern at offset 3h === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xa5000000) {
/* return -11; */
ret = -11;
goto fail;
} else {
mem8_base[i * 4 + 3] = 0x00;
}
}
/* === Read Check then Fill Memory with ffff */
/* Word Pattern at offset 1h == */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0x00000000) {
/* return -12; */
ret = -12;
goto fail;
} else {
mem16_base[i * 2 + 1] = 0xffff;
}
}
/* === Read Check then Fill Memory with ffff */
/* Word Pattern at offset 0h == */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffff0000) {
/* return -13; */
ret = -13;
goto fail;
} else {
mem16_base[i * 2] = 0xffff;
}
}
/*=== Read Check === */
for (i = 0; i < size; i++) {
if (mem32_base[i] != 0xffffffff) {
/* return -14; */
ret = -14;
goto fail;
}
}
/************************************************
* Additional verification
************************************************/
/* === stage 1 => write 0 === */
for (i = 0; i < size; i++)
mem_base[i] = PATTERN1;
/* === stage 2 => read 0, write 0xF === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -15; */
ret = -15;
goto fail;
}
mem_base[i] = PATTERN2;
}
/* === stage 3 => read 0xF, write 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN2) {
/* return -16; */
ret = -16;
goto fail;
}
mem_base[i] = PATTERN1;
}
/* === stage 4 => read 0, write 0xF === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -17; */
ret = -17;
goto fail;
}
mem_base[i] = PATTERN2;
}
/* === stage 5 => read 0xF, write 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN2) {
/* return -18; */
ret = -18;
goto fail;
}
mem_base[i] = PATTERN1;
}
/* === stage 6 => read 0 === */
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != PATTERN1) {
/* return -19; */
ret = -19;
goto fail;
}
}
/* === 1/2/4-byte combination test === */
mem_ptr = (unsigned long)mem_base;
while (mem_ptr < ((unsigned long)mem_base + (size << 2))) {
*((unsigned char *)mem_ptr) = 0x78;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x56;
mem_ptr += 1;
*((unsigned short *)mem_ptr) = 0x1234;
mem_ptr += 2;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned short *)mem_ptr) = 0x5678;
mem_ptr += 2;
*((unsigned char *)mem_ptr) = 0x34;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x12;
mem_ptr += 1;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned char *)mem_ptr) = 0x78;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x56;
mem_ptr += 1;
*((unsigned short *)mem_ptr) = 0x1234;
mem_ptr += 2;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
*((unsigned short *)mem_ptr) = 0x5678;
mem_ptr += 2;
*((unsigned char *)mem_ptr) = 0x34;
mem_ptr += 1;
*((unsigned char *)mem_ptr) = 0x12;
mem_ptr += 1;
*((unsigned int *)mem_ptr) = 0x12345678;
mem_ptr += 4;
}
for (i = 0; i < size; i++) {
value = mem_base[i];
if (value != 0x12345678) {
/* return -20; */
ret = -20;
goto fail;
}
}
/* === Verify pattern 1 (0x00~0xff) === */
pattern8 = 0x00;
mem8_base[0] = pattern8;
for (i = 0; i < size * 4; i++) {
unsigned char waddr8, raddr8;
waddr8 = i + 1;
raddr8 = i;
if (i < size * 4 - 1)
mem8_base[waddr8] = pattern8 + 1;
if (mem8_base[raddr8] != pattern8) {
/* return -21; */
ret = -21;
goto fail;
}
pattern8++;
}
/* === Verify pattern 2 (0x00~0xffff) === */
pattern16 = 0x00;
mem16_base[0] = pattern16;
for (i = 0; i < size * 2; i++) {
if (i < size * 2 - 1)
mem16_base[i + 1] = pattern16 + 1;
if (mem16_base[i] != pattern16) {
/* return -22; */
ret = -22;
goto fail;
}
pattern16++;
}
/* === Verify pattern 3 (0x00~0xffffffff) === */
pattern32 = 0x00;
mem32_base[0] = pattern32;
for (i = 0; i < size; i++) {
if (i < size - 1)
mem32_base[i + 1] = pattern32 + 1;
if (mem32_base[i] != pattern32) {
/* return -23; */
ret = -23;
goto fail;
}
pattern32++;
}
pr_info("complex R/W mem test pass\n");
fail:
vfree(ptr);
return ret;
}
+681
View File
@@ -0,0 +1,681 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <memory/mediatek/dramc.h>
#include <linux/bug.h>
static struct platform_device *dramc_pdev;
static struct platform_driver dramc_drv;
static int mr4_v1_init(struct platform_device *pdev,
struct mr4_dev_t *mr4_dev_ptr)
{
struct device_node *dramc_node = pdev->dev.of_node;
int ret;
mr4_dev_ptr->version = 1;
ret = of_property_read_u32_array(dramc_node,
"mr4_rg", (unsigned int *)&(mr4_dev_ptr->mr4_rg), 3);
return ret;
}
static int fmeter_v1_init(struct platform_device *pdev,
struct fmeter_dev_t *fmeter_dev_ptr)
{
struct device_node *dramc_node = pdev->dev.of_node;
int ret;
fmeter_dev_ptr->version = 1;
ret = of_property_read_u32(dramc_node,
"crystal_freq", &(fmeter_dev_ptr->crystal_freq));
ret |= of_property_read_u32(dramc_node,
"shu_of", &(fmeter_dev_ptr->shu_of));
ret |= of_property_read_u32_array(dramc_node,
"shu_lv", (unsigned int *)&(fmeter_dev_ptr->shu_lv), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_id", (unsigned int *)&(fmeter_dev_ptr->pll_id), 3);
ret |= of_property_read_u32_array(dramc_node,
"pll_md", (unsigned int *)(fmeter_dev_ptr->pll_md), 6);
ret |= of_property_read_u32_array(dramc_node,
"sdmpcw", (unsigned int *)(fmeter_dev_ptr->sdmpcw), 6);
ret |= of_property_read_u32_array(dramc_node,
"prediv", (unsigned int *)(fmeter_dev_ptr->prediv), 6);
ret |= of_property_read_u32_array(dramc_node,
"posdiv", (unsigned int *)(fmeter_dev_ptr->posdiv), 6);
ret |= of_property_read_u32_array(dramc_node,
"ckdiv4", (unsigned int *)(fmeter_dev_ptr->ckdiv4), 6);
ret |= of_property_read_u32_array(dramc_node,
"cldiv2", (unsigned int *)(fmeter_dev_ptr->cldiv2), 6);
ret |= of_property_read_u32_array(dramc_node,
"fbksel", (unsigned int *)(fmeter_dev_ptr->fbksel), 6);
ret |= of_property_read_u32_array(dramc_node,
"dqsopen", (unsigned int *)(fmeter_dev_ptr->dqsopen), 6);
ret |= of_property_read_u32_array(dramc_node,
"dqopen", (unsigned int *)(fmeter_dev_ptr->dqopen), 6);
ret |= of_property_read_u32_array(dramc_node,
"ckdiv4_ca", (unsigned int *)(fmeter_dev_ptr->ckdiv4_ca), 6);
return ret;
}
static ssize_t mr_show(struct device_driver *driver, char *buf)
{
struct dramc_dev_t *dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
struct mr_info_t *mr_info_ptr = dramc_dev_ptr->mr_info_ptr;
unsigned int i;
ssize_t ret;
for (ret = 0, i = 0; i < dramc_dev_ptr->mr_cnt; i++) {
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mr%d: 0x%x\n",
mr_info_ptr[i].mr_index, mr_info_ptr[i].mr_value);
if (ret >= PAGE_SIZE)
return strlen(buf);
}
return strlen(buf);
}
static ssize_t mr4_show(struct device_driver *driver, char *buf)
{
struct dramc_dev_t *dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
unsigned int i;
ssize_t ret;
for (ret = 0, i = 0; i < dramc_dev_ptr->ch_cnt; i++) {
ret += snprintf(buf + ret, PAGE_SIZE - ret,
"mr4: ch%d 0x%x\n", i, mtk_dramc_get_mr4(i));
if (ret >= PAGE_SIZE)
return strlen(buf);
}
return strlen(buf);
}
static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
{
return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
mtk_dramc_get_data_rate());
}
__weak int mtk_dramc_binning_test(void)
{
return 0;
}
static ssize_t binning_test_show(struct device_driver *driver, char *buf)
{
int ret;
ret = mtk_dramc_binning_test();
if (!ret)
return snprintf(buf, PAGE_SIZE, "unsupport mem test\n");
else if (ret > 0)
return snprintf(buf, PAGE_SIZE, "mem test all pass\n");
else
return snprintf(buf, PAGE_SIZE, "mem test failed %d\n", ret);
}
static DRIVER_ATTR_RO(mr);
static DRIVER_ATTR_RO(mr4);
static DRIVER_ATTR_RO(dram_data_rate);
static DRIVER_ATTR_RO(binning_test);
static int dramc_probe(struct platform_device *pdev)
{
struct device_node *dramc_node = pdev->dev.of_node;
struct dramc_dev_t *dramc_dev_ptr;
unsigned int mr4_version;
unsigned int fmeter_version;
struct resource *res;
unsigned int i, size;
int ret;
pr_info("%s: module probe.\n", __func__);
dramc_pdev = pdev;
dramc_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct dramc_dev_t), GFP_KERNEL);
if (!dramc_dev_ptr)
return -ENOMEM;
ret = of_property_read_u32(dramc_node,
"dram_type", &(dramc_dev_ptr->dram_type));
if (ret) {
pr_info("%s: get dram_type fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"support_ch_cnt", &(dramc_dev_ptr->support_ch_cnt));
if (ret) {
pr_info("%s: get support_ch_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"ch_cnt", &(dramc_dev_ptr->ch_cnt));
if (ret) {
pr_info("%s: get ch_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"rk_cnt", &(dramc_dev_ptr->rk_cnt));
if (ret) {
pr_info("%s: get rk_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"mr_cnt", &(dramc_dev_ptr->mr_cnt));
if (ret) {
pr_info("%s: get mr_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node,
"freq_cnt", &(dramc_dev_ptr->freq_cnt));
if (ret) {
pr_info("%s: get freq_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(dramc_node, "mr4_version", &mr4_version);
if (ret)
pr_info("%s: not support mr4\n", __func__);
else if (mr4_version == 1) {
dramc_dev_ptr->mr4_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct mr4_dev_t), GFP_KERNEL);
if (!(dramc_dev_ptr->mr4_dev_ptr))
return -ENOMEM;
ret = mr4_v1_init(pdev,
(struct mr4_dev_t *)(dramc_dev_ptr->mr4_dev_ptr));
if (ret) {
pr_info("%s: mr4_v1_init fail\n", __func__);
return -EINVAL;
}
} else
dramc_dev_ptr->mr4_dev_ptr = NULL;
pr_info("%s: %s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%d),%s(%s)\n",
__func__,
"dram_type", dramc_dev_ptr->dram_type,
"support_ch_cnt", dramc_dev_ptr->support_ch_cnt,
"ch_cnt", dramc_dev_ptr->ch_cnt,
"rk_cnt", dramc_dev_ptr->rk_cnt,
"mr_cnt", dramc_dev_ptr->mr_cnt,
"freq_cnt", dramc_dev_ptr->freq_cnt,
"mr4", (dramc_dev_ptr->mr4_dev_ptr) ? "true" : "false");
size = sizeof(unsigned int) * dramc_dev_ptr->rk_cnt;
dramc_dev_ptr->rk_size = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
if (!(dramc_dev_ptr->rk_size))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node,
"rk_size", dramc_dev_ptr->rk_size, dramc_dev_ptr->rk_cnt);
if (ret) {
pr_info("%s: get rk_size fail\n", __func__);
return -EINVAL;
}
size = sizeof(struct mr_info_t) * dramc_dev_ptr->mr_cnt;
dramc_dev_ptr->mr_info_ptr = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->mr_info_ptr))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node, "mr",
(unsigned int *)dramc_dev_ptr->mr_info_ptr, size >> 2);
if (ret) {
pr_info("%s: get mr_info fail\n", __func__);
return -EINVAL;
}
for (i = 0; i < dramc_dev_ptr->mr_cnt; i++)
pr_info("%s: mr%d(%x)\n", __func__,
dramc_dev_ptr->mr_info_ptr[i].mr_index,
dramc_dev_ptr->mr_info_ptr[i].mr_value);
size = sizeof(unsigned int) * dramc_dev_ptr->freq_cnt;
dramc_dev_ptr->freq_step = devm_kmalloc(&pdev->dev, size, GFP_KERNEL);
if (!(dramc_dev_ptr->freq_step))
return -ENOMEM;
ret = of_property_read_u32_array(dramc_node, "freq_step",
dramc_dev_ptr->freq_step, dramc_dev_ptr->freq_cnt);
if (ret) {
pr_info("%s: get freq_step fail\n", __func__);
return -EINVAL;
}
dramc_dev_ptr->sleep_base = of_iomap(dramc_node,
dramc_dev_ptr->support_ch_cnt * 4);
if (IS_ERR(dramc_dev_ptr->sleep_base)) {
pr_info("%s: unable to map sleep base\n", __func__);
return -EINVAL;
}
size = sizeof(phys_addr_t) * dramc_dev_ptr->support_ch_cnt;
dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->dramc_chn_base_ao))
return -ENOMEM;
dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->dramc_chn_base_nao))
return -ENOMEM;
dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
return -ENOMEM;
dramc_dev_ptr->ddrphy_chn_base_nao = devm_kmalloc(&pdev->dev,
size, GFP_KERNEL);
if (!(dramc_dev_ptr->ddrphy_chn_base_nao))
return -ENOMEM;
for (i = 0; i < dramc_dev_ptr->support_ch_cnt; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
dramc_dev_ptr->dramc_chn_base_ao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
pr_info("%s: unable to map ch%d DRAMC AO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt);
dramc_dev_ptr->dramc_chn_base_nao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
pr_info("%s: unable to map ch%d DRAMC NAO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt * 2);
dramc_dev_ptr->ddrphy_chn_base_ao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
pr_info("%s: unable to map ch%d DDRPHY AO base\n",
__func__, i);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM,
i + dramc_dev_ptr->support_ch_cnt * 3);
dramc_dev_ptr->ddrphy_chn_base_nao[i] =
devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_nao[i])) {
pr_info("%s: unable to map ch%d DDRPHY NAO base\n",
__func__, i);
return -EINVAL;
}
}
ret = of_property_read_u32(
dramc_node, "fmeter_version", &fmeter_version);
if (ret) {
pr_info("%s: get fmeter_version fail\n", __func__);
return -EINVAL;
}
pr_info("%s: fmeter_version(%d)\n", __func__, fmeter_version);
if (fmeter_version == 1) {
dramc_dev_ptr->fmeter_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct fmeter_dev_t), GFP_KERNEL);
if (!(dramc_dev_ptr->fmeter_dev_ptr)) {
pr_info("%s: memory alloc fail\n", __func__);
return -ENOMEM;
}
ret = fmeter_v1_init(pdev, dramc_dev_ptr->fmeter_dev_ptr);
if (ret) {
pr_info("%s: fmeter_init fail\n", __func__);
return -EINVAL;
}
} else
dramc_dev_ptr->fmeter_dev_ptr = NULL;
ret = driver_create_file(
pdev->dev.driver, &driver_attr_binning_test);
if (ret) {
pr_info("%s: fail to create binning_test sysfs\n", __func__);
return ret;
}
ret = driver_create_file(
pdev->dev.driver, &driver_attr_dram_data_rate);
if (ret) {
pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
return ret;
}
ret = driver_create_file(
pdev->dev.driver, &driver_attr_mr);
if (ret) {
pr_info("%s: fail to create mr sysfs\n", __func__);
return ret;
}
if (dramc_dev_ptr->mr4_dev_ptr) {
ret = driver_create_file(
pdev->dev.driver, &driver_attr_mr4);
if (ret) {
pr_info("%s: fail to create mr4 sysfs\n", __func__);
return ret;
}
}
platform_set_drvdata(pdev, dramc_dev_ptr);
pr_info("%s: DRAM data type = %d\n", __func__,
mtk_dramc_get_ddr_type());
pr_info("%s: DRAM data rate = %d\n", __func__,
mtk_dramc_get_data_rate());
return ret;
}
static int dramc_remove(struct platform_device *dev)
{
return 0;
}
static const struct of_device_id dramc_of_ids[] = {
{.compatible = "mediatek,common-dramc",},
{}
};
static struct platform_driver dramc_drv = {
.probe = dramc_probe,
.remove = dramc_remove,
.driver = {
.name = "dramc_drv",
.owner = THIS_MODULE,
.of_match_table = dramc_of_ids,
},
};
static int __init dramc_drv_init(void)
{
int ret;
ret = platform_driver_register(&dramc_drv);
if (ret) {
pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
return ret;
}
return ret;
}
static void __exit dramc_drv_exit(void)
{
platform_driver_unregister(&dramc_drv);
}
module_init(dramc_drv_init);
module_exit(dramc_drv_exit);
/*
* mtk_dramc_get_steps_freq - get the freq of target DVFS step
* @step: the step index of DVFS
*
* Returns the DRAM freq
*/
int mtk_dramc_get_steps_freq(unsigned int step)
{
struct dramc_dev_t *dramc_dev_ptr;
if (!dramc_pdev)
return -1;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
if (step < dramc_dev_ptr->freq_cnt)
return dramc_dev_ptr->freq_step[step];
return -1;
}
EXPORT_SYMBOL(mtk_dramc_get_steps_freq);
static unsigned int decode_freq(unsigned int vco_freq)
{
switch (vco_freq) {
case 5460:
return 5500;
case 4264:
return 4266;
case 3718:
case 3588:
return 3733;
case 3068:
return 3200;
case 2652:
return 2667;
case 2366:
return 2400;
case 1859:
case 1794:
return 1866;
case 1534:
return 1600;
case 1144:
case 1196:
return 1200;
case 754:
case 799:
return 800;
case 396:
return 400;
}
return vco_freq;
}
static unsigned int fmeter_v1(struct dramc_dev_t *dramc_dev_ptr)
{
struct fmeter_dev_t *fmeter_dev_ptr =
(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
unsigned int shu_lv_val;
unsigned int pll_id_val;
unsigned int pll_md_val;
unsigned int sdmpcw_val;
unsigned int prediv_val;
unsigned int posdiv_val;
unsigned int ckdiv4_val;
unsigned int cldiv2_val;
unsigned int offset;
unsigned int vco_freq;
unsigned int fbksel;
unsigned int dqsopen;
unsigned int dqopen;
unsigned int ckdiv4_ca_val;
shu_lv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] +
fmeter_dev_ptr->shu_lv.offset) &
fmeter_dev_ptr->shu_lv.mask) >>
fmeter_dev_ptr->shu_lv.shift;
pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] +
fmeter_dev_ptr->pll_id.offset) &
fmeter_dev_ptr->pll_id.mask) >>
fmeter_dev_ptr->pll_id.shift;
offset = fmeter_dev_ptr->pll_md[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
pll_md_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->pll_md[pll_id_val].mask) >>
fmeter_dev_ptr->pll_md[pll_id_val].shift;
offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
offset = fmeter_dev_ptr->prediv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
prediv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->prediv[pll_id_val].mask) >>
fmeter_dev_ptr->prediv[pll_id_val].shift;
offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
fmeter_dev_ptr->posdiv[pll_id_val].shift;
offset = fmeter_dev_ptr->ckdiv4[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
ckdiv4_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->ckdiv4[pll_id_val].mask) >>
fmeter_dev_ptr->ckdiv4[pll_id_val].shift;
offset = fmeter_dev_ptr->cldiv2[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
cldiv2_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->cldiv2[pll_id_val].mask) >>
fmeter_dev_ptr->cldiv2[pll_id_val].shift;
offset = fmeter_dev_ptr->fbksel[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
fbksel = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->fbksel[pll_id_val].mask) >>
fmeter_dev_ptr->fbksel[pll_id_val].shift;
offset = fmeter_dev_ptr->dqsopen[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
dqsopen = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->dqsopen[pll_id_val].mask) >>
fmeter_dev_ptr->dqsopen[pll_id_val].shift;
offset = fmeter_dev_ptr->dqopen[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
dqopen = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->dqopen[pll_id_val].mask) >>
fmeter_dev_ptr->dqopen[pll_id_val].shift;
offset = fmeter_dev_ptr->ckdiv4_ca[pll_id_val].offset +
fmeter_dev_ptr->shu_of * shu_lv_val;
ckdiv4_ca_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] + offset) &
fmeter_dev_ptr->ckdiv4_ca[pll_id_val].mask) >>
fmeter_dev_ptr->ckdiv4_ca[pll_id_val].shift;
vco_freq = ((fmeter_dev_ptr->crystal_freq >> prediv_val) *
(sdmpcw_val >> 8)) >> posdiv_val >> ckdiv4_val >>
pll_md_val >> cldiv2_val << fbksel;
if ((dqsopen == 1 || dqopen == 1) && (ckdiv4_ca_val == 1))
vco_freq >>= 2;
else if ((dqsopen == 1 || dqopen == 1) && (ckdiv4_ca_val == 0))
vco_freq >>= 1;
return decode_freq(vco_freq);
}
/*
* mtk_dramc_get_data_rate - calculate DRAM data rate
*
* Returns DRAM data rate (MB/s)
*/
unsigned int mtk_dramc_get_data_rate(void)
{
struct dramc_dev_t *dramc_dev_ptr;
struct fmeter_dev_t *fmeter_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
if (!fmeter_dev_ptr)
return 0;
if (fmeter_dev_ptr->version == 1)
return fmeter_v1(dramc_dev_ptr);
return 0;
}
EXPORT_SYMBOL(mtk_dramc_get_data_rate);
static unsigned int mr4_v1(struct dramc_dev_t *dramc_dev_ptr, unsigned int ch)
{
struct mr4_dev_t *mr4_dev_ptr =
(struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
return (readl(dramc_dev_ptr->dramc_chn_base_nao[ch] +
mr4_dev_ptr->mr4_rg.offset) & mr4_dev_ptr->mr4_rg.mask) >>
mr4_dev_ptr->mr4_rg.shift;
}
/*
* mtk_dramc_get_mr4 - get the DRAM MR4 value of specific DRAM channel
* @ch: the channel index
*
* Returns the MR4 value
*/
unsigned int mtk_dramc_get_mr4(unsigned int ch)
{
struct dramc_dev_t *dramc_dev_ptr;
struct mr4_dev_t *mr4_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
mr4_dev_ptr = (struct mr4_dev_t *)dramc_dev_ptr->mr4_dev_ptr;
if (!mr4_dev_ptr)
return 0;
if (ch >= dramc_dev_ptr->ch_cnt)
return 0;
if (mr4_dev_ptr->version == 1)
return mr4_v1(dramc_dev_ptr, ch);
return 0;
}
EXPORT_SYMBOL(mtk_dramc_get_mr4);
/*
* mtk_dramc_get_ddr_type - get DRAM type
*
* Returns the DRAM type
*/
unsigned int mtk_dramc_get_ddr_type(void)
{
struct dramc_dev_t *dramc_dev_ptr;
if (!dramc_pdev)
return 0;
dramc_dev_ptr =
(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
return dramc_dev_ptr->dram_type;
}
EXPORT_SYMBOL(mtk_dramc_get_ddr_type);
MODULE_DESCRIPTION("MediaTek DRAMC Driver v0.1");
+226
View File
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/arm-smccc.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
#include <memory/mediatek/emi.h>
DEFINE_SPINLOCK(emidbg_lock);
static struct platform_device *emicen_pdev;
static int emicen_probe(struct platform_device *pdev)
{
struct device_node *emicen_node = pdev->dev.of_node;
struct device_node *emichn_node =
of_parse_phandle(emicen_node, "mediatek,emi-reg", 0);
struct emicen_dev_t *emicen_dev_ptr;
unsigned int i;
int ret;
pr_info("%s: module probe.\n", __func__);
emicen_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct emicen_dev_t), GFP_KERNEL);
if (!emicen_dev_ptr)
return -ENOMEM;
ret = of_property_read_u32(emicen_node,
"ch_cnt", &(emicen_dev_ptr->ch_cnt));
if (ret) {
pr_info("%s: get ch_cnt fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(emicen_node,
"rk_cnt", &(emicen_dev_ptr->rk_cnt));
if (ret) {
pr_info("%s: get rk_cnt fail\n", __func__);
return -EINVAL;
}
pr_info("%s: %s(%d), %s(%d)\n", __func__,
"ch_cnt", emicen_dev_ptr->ch_cnt,
"rk_cnt", emicen_dev_ptr->rk_cnt);
emicen_dev_ptr->rk_size = devm_kmalloc_array(&pdev->dev,
emicen_dev_ptr->rk_cnt, sizeof(unsigned long long),
GFP_KERNEL);
if (!(emicen_dev_ptr->rk_size))
return -ENOMEM;
ret = of_property_read_u64_array(emicen_node,
"rk_size", emicen_dev_ptr->rk_size, emicen_dev_ptr->rk_cnt);
for (i = 0; i < emicen_dev_ptr->rk_cnt; i++)
pr_info("%s: rk_size%d(0x%llx)\n", __func__,
i, emicen_dev_ptr->rk_size[i]);
ret = of_property_count_elems_of_size(
emicen_node, "reg", sizeof(unsigned int) * 4);
if (ret <= 0) {
pr_info("%s: get emi_cen_cnt fail\n", __func__);
return -EINVAL;
}
emicen_dev_ptr->emi_cen_cnt = (unsigned int)ret;
emicen_dev_ptr->emi_cen_base = devm_kmalloc_array(&pdev->dev,
emicen_dev_ptr->emi_cen_cnt, sizeof(phys_addr_t), GFP_KERNEL);
if (!(emicen_dev_ptr->emi_cen_base))
return -ENOMEM;
for (i = 0; i < emicen_dev_ptr->emi_cen_cnt; i++)
emicen_dev_ptr->emi_cen_base[i] = of_iomap(emicen_node, i);
emicen_dev_ptr->emi_chn_base = devm_kmalloc_array(&pdev->dev,
emicen_dev_ptr->ch_cnt, sizeof(phys_addr_t), GFP_KERNEL);
if (!(emicen_dev_ptr->emi_chn_base))
return -ENOMEM;
for (i = 0; i < emicen_dev_ptr->ch_cnt; i++)
emicen_dev_ptr->emi_chn_base[i] = of_iomap(emichn_node, i);
platform_set_drvdata(pdev, emicen_dev_ptr);
emicen_pdev = pdev;
return 0;
}
static int emicen_remove(struct platform_device *dev)
{
return 0;
}
static const struct of_device_id emicen_of_ids[] = {
{.compatible = "mediatek,common-emicen",},
{}
};
static struct platform_driver emicen_drv = {
.probe = emicen_probe,
.remove = emicen_remove,
.driver = {
.name = "emicen_drv",
.owner = THIS_MODULE,
.of_match_table = emicen_of_ids,
},
};
static int __init emicen_drv_init(void)
{
int ret;
ret = platform_driver_register(&emicen_drv);
if (ret) {
pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
return ret;
}
return ret;
}
static void __exit emicen_drv_exit(void)
{
platform_driver_unregister(&emicen_drv);
}
module_init(emicen_drv_init);
module_exit(emicen_drv_exit);
/*
* mtk_emicen_get_ch_cnt - get the channel count
*
* Returns the channel count
*/
unsigned int mtk_emicen_get_ch_cnt(void)
{
struct emicen_dev_t *emicen_dev_ptr;
if (!emicen_pdev)
return 0;
emicen_dev_ptr =
(struct emicen_dev_t *)platform_get_drvdata(emicen_pdev);
return emicen_dev_ptr->ch_cnt;
}
EXPORT_SYMBOL(mtk_emicen_get_ch_cnt);
/*
* mtk_emicen_get_rk_cnt - get the rank count
*
* Returns the rank count
*/
unsigned int mtk_emicen_get_rk_cnt(void)
{
struct emicen_dev_t *emicen_dev_ptr;
if (!emicen_pdev)
return 0;
emicen_dev_ptr =
(struct emicen_dev_t *)platform_get_drvdata(emicen_pdev);
return emicen_dev_ptr->rk_cnt;
}
EXPORT_SYMBOL(mtk_emicen_get_rk_cnt);
/*
* mtk_emicen_get_rk_size - get the rank size of target rank
* @rk_id: the id of target rank
*
* Returns the rank size of target rank
*/
unsigned int mtk_emicen_get_rk_size(unsigned int rk_id)
{
struct emicen_dev_t *emicen_dev_ptr;
if (!emicen_pdev)
return 0;
emicen_dev_ptr =
(struct emicen_dev_t *)platform_get_drvdata(emicen_pdev);
if (rk_id < emicen_dev_ptr->rk_cnt)
return emicen_dev_ptr->rk_size[rk_id];
return 0;
}
EXPORT_SYMBOL(mtk_emicen_get_rk_size);
/*
* mtk_emidbg_dump - dump emi full status to atf log
*
*/
void mtk_emidbg_dump(void)
{
unsigned long spinlock_save_flags;
struct arm_smccc_res smc_res;
spin_lock_irqsave(&emidbg_lock, spinlock_save_flags);
arm_smccc_smc(MTK_SIP_EMIMPU_CONTROL, MTK_EMIDBG_DUMP,
0, 0, 0, 0, 0, 0, &smc_res);
while (smc_res.a0 > 0) {
arm_smccc_smc(MTK_SIP_EMIMPU_CONTROL, MTK_EMIDBG_MSG,
0, 0, 0, 0, 0, 0, &smc_res);
pr_info("%s: %d, 0x%x, 0x%x, 0x%x\n", __func__,
(int)smc_res.a0,
(unsigned int)smc_res.a1,
(unsigned int)smc_res.a2,
(unsigned int)smc_res.a3);
}
spin_unlock_irqrestore(&emidbg_lock, spinlock_save_flags);
}
EXPORT_SYMBOL(mtk_emidbg_dump);
MODULE_DESCRIPTION("MediaTek EMICEN Driver v0.1");
File diff suppressed because it is too large. Load diff
+282
View File
@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <memory/mediatek/emi.h>
static struct platform_device *emiisu_pdev;
static int emiisu_probe(struct platform_device *pdev);
static ssize_t emiisu_ctrl_show(struct device_driver *driver, char *buf)
{
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned int state;
if (!emiisu_pdev)
return 0;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
if (!(emiisu_dev_ptr->con_addr))
return 0;
state = readl(emiisu_dev_ptr->con_addr);
return snprintf(buf, PAGE_SIZE, "isu_state: 0x%x\n", state);
}
static ssize_t emiisu_ctrl_store
(struct device_driver *driver, const char *buf, size_t count)
{
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned long state;
char *command;
char *backup_command;
char *ptr;
char *token[MTK_EMI_MAX_TOKEN];
int i;
if (!emiisu_pdev)
return count;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
if (!(emiisu_dev_ptr->ctrl_intf))
return count;
if (!(emiisu_dev_ptr->con_addr))
return count;
if ((strlen(buf) + 1) > MTK_EMI_MAX_CMD_LEN) {
pr_info("%s: store command overflow\n", __func__);
return count;
}
command = kmalloc((size_t)MTK_EMI_MAX_CMD_LEN, GFP_KERNEL);
if (!command)
return count;
backup_command = command;
strncpy(command, buf, (size_t)MTK_EMI_MAX_CMD_LEN);
for (i = 0; i < MTK_EMI_MAX_TOKEN; i++) {
ptr = strsep(&command, " ");
if (!ptr)
break;
token[i] = ptr;
}
if (i < 1)
goto emiisu_ctrl_store_end;
if (kstrtoul(token[0], 16, &state) == 0)
writel((unsigned int)state, emiisu_dev_ptr->con_addr);
emiisu_ctrl_store_end:
kfree(backup_command);
return count;
}
static DRIVER_ATTR_RW(emiisu_ctrl);
static ssize_t dump_buf_read
(struct file *file, char __user *data, size_t len, loff_t *ppos)
{
struct emiisu_dev_t *emiisu_dev_ptr;
ssize_t bytes;
ssize_t header_bytes = 0;
if (!emiisu_pdev)
return 0;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
bytes = len < (emiisu_dev_ptr->buf_size - *ppos) ?
len : (emiisu_dev_ptr->buf_size - *ppos);
if (!(emiisu_dev_ptr->buf_addr))
return 0;
if (!(emiisu_dev_ptr->ver_addr))
return 0;
if (*ppos == 0) {
if (copy_to_user(data, (char *)(emiisu_dev_ptr->ver_addr), 4)
|| (bytes < 4))
return -EFAULT;
data += 4;
header_bytes += 4;
bytes -= 4;
}
if (bytes) {
if (copy_to_user(data, (char *)(emiisu_dev_ptr->buf_addr) +
*ppos, bytes))
return -EFAULT;
}
*ppos += bytes;
return (bytes + header_bytes);
}
static int dump_buf_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}
static const struct file_operations dump_buf_fops = {
.owner = THIS_MODULE,
.read = dump_buf_read,
.open = dump_buf_open
};
static int emiisu_remove(struct platform_device *dev)
{
return 0;
}
static const struct of_device_id emiisu_of_ids[] = {
{.compatible = "mediatek,common-emiisu",},
{}
};
static struct platform_driver emiisu_drv = {
.probe = emiisu_probe,
.remove = emiisu_remove,
.driver = {
.name = "emiisu_drv",
.owner = THIS_MODULE,
.of_match_table = emiisu_of_ids,
},
};
static int emiisu_probe(struct platform_device *pdev)
{
struct device_node *emiisu_node = pdev->dev.of_node;
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned long long addr_temp;
int ret;
pr_info("%s: module probe.\n", __func__);
emiisu_pdev = pdev;
emiisu_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct emiisu_dev_t), GFP_KERNEL);
if (!emiisu_dev_ptr)
return -ENOMEM;
ret = of_property_read_u32(emiisu_node,
"buf_size", &(emiisu_dev_ptr->buf_size));
if (ret) {
pr_info("%s: get buf_size fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u64(emiisu_node, "buf_addr", &addr_temp);
if (ret) {
pr_info("%s: get buf_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->buf_addr = ioremap_wc(
(phys_addr_t)addr_temp, emiisu_dev_ptr->buf_size);
else
emiisu_dev_ptr->buf_addr = NULL;
ret = of_property_read_u64(emiisu_node, "ver_addr", &addr_temp);
if (ret) {
pr_info("%s: get ver_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->ver_addr = ioremap((phys_addr_t)addr_temp, 4);
else
emiisu_dev_ptr->ver_addr = NULL;
ret = of_property_read_u64(emiisu_node, "con_addr", &addr_temp);
if (ret) {
pr_info("%s: get con_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->con_addr = ioremap((phys_addr_t)addr_temp, 4);
else
emiisu_dev_ptr->con_addr = NULL;
ret = of_property_read_u32(emiisu_node,
"ctrl_intf", &(emiisu_dev_ptr->ctrl_intf));
if (ret) {
pr_info("%s: get ctrl_intf fail\n", __func__);
return -EINVAL;
}
pr_info("%s: %s(%x),%s(%lx),%s(%lx),%s(%lx),%s(%d)\n", __func__,
"buf_size", emiisu_dev_ptr->buf_size,
"buf_addr", (unsigned long)(emiisu_dev_ptr->buf_addr),
"ver_addr", (unsigned long)(emiisu_dev_ptr->ver_addr),
"con_addr", (unsigned long)(emiisu_dev_ptr->con_addr),
"ctrl_intf", emiisu_dev_ptr->ctrl_intf);
emiisu_dev_ptr->dump_dir = debugfs_create_dir("emi_mbw", NULL);
if (!emiisu_dev_ptr->dump_dir) {
pr_info("%s: fail to create dump_dir\n", __func__);
return -EINVAL;
}
emiisu_dev_ptr->dump_buf = debugfs_create_file("dump_buf", 0444,
emiisu_dev_ptr->dump_dir, NULL, &dump_buf_fops);
if (!emiisu_dev_ptr->dump_buf) {
pr_info("%s: fail to create dump_buf\n", __func__);
return -EINVAL;
}
platform_set_drvdata(pdev, emiisu_dev_ptr);
ret = driver_create_file(&emiisu_drv.driver,
&driver_attr_emiisu_ctrl);
if (ret)
pr_info("%s: fail to create emiisu_ctrl\n", __func__);
return ret;
}
static int __init emiisu_drv_init(void)
{
int ret;
ret = platform_driver_register(&emiisu_drv);
if (ret) {
pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
return ret;
}
return ret;
}
static void __exit emiisu_drv_exit(void)
{
platform_driver_unregister(&emiisu_drv);
}
module_init(emiisu_drv_init);
module_exit(emiisu_drv_exit);
MODULE_DESCRIPTION("MediaTek EMIISU Driver v0.1");
+283
View File
@@ -0,0 +1,283 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <memory/mediatek/emi.h>
static struct platform_device *emiisu_pdev;
static int emiisu_probe(struct platform_device *pdev);
static ssize_t emiisu_ctrl_show(struct device_driver *driver, char *buf)
{
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned int state;
if (!emiisu_pdev)
return 0;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
if (!(emiisu_dev_ptr->con_addr))
return 0;
state = readl(emiisu_dev_ptr->con_addr);
return snprintf(buf, PAGE_SIZE, "isu_state: 0x%x\n", state);
}
static ssize_t emiisu_ctrl_store
(struct device_driver *driver, const char *buf, size_t count)
{
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned long state;
char *command;
char *backup_command;
char *ptr;
char *token[MTK_EMI_MAX_TOKEN];
int i;
if (!emiisu_pdev)
return count;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
if (!(emiisu_dev_ptr->ctrl_intf))
return count;
if (!(emiisu_dev_ptr->con_addr))
return count;
if ((strlen(buf) + 1) > MTK_EMI_MAX_CMD_LEN) {
pr_info("%s: store command overflow\n", __func__);
return count;
}
command = kmalloc((size_t)MTK_EMI_MAX_CMD_LEN, GFP_KERNEL);
if (!command)
return count;
backup_command = command;
strncpy(command, buf, (size_t)MTK_EMI_MAX_CMD_LEN);
for (i = 0; i < MTK_EMI_MAX_TOKEN; i++) {
ptr = strsep(&command, " ");
if (!ptr)
break;
token[i] = ptr;
}
if (i < 1)
goto emiisu_ctrl_store_end;
if (kstrtoul(token[0], 16, &state) == 0)
writel((unsigned int)state, emiisu_dev_ptr->con_addr);
emiisu_ctrl_store_end:
kfree(backup_command);
return count;
}
static DRIVER_ATTR_RW(emiisu_ctrl);
static ssize_t dump_buf_read
(struct file *file, char __user *data, size_t len, loff_t *ppos)
{
struct emiisu_dev_t *emiisu_dev_ptr;
ssize_t bytes;
ssize_t header_bytes = 0;
if (!emiisu_pdev)
return 0;
emiisu_dev_ptr =
(struct emiisu_dev_t *)platform_get_drvdata(emiisu_pdev);
bytes = len < (emiisu_dev_ptr->buf_size - *ppos) ?
len : (emiisu_dev_ptr->buf_size - *ppos);
if (!(emiisu_dev_ptr->buf_addr))
return 0;
if (!(emiisu_dev_ptr->ver_addr))
return 0;
if (*ppos == 0) {
if (copy_to_user(data, (char *)(emiisu_dev_ptr->ver_addr), 4)
|| (bytes < 4))
return -EFAULT;
data += 4;
header_bytes += 4;
bytes -= 4;
}
if (bytes) {
if (copy_to_user(data, (char *)(emiisu_dev_ptr->buf_addr) +
*ppos, bytes))
return -EFAULT;
}
*ppos += bytes;
return (bytes + header_bytes);
}
static int dump_buf_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}
static const struct file_operations dump_buf_fops = {
.owner = THIS_MODULE,
.read = dump_buf_read,
.open = dump_buf_open
};
static int emiisu_remove(struct platform_device *dev)
{
return 0;
}
static const struct of_device_id emiisu_of_ids[] = {
{.compatible = "mediatek,common-emiisu",},
{}
};
static struct platform_driver emiisu_drv = {
.probe = emiisu_probe,
.remove = emiisu_remove,
.driver = {
.name = "emiisu_drv",
.owner = THIS_MODULE,
.of_match_table = emiisu_of_ids,
},
};
static int emiisu_probe(struct platform_device *pdev)
{
struct device_node *emiisu_node = pdev->dev.of_node;
struct emiisu_dev_t *emiisu_dev_ptr;
unsigned long long addr_temp;
int ret;
pr_info("%s: module probe.\n", __func__);
emiisu_pdev = pdev;
emiisu_dev_ptr = devm_kmalloc(&pdev->dev,
sizeof(struct emiisu_dev_t), GFP_KERNEL);
if (!emiisu_dev_ptr)
return -ENOMEM;
ret = of_property_read_u32(emiisu_node,
"buf_size", &(emiisu_dev_ptr->buf_size));
if (ret) {
pr_info("%s: get buf_size fail\n", __func__);
return -EINVAL;
}
ret = of_property_read_u64(emiisu_node, "buf_addr", &addr_temp);
if (ret) {
pr_info("%s: get buf_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->buf_addr = ioremap_wc(
(phys_addr_t)addr_temp, emiisu_dev_ptr->buf_size);
else
emiisu_dev_ptr->buf_addr = NULL;
ret = of_property_read_u64(emiisu_node, "ver_addr", &addr_temp);
if (ret) {
pr_info("%s: get ver_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->ver_addr = ioremap((phys_addr_t)addr_temp, 4);
else
emiisu_dev_ptr->ver_addr = NULL;
ret = of_property_read_u64(emiisu_node, "con_addr", &addr_temp);
if (ret) {
pr_info("%s: get con_addr fail\n", __func__);
return -EINVAL;
}
if (addr_temp)
emiisu_dev_ptr->con_addr = ioremap((phys_addr_t)addr_temp, 4);
else
emiisu_dev_ptr->con_addr = NULL;
ret = of_property_read_u32(emiisu_node,
"ctrl_intf", &(emiisu_dev_ptr->ctrl_intf));
if (ret) {
pr_info("%s: get ctrl_intf fail\n", __func__);
return -EINVAL;
}
pr_info("%s: %s(%x),%s(%lx),%s(%lx),%s(%lx),%s(%d)\n", __func__,
"buf_size", emiisu_dev_ptr->buf_size,
"buf_addr", (unsigned long)(emiisu_dev_ptr->buf_addr),
"ver_addr", (unsigned long)(emiisu_dev_ptr->ver_addr),
"con_addr", (unsigned long)(emiisu_dev_ptr->con_addr),
"ctrl_intf", emiisu_dev_ptr->ctrl_intf);
emiisu_dev_ptr->dump_dir = debugfs_create_dir("emi_mbw", NULL);
if (!emiisu_dev_ptr->dump_dir) {
pr_info("%s: fail to create dump_dir\n", __func__);
return -EINVAL;
}
emiisu_dev_ptr->dump_buf = debugfs_create_file("dump_buf", 0444,
emiisu_dev_ptr->dump_dir, NULL, &dump_buf_fops);
if (!emiisu_dev_ptr->dump_buf) {
pr_info("%s: fail to create dump_buf\n", __func__);
return -EINVAL;
}
platform_set_drvdata(pdev, emiisu_dev_ptr);
ret = driver_create_file(&emiisu_drv.driver,
&driver_attr_emiisu_ctrl);
if (ret)
pr_info("%s: fail to create emiisu_ctrl\n", __func__);
return ret;
}
static int __init emiisu_drv_init(void)
{
int ret;
ret = platform_driver_register(&emiisu_drv);
if (ret) {
pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
return ret;
}
return ret;
}
static void __exit emiisu_drv_exit(void)
{
platform_driver_unregister(&emiisu_drv);
}
module_init(emiisu_drv_init);
module_exit(emiisu_drv_exit);
MODULE_DESCRIPTION("MediaTek EMIISU Driver v0.1");
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+891
View File
@@ -0,0 +1,891 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015-2020 MediaTek Inc.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <soc/mediatek/smi.h>
#include <dt-bindings/memory/mt2701-larb-port.h>
/* mt8173 */
#define SMI_LARB_MMU_EN 0xf00
#define SMI_LARB_MMU_EN_MT8167 0xfc0
/* mt2701 */
#define REG_SMI_SECUR_CON_BASE 0x5c0
/* every register control 8 port, register offset 0x4 */
#define REG_SMI_SECUR_CON_OFFSET(id) (((id) >> 3) << 2)
#define REG_SMI_SECUR_CON_ADDR(id) \
(REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id))
/*
* every port have 4 bit to control, bit[port + 3] control virtual or physical,
* bit[port + 2 : port + 1] control the domain, bit[port] control the security
* or non-security.
*/
#define SMI_SECUR_CON_VAL_MSK(id) (~(0xf << (((id) & 0x7) << 2)))
#define SMI_SECUR_CON_VAL_VIRT(id) BIT((((id) & 0x7) << 2) + 3)
/* mt2701 domain should be set to 3 */
#define SMI_SECUR_CON_VAL_DOMAIN(id) (0x3 << ((((id) & 0x7) << 2) + 1))
/* mt2712 */
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
#define F_MMU_EN BIT(0)
#define SMI_LARB_SLP_CON 0x00c
#define SLP_PROT_EN BIT(0)
#define SLP_PROT_RDY BIT(16)
/* SMI COMMON */
#define SMI_BUS_SEL 0x220
#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1)
/* COUNT PROBE */
int count_number = 1;
int smi_dev_number = 1;
/* All are MMU0 defaultly. Only specialize mmu1 here. */
#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid))
#if !IS_ENABLED(CONFIG_MTK_SMI_EXT)
enum mtk_smi_gen {
MTK_SMI_GEN1,
MTK_SMI_GEN2
};
struct mtk_smi_common_plat {
enum mtk_smi_gen gen;
/* Adjust some larbs to mmu1 to balance the bandwidth */
unsigned int bus_sel;
};
struct mtk_smi_larb_gen {
bool need_larbid;
int port_in_larb[MTK_LARB_NR_MAX + 1];
void (*config_port)(struct device *);
void (*larb_sleep_ctrl)(struct device *dev, bool toslp);
};
struct mtk_smi {
struct device *dev;
struct clk *clk_apb, *clk_smi;
struct clk *clk_gals0, *clk_gals1;
struct clk *clk_async; /*only needed by mt2701*/
void __iomem *smi_ao_base; /* only for gen1 */
void __iomem *base; /* only for gen2 */
const struct mtk_smi_common_plat *plat;
};
struct mtk_smi_larb { /* larb: local arbiter */
struct mtk_smi smi;
void __iomem *base;
struct device *smi_common_dev;
const struct mtk_smi_larb_gen *larb_gen;
int larbid;
u32 *mmu;
};
static int mtk_smi_clk_enable(const struct mtk_smi *smi)
{
int ret;
ret = clk_prepare_enable(smi->clk_apb);
if (ret)
return ret;
ret = clk_prepare_enable(smi->clk_smi);
if (ret)
goto err_disable_apb;
ret = clk_prepare_enable(smi->clk_gals0);
if (ret)
goto err_disable_smi;
ret = clk_prepare_enable(smi->clk_gals1);
if (ret)
goto err_disable_gals0;
return 0;
err_disable_gals0:
clk_disable_unprepare(smi->clk_gals0);
err_disable_smi:
clk_disable_unprepare(smi->clk_smi);
err_disable_apb:
clk_disable_unprepare(smi->clk_apb);
return ret;
}
static void mtk_smi_clk_disable(const struct mtk_smi *smi)
{
clk_disable_unprepare(smi->clk_gals1);
clk_disable_unprepare(smi->clk_gals0);
clk_disable_unprepare(smi->clk_smi);
clk_disable_unprepare(smi->clk_apb);
}
static int
mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
struct mtk_smi_iommu *smi_iommu = data;
unsigned int i;
if (larb->larb_gen->need_larbid) {
larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu;
return 0;
}
/*
* If there is no larbid property, Loop to find the corresponding
* iommu information.
*/
for (i = 0; i < smi_iommu->larb_nr; i++) {
if (dev == smi_iommu->larb_imu[i].dev) {
/* The 'mmu' may be updated in iommu-attach/detach. */
larb->mmu = &smi_iommu->larb_imu[i].mmu;
return 0;
}
}
return -ENODEV;
}
static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
u32 reg;
int i;
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
reg |= F_MMU_EN;
writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
}
}
static void mtk_smi_larb_config_port_mt2712(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
/*
* larb 8/9 is the bdpsys larb, the iommu_en is enabled defaultly.
* Don't need to set it again.
*/
if (larb->larbid == 8 || larb->larbid == 9)
return;
mtk_smi_larb_config_port_gen2_general(dev);
}
static void mtk_smi_larb_config_port_mt8173(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
}
static void mtk_smi_larb_config_port_mt8167(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN_MT8167);
}
static void mtk_smi_larb_config_port_gen1(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
int i, m4u_port_id, larb_port_num;
u32 sec_con_val, reg_val;
m4u_port_id = larb_gen->port_in_larb[larb->larbid];
larb_port_num = larb_gen->port_in_larb[larb->larbid + 1]
- larb_gen->port_in_larb[larb->larbid];
for (i = 0; i < larb_port_num; i++, m4u_port_id++) {
if (*larb->mmu & BIT(i)) {
/* bit[port + 3] controls the virtual or physical */
sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id);
} else {
/* do not need to enable m4u for this port */
continue;
}
reg_val = readl(common->smi_ao_base
+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id);
reg_val |= sec_con_val;
reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id);
writel(reg_val,
common->smi_ao_base
+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
}
}
static void mtk_smi_larb_sleep_ctrl_mt8168(struct device *dev, bool toslp)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
void __iomem *base = larb->base;
u32 tmp;
/* larb4 should be use a general way. */
if (larb->larbid == 4)
return;
if (toslp) {
writel_relaxed(SLP_PROT_EN, base + SMI_LARB_SLP_CON);
if (readl_poll_timeout_atomic(base + SMI_LARB_SLP_CON,
tmp, !!(tmp & SLP_PROT_RDY), 10, 10000))
dev_notice(dev, "larb sleep con not ready(%d)\n", tmp);
} else
writel_relaxed(0, base + SMI_LARB_SLP_CON);
}
static void
mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
{
/* Do nothing as the iommu is always enabled. */
}
static const struct component_ops mtk_smi_larb_component_ops = {
.bind = mtk_smi_larb_bind,
.unbind = mtk_smi_larb_unbind,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
/* mt8173 do not need the port in larb */
.config_port = mtk_smi_larb_config_port_mt8173,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
.config_port = mtk_smi_larb_config_port_mt8167,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
.need_larbid = true,
.port_in_larb = {
LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
},
.config_port = mtk_smi_larb_config_port_gen1,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
.need_larbid = true,
.config_port = mtk_smi_larb_config_port_mt2712,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
.config_port = mtk_smi_larb_config_port_gen2_general,
};
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8168 = {
.config_port = mtk_smi_larb_config_port_gen2_general,
.larb_sleep_ctrl = mtk_smi_larb_sleep_ctrl_mt8168,
};
static const struct of_device_id mtk_smi_larb_of_ids[] = {
{
.compatible = "mediatek,mt8173-smi-larb",
.data = &mtk_smi_larb_mt8173
},
{
.compatible = "mediatek,mt8167-smi-larb",
.data = &mtk_smi_larb_mt8167
},
{
.compatible = "mediatek,mt2701-smi-larb",
.data = &mtk_smi_larb_mt2701
},
{
.compatible = "mediatek,mt2712-smi-larb",
.data = &mtk_smi_larb_mt2712
},
{
.compatible = "mediatek,mt8183-smi-larb",
.data = &mtk_smi_larb_mt8183
},
{
.compatible = "mediatek,mt8168-smi-larb",
.data = &mtk_smi_larb_mt8168
},
{}
};
static int mtk_smi_larb_probe(struct platform_device *pdev)
{
struct mtk_smi_larb *larb;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *smi_node;
struct platform_device *smi_pdev;
int err;
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
if (!larb)
return -ENOMEM;
larb->larb_gen = of_device_get_match_data(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
larb->base = devm_ioremap_resource(dev, res);
if (IS_ERR(larb->base))
return PTR_ERR(larb->base);
larb->smi.clk_apb = devm_clk_get(dev, "apb");
if (IS_ERR(larb->smi.clk_apb))
return PTR_ERR(larb->smi.clk_apb);
larb->smi.clk_smi = devm_clk_get(dev, "smi");
if (IS_ERR(larb->smi.clk_smi))
return PTR_ERR(larb->smi.clk_smi);
larb->smi.clk_gals0 = devm_clk_get(dev, "gals");
if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT)
larb->smi.clk_gals0 = NULL;
else if (IS_ERR(larb->smi.clk_gals0))
return PTR_ERR(larb->smi.clk_gals0);
larb->smi.dev = dev;
if (larb->larb_gen->need_larbid) {
err = of_property_read_u32(dev->of_node, "mediatek,larb-id",
&larb->larbid);
if (err) {
dev_notice(dev, "missing larbid property\n");
return err;
}
}
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
if (!smi_node)
return -EINVAL;
smi_pdev = of_find_device_by_node(smi_node);
of_node_put(smi_node);
if (smi_pdev) {
if (!platform_get_drvdata(smi_pdev))
return -EPROBE_DEFER;
larb->smi_common_dev = &smi_pdev->dev;
} else {
dev_notice(dev, "Failed to get the smi_common device\n");
return -EINVAL;
}
pm_runtime_enable(dev);
platform_set_drvdata(pdev, larb);
return component_add(dev, &mtk_smi_larb_component_ops);
}
static int mtk_smi_larb_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &mtk_smi_larb_component_ops);
return 0;
}
static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
int ret;
/* Power on smi-common. */
ret = pm_runtime_get_sync(larb->smi_common_dev);
if (ret < 0) {
dev_notice(dev, "smi-common pm get failed(%d).\n", ret);
return ret;
}
ret = mtk_smi_clk_enable(&larb->smi);
if (ret < 0) {
dev_notice(dev, "larb clk enable failed(%d).\n", ret);
pm_runtime_put_sync(larb->smi_common_dev);
return ret;
}
if (larb_gen->larb_sleep_ctrl)
larb_gen->larb_sleep_ctrl(dev, false);
/* Configure the basic setting for this larb */
larb_gen->config_port(dev);
return 0;
}
static int __maybe_unused mtk_smi_larb_suspend(struct device *dev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
if (larb_gen->larb_sleep_ctrl)
larb_gen->larb_sleep_ctrl(dev, true);
mtk_smi_clk_disable(&larb->smi);
pm_runtime_put_sync(larb->smi_common_dev);
return 0;
}
static const struct dev_pm_ops smi_larb_pm_ops = {
SET_RUNTIME_PM_OPS(mtk_smi_larb_suspend, mtk_smi_larb_resume, NULL)
};
static struct platform_driver mtk_smi_larb_driver = {
.probe = mtk_smi_larb_probe,
.remove = mtk_smi_larb_remove,
.driver = {
.name = "mtk-smi-larb",
.of_match_table = mtk_smi_larb_of_ids,
.pm = &smi_larb_pm_ops,
}
};
static const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
.gen = MTK_SMI_GEN1,
};
static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
.gen = MTK_SMI_GEN2,
};
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
.gen = MTK_SMI_GEN2,
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(3) | F_MMU1_LARB(4) |
F_MMU1_LARB(7),
};
static const struct of_device_id mtk_smi_common_of_ids[] = {
{
.compatible = "mediatek,mt8173-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt8167-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt2701-smi-common",
.data = &mtk_smi_common_gen1,
},
{
.compatible = "mediatek,mt2712-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt8168-smi-common",
.data = &mtk_smi_common_gen2,
},
{
.compatible = "mediatek,mt8183-smi-common",
.data = &mtk_smi_common_mt8183,
},
{}
};
#ifdef CONFIG_MACH_MT8167
static struct mtk_smi *gmtk_common_dev;
#endif
static int mtk_smi_common_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_smi *common;
struct resource *res;
int ret;
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
if (!common)
return -ENOMEM;
common->dev = dev;
common->plat = of_device_get_match_data(dev);
common->clk_apb = devm_clk_get(dev, "apb");
if (IS_ERR(common->clk_apb))
return PTR_ERR(common->clk_apb);
common->clk_smi = devm_clk_get(dev, "smi");
if (IS_ERR(common->clk_smi))
return PTR_ERR(common->clk_smi);
common->clk_gals0 = devm_clk_get(dev, "gals0");
if (PTR_ERR(common->clk_gals0) == -ENOENT)
common->clk_gals0 = NULL;
else if (IS_ERR(common->clk_gals0))
return PTR_ERR(common->clk_gals0);
common->clk_gals1 = devm_clk_get(dev, "gals1");
if (PTR_ERR(common->clk_gals1) == -ENOENT)
common->clk_gals1 = NULL;
else if (IS_ERR(common->clk_gals1))
return PTR_ERR(common->clk_gals1);
/*
* for mtk smi gen 1, we need to get the ao(always on) base to config
* m4u port, and we need to enable the aync clock for transform the smi
* clock into emi clock domain, but for mtk smi gen2, there's no smi ao
* base.
*/
if (common->plat->gen == MTK_SMI_GEN1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
common->smi_ao_base = devm_ioremap_resource(dev, res);
if (IS_ERR(common->smi_ao_base))
return PTR_ERR(common->smi_ao_base);
common->clk_async = devm_clk_get(dev, "async");
if (IS_ERR(common->clk_async))
return PTR_ERR(common->clk_async);
ret = clk_prepare_enable(common->clk_async);
if (ret)
return ret;
} else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
common->base = devm_ioremap_resource(dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
}
pm_runtime_enable(dev);
platform_set_drvdata(pdev, common);
#ifdef CONFIG_MACH_MT8167
/*
* Without pm_runtime_get_sync(dev), the disp power domain
* would be turn off after pm_runtime_enable, meanwhile disp
* hw are still access register, this would cause system
* abnormal.
*
* If we do not call pm_runtime_get_sync, then system would hang
* in larb0's power domain attach, power domain SA and DE are
* still checking that. We would like to bypass this first and
* don't block the software flow.
*/
pm_runtime_get_sync(dev);
gmtk_common_dev = common;
#endif
return 0;
}
static int mtk_smi_common_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static int __maybe_unused mtk_smi_common_resume(struct device *dev)
{
struct mtk_smi *common = dev_get_drvdata(dev);
unsigned int bus_sel = common->plat->bus_sel;
int ret;
ret = mtk_smi_clk_enable(common);
if (ret)
return ret;
if (common->plat->gen == MTK_SMI_GEN2 && bus_sel)
writel(bus_sel, common->base + SMI_BUS_SEL);
return 0;
}
static int __maybe_unused mtk_smi_common_suspend(struct device *dev)
{
struct mtk_smi *common = dev_get_drvdata(dev);
mtk_smi_clk_disable(common);
return 0;
}
static const struct dev_pm_ops smi_common_pm_ops = {
SET_RUNTIME_PM_OPS(mtk_smi_common_suspend, mtk_smi_common_resume, NULL)
};
static struct platform_driver mtk_smi_common_driver = {
.probe = mtk_smi_common_probe,
.remove = mtk_smi_common_remove,
.driver = {
.name = "mtk-smi-common",
.of_match_table = mtk_smi_common_of_ids,
.pm = &smi_common_pm_ops,
}
};
#ifdef CONFIG_MACH_MT8167
/* put the disp power domain that we got in smi probe */
static int __init mtk_smi_init_late(void)
{
pm_runtime_put_sync(gmtk_common_dev->dev);
return 0;
}
#endif
#else /* IS_ENABLED(CONFIG_MTK_SMI_EXT) */
#include <linux/of_address.h>
static u32 nr_larbs, nr_dev;
static struct mtk_smi_dev **smi_dev;
s32 mtk_smi_clk_enable(struct mtk_smi_dev *smi)
{
s32 i, j, ret = 0;
if (!smi) {
pr_info("No such device or address\n");
return -ENXIO;
} else if (!smi->dev || !smi->clks) {
pr_info("SMI%u no such device or address\n", smi->id);
return -ENXIO;
}
for (i = 1; i < smi->nr_clks; i++) { /* without MTCMOS */
ret = clk_prepare_enable(smi->clks[i]);
if (ret) {
dev_info(smi->dev, "SMI%u CLK%d enable failed:%d\n",
smi->id, i, ret);
for (j = i - 1; j > 0; j--)
clk_disable_unprepare(smi->clks[j]);
return ret;
}
}
atomic_inc(&(smi->clk_cnts));
return ret;
}
EXPORT_SYMBOL_GPL(mtk_smi_clk_enable);
void mtk_smi_clk_disable(struct mtk_smi_dev *smi)
{
s32 i;
if (!smi)
pr_info("No such device or address\n");
else if (!smi->dev || !smi->clks)
pr_info("SMI%u no such device or address\n", smi->id);
else {
atomic_dec(&(smi->clk_cnts));
for (i = smi->nr_clks - 1; i > 0; i--)
clk_disable_unprepare(smi->clks[i]);
}
}
EXPORT_SYMBOL_GPL(mtk_smi_clk_disable);
struct mtk_smi_dev *mtk_smi_dev_get(const u32 id)
{
if (id > nr_dev)
pr_info("Invalid id: %u, nr_dev=%u\n", id, nr_dev);
else if (!smi_dev[id])
pr_info("SMI%u no such device or address\n", id);
else
return smi_dev[id];
return NULL;
}
EXPORT_SYMBOL_GPL(mtk_smi_dev_get);
s32 mtk_smi_conf_set(const struct mtk_smi_dev *smi, const u32 scen_id)
{
u32 cnts, i;
if (!smi) {
pr_info("No such device or address\n");
return -ENXIO;
} else if (!smi->dev) {
pr_info("SMI%u no such device or address\n", smi->id);
return -ENXIO;
}
cnts = atomic_read(&(smi->clk_cnts));
if (cnts <= 0) {
dev_dbg(smi->dev, "SMI%u without MTCMOS: %d\n", smi->id, cnts);
return cnts;
}
for (i = 0; i < smi->nr_conf_pairs; i++) /* conf */
writel(smi->conf_pairs[i].val,
smi->base + smi->conf_pairs[i].off);
for (i = 0; i < smi->nr_scen_pairs; i++) /* scen */
writel(smi->scen_pairs[scen_id][i].val,
smi->base + smi->scen_pairs[scen_id][i].off);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_smi_conf_set);
static s32 mtk_smi_clks_get(struct mtk_smi_dev *smi)
{
struct property *prop;
const char *name, *clk_names = "clock-names";
s32 i = 0, ret;
if (!smi) {
pr_info("No such device or address\n");
return -ENXIO;
} else if (!smi->dev) {
pr_info("SMI%u no such device or address\n", smi->id);
return -ENXIO;
}
ret = of_property_count_strings(smi->dev->of_node, clk_names);
if (ret < 0)
return ret;
smi->nr_clks = (u32)ret;
smi->clks = devm_kcalloc(smi->dev, smi->nr_clks, sizeof(*smi->clks),
GFP_KERNEL);
if (!smi->clks)
return -ENOMEM;
of_property_for_each_string(smi->dev->of_node, clk_names, prop, name) {
smi->clks[i] = devm_clk_get(smi->dev, name);
if (IS_ERR(smi->clks[i])) {
dev_info(smi->dev, "SMI%u CLK%d:%s get failed, err:%x\n",
smi->id, i, name, IS_ERR(smi->clks[i]));
break;
}
dev_info(smi->dev, "SMI%u CLK%d:%s\n", smi->id, i, name);
i += 1;
}
if (i < smi->nr_clks)
return PTR_ERR(smi->clks[i]);
atomic_set(&(smi->clk_cnts), 0);
if (count_number == smi_dev_number+1) {
ret = smi_register();
if (ret)
pr_info("Failed to register SMI_EXT driver\n");
} else if (count_number > smi_dev_number) {
pr_info("SMI probe too much\n");
}
return 0;
}
static int mtk_smi_dev_probe(struct platform_device *pdev, const u32 id)
{
struct resource *res;
void __iomem *base;
u32 reset_tmp, reset_num, offset,
comm_reset_tmp, comm_reset_num, comm_offset, comm_clamp_tmp;
s32 i;
if (id > nr_dev) {
dev_dbg(&pdev->dev,
"Invalid id:%u, nr_dev=%u\n", id, nr_dev);
return -EINVAL;
}
smi_dev[id] =
devm_kzalloc(&pdev->dev, sizeof(*smi_dev[id]), GFP_KERNEL);
if (!smi_dev[id])
return -ENOMEM;
smi_dev[id]->id = id;
smi_dev[id]->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(smi_dev[id]->dev, res);
if (IS_ERR(base)) {
dev_info(&pdev->dev, "SMI%u base:%p read failed\n",
id, base);
return PTR_ERR(base);
}
smi_dev[id]->base = base;
if (of_address_to_resource(smi_dev[id]->dev->of_node, 0, res))
return -EINVAL;
dev_info(&pdev->dev,
"SMI%u base: VA=%p, PA=%pa\n", id, base, &res->start);
platform_set_drvdata(pdev, smi_dev[id]);
if (of_get_property(smi_dev[id]->dev->of_node, "power-reset", &reset_tmp)) {
reset_num = reset_tmp / (sizeof(u32) * RESET_CELL_NUM);
for (i = 0; i < reset_num; i++) {
offset = i * RESET_CELL_NUM;
if (of_property_read_u32_index(smi_dev[id]->dev->of_node,
"power-reset", offset, &reset_tmp))
break;
smi_dev[id]->power_reset_pa[i] = reset_tmp;
smi_dev[id]->power_reset_reg[i] = ioremap(reset_tmp, 4);
if (of_property_read_u32_index(smi_dev[id]->dev->of_node,
"power-reset", offset + 1, &reset_tmp))
break;
smi_dev[id]->power_reset_value[i] = 1 << reset_tmp;
}
}
if (of_get_property(smi_dev[id]->dev->of_node, "common-reset", &comm_reset_tmp)) {
comm_reset_num = comm_reset_tmp / (sizeof(u32) * RESET_CELL_NUM);
for (i = 0; i < comm_reset_num; i++) {
comm_offset = i * RESET_CELL_NUM;
if (of_property_read_u32_index(smi_dev[id]->dev->of_node,
"common-reset", comm_offset, &comm_reset_tmp)) {
break;
}
smi_dev[id]->comm_reset_pa[i] = comm_reset_tmp;
smi_dev[id]->comm_reset_reg[i] = ioremap(comm_reset_tmp, 1000);
if (of_property_read_u32_index(smi_dev[id]->dev->of_node,
"common-reset", comm_offset + 1, &comm_reset_tmp)) {
break;
}
smi_dev[id]->comm_reset_value[i] = 1 << comm_reset_tmp;
smi_dev[id]->comm_reset = true;
}
}
if (of_get_property(smi_dev[id]->dev->of_node, "common-clamp", &comm_clamp_tmp)) {
for (i = 0; i < MAX_COMMON_FOR_CLAMP; i++) {
comm_offset = i * RESET_CELL_NUM;
if (of_property_read_u32_index(smi_dev[id]->dev->of_node,
"common-clamp",comm_offset, &comm_clamp_tmp)) {
break;
}
smi_dev[id]->comm_clamp_value[i] = 1 << comm_clamp_tmp;
}
}
return mtk_smi_clks_get(smi_dev[id]);
}
static int mtk_smi_larb_probe(struct platform_device *pdev)
{
u32 id = 0;
s32 ret;
if (!pdev) {
pr_notice("platform_device missed\n");
return -ENODEV;
}
ret = of_property_read_u32(pdev->dev.of_node, "mediatek,smi-id", &id);
if (ret) {
dev_info(&pdev->dev, "LARB read failed:%d\n", ret);
return ret;
}
count_number = count_number + 1;
return mtk_smi_dev_probe(pdev, id);
}
static int mtk_smi_common_probe(struct platform_device *pdev)
{
u32 id = 0, cnt = 0;
s32 ret;
if (count_number == 1)
smi_dev_number = smi_get_dev_num();
if (!pdev) {
pr_notice("platform_device missed\n");
return -ENODEV;
}
ret = of_property_read_u32(pdev->dev.of_node, "mediatek,smi-id", &id);
if (ret) {
dev_info(&pdev->dev, "COMMON read failed:%d\n", ret);
return ret;
}
nr_larbs = id;
ret = of_property_read_u32(pdev->dev.of_node, "mediatek,smi-cnt", &cnt);
if (ret)
dev_dbg(&pdev->dev, "COMMON%u read all:%d failed:%d\n",
nr_larbs, cnt, ret);
if (!smi_dev) {
nr_dev = cnt ? cnt : (id + 1);
smi_dev = devm_kcalloc(
&pdev->dev, nr_dev, sizeof(*smi_dev), GFP_KERNEL);
dev_info(&pdev->dev, "COMMON%u nr_dev:%u\n", id, nr_dev);
}
if (!smi_dev)
return -ENOMEM;
count_number = count_number + 1;
return mtk_smi_dev_probe(pdev, id);
}
static const struct of_device_id mtk_smi_larb_of_ids[] = {
{
.compatible = "mediatek,smi_larb",
},
{}
};
static const struct of_device_id mtk_smi_common_of_ids[] = {
{
.compatible = "mediatek,smi_common",
},
{
.compatible = "mediatek,smi_sub_common",
},
{}
};
static struct platform_driver mtk_smi_larb_driver = {
.probe = mtk_smi_larb_probe,
.driver = {
.name = "mtk-smi-larb",
.of_match_table = mtk_smi_larb_of_ids,
}
};
static struct platform_driver mtk_smi_common_driver = {
.probe = mtk_smi_common_probe,
.driver = {
.name = "mtk-smi-common",
.of_match_table = mtk_smi_common_of_ids,
}
};
#endif /* IS_ENABLED(CONFIG_MTK_SMI_EXT) */
static int __init mtk_smi_init(void)
{
int ret;
ret = platform_driver_register(&mtk_smi_common_driver);
if (ret != 0) {
pr_err("Failed to register SMI driver\n");
return ret;
}
ret = platform_driver_register(&mtk_smi_larb_driver);
if (ret != 0) {
pr_err("Failed to register SMI-LARB driver\n");
goto err_unreg_smi;
}
return ret;
err_unreg_smi:
platform_driver_unregister(&mtk_smi_common_driver);
return ret;
}
#if !IS_ENABLED(CONFIG_MTK_SMI_EXT)
module_init(mtk_smi_init);
#ifdef CONFIG_MACH_MT8167
late_initcall(mtk_smi_init_late);
#endif
#else
#if (defined(CONFIG_MACH_MT6833) || defined(CONFIG_MACH_MT6893) || defined(CONFIG_MACH_MT6885))
arch_initcall_sync(mtk_smi_init);
#else
arch_initcall(mtk_smi_init);
#endif
#endif
MODULE_DESCRIPTION("MediaTek SMI driver");
MODULE_LICENSE("GPL v2");
+361
View File
@@ -0,0 +1,361 @@
/*
* Marvell EBU SoC Device Bus Controller
* (memory controller for NOR/NAND/SRAM/FPGA devices)
*
* Copyright (C) 2013-2014 Marvell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mbus.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
/* Register definitions */
#define ARMADA_DEV_WIDTH_SHIFT 30
#define ARMADA_BADR_SKEW_SHIFT 28
#define ARMADA_RD_HOLD_SHIFT 23
#define ARMADA_ACC_NEXT_SHIFT 17
#define ARMADA_RD_SETUP_SHIFT 12
#define ARMADA_ACC_FIRST_SHIFT 6
#define ARMADA_SYNC_ENABLE_SHIFT 24
#define ARMADA_WR_HIGH_SHIFT 16
#define ARMADA_WR_LOW_SHIFT 8
#define ARMADA_READ_PARAM_OFFSET 0x0
#define ARMADA_WRITE_PARAM_OFFSET 0x4
#define ORION_RESERVED (0x2 << 30)
#define ORION_BADR_SKEW_SHIFT 28
#define ORION_WR_HIGH_EXT_BIT BIT(27)
#define ORION_WR_HIGH_EXT_MASK 0x8
#define ORION_WR_LOW_EXT_BIT BIT(26)
#define ORION_WR_LOW_EXT_MASK 0x8
#define ORION_ALE_WR_EXT_BIT BIT(25)
#define ORION_ALE_WR_EXT_MASK 0x8
#define ORION_ACC_NEXT_EXT_BIT BIT(24)
#define ORION_ACC_NEXT_EXT_MASK 0x10
#define ORION_ACC_FIRST_EXT_BIT BIT(23)
#define ORION_ACC_FIRST_EXT_MASK 0x10
#define ORION_TURN_OFF_EXT_BIT BIT(22)
#define ORION_TURN_OFF_EXT_MASK 0x8
#define ORION_DEV_WIDTH_SHIFT 20
#define ORION_WR_HIGH_SHIFT 17
#define ORION_WR_HIGH_MASK 0x7
#define ORION_WR_LOW_SHIFT 14
#define ORION_WR_LOW_MASK 0x7
#define ORION_ALE_WR_SHIFT 11
#define ORION_ALE_WR_MASK 0x7
#define ORION_ACC_NEXT_SHIFT 7
#define ORION_ACC_NEXT_MASK 0xF
#define ORION_ACC_FIRST_SHIFT 3
#define ORION_ACC_FIRST_MASK 0xF
#define ORION_TURN_OFF_SHIFT 0
#define ORION_TURN_OFF_MASK 0x7
struct devbus_read_params {
u32 bus_width;
u32 badr_skew;
u32 turn_off;
u32 acc_first;
u32 acc_next;
u32 rd_setup;
u32 rd_hold;
};
struct devbus_write_params {
u32 sync_enable;
u32 wr_high;
u32 wr_low;
u32 ale_wr;
};
struct devbus {
struct device *dev;
void __iomem *base;
unsigned long tick_ps;
};
static int get_timing_param_ps(struct devbus *devbus,
struct device_node *node,
const char *name,
u32 *ticks)
{
u32 time_ps;
int err;
err = of_property_read_u32(node, name, &time_ps);
if (err < 0) {
dev_err(devbus->dev, "%pOF has no '%s' property\n",
node, name);
return err;
}
*ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps;
dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n",
name, time_ps, *ticks);
return 0;
}
static int devbus_get_timing_params(struct devbus *devbus,
struct device_node *node,
struct devbus_read_params *r,
struct devbus_write_params *w)
{
int err;
err = of_property_read_u32(node, "devbus,bus-width", &r->bus_width);
if (err < 0) {
dev_err(devbus->dev,
"%pOF has no 'devbus,bus-width' property\n",
node);
return err;
}
/*
* The bus width is encoded into the register as 0 for 8 bits,
* and 1 for 16 bits, so we do the necessary conversion here.
*/
if (r->bus_width == 8)
r->bus_width = 0;
else if (r->bus_width == 16)
r->bus_width = 1;
else {
dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width);
return -EINVAL;
}
err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
&r->badr_skew);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
&r->turn_off);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
&r->acc_first);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
&r->acc_next);
if (err < 0)
return err;
if (of_device_is_compatible(devbus->dev->of_node, "marvell,mvebu-devbus")) {
err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps",
&r->rd_setup);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps",
&r->rd_hold);
if (err < 0)
return err;
err = of_property_read_u32(node, "devbus,sync-enable",
&w->sync_enable);
if (err < 0) {
dev_err(devbus->dev,
"%pOF has no 'devbus,sync-enable' property\n",
node);
return err;
}
}
err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
&w->ale_wr);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
&w->wr_low);
if (err < 0)
return err;
err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
&w->wr_high);
if (err < 0)
return err;
return 0;
}
static void devbus_orion_set_timing_params(struct devbus *devbus,
struct device_node *node,
struct devbus_read_params *r,
struct devbus_write_params *w)
{
u32 value;
/*
* The hardware designers found it would be a good idea to
* split most of the values in the register into two fields:
* one containing all the low-order bits, and another one
* containing just the high-order bit. For all of those
* fields, we have to split the value into these two parts.
*/
value = (r->turn_off & ORION_TURN_OFF_MASK) << ORION_TURN_OFF_SHIFT |
(r->acc_first & ORION_ACC_FIRST_MASK) << ORION_ACC_FIRST_SHIFT |
(r->acc_next & ORION_ACC_NEXT_MASK) << ORION_ACC_NEXT_SHIFT |
(w->ale_wr & ORION_ALE_WR_MASK) << ORION_ALE_WR_SHIFT |
(w->wr_low & ORION_WR_LOW_MASK) << ORION_WR_LOW_SHIFT |
(w->wr_high & ORION_WR_HIGH_MASK) << ORION_WR_HIGH_SHIFT |
r->bus_width << ORION_DEV_WIDTH_SHIFT |
((r->turn_off & ORION_TURN_OFF_EXT_MASK) ? ORION_TURN_OFF_EXT_BIT : 0) |
((r->acc_first & ORION_ACC_FIRST_EXT_MASK) ? ORION_ACC_FIRST_EXT_BIT : 0) |
((r->acc_next & ORION_ACC_NEXT_EXT_MASK) ? ORION_ACC_NEXT_EXT_BIT : 0) |
((w->ale_wr & ORION_ALE_WR_EXT_MASK) ? ORION_ALE_WR_EXT_BIT : 0) |
((w->wr_low & ORION_WR_LOW_EXT_MASK) ? ORION_WR_LOW_EXT_BIT : 0) |
((w->wr_high & ORION_WR_HIGH_EXT_MASK) ? ORION_WR_HIGH_EXT_BIT : 0) |
(r->badr_skew << ORION_BADR_SKEW_SHIFT) |
ORION_RESERVED;
writel(value, devbus->base);
}
static void devbus_armada_set_timing_params(struct devbus *devbus,
struct device_node *node,
struct devbus_read_params *r,
struct devbus_write_params *w)
{
u32 value;
/* Set read timings */
value = r->bus_width << ARMADA_DEV_WIDTH_SHIFT |
r->badr_skew << ARMADA_BADR_SKEW_SHIFT |
r->rd_hold << ARMADA_RD_HOLD_SHIFT |
r->acc_next << ARMADA_ACC_NEXT_SHIFT |
r->rd_setup << ARMADA_RD_SETUP_SHIFT |
r->acc_first << ARMADA_ACC_FIRST_SHIFT |
r->turn_off;
dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n",
devbus->base + ARMADA_READ_PARAM_OFFSET,
value);
writel(value, devbus->base + ARMADA_READ_PARAM_OFFSET);
/* Set write timings */
value = w->sync_enable << ARMADA_SYNC_ENABLE_SHIFT |
w->wr_low << ARMADA_WR_LOW_SHIFT |
w->wr_high << ARMADA_WR_HIGH_SHIFT |
w->ale_wr;
dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n",
devbus->base + ARMADA_WRITE_PARAM_OFFSET,
value);
writel(value, devbus->base + ARMADA_WRITE_PARAM_OFFSET);
}
static int mvebu_devbus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct devbus_read_params r;
struct devbus_write_params w;
struct devbus *devbus;
struct resource *res;
struct clk *clk;
unsigned long rate;
int err;
devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL);
if (!devbus)
return -ENOMEM;
devbus->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
devbus->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(devbus->base))
return PTR_ERR(devbus->base);
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_prepare_enable(clk);
/*
* Obtain clock period in picoseconds,
* we need this in order to convert timing
* parameters from cycles to picoseconds.
*/
rate = clk_get_rate(clk) / 1000;
devbus->tick_ps = 1000000000 / rate;
dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n",
devbus->tick_ps);
if (!of_property_read_bool(node, "devbus,keep-config")) {
/* Read the Device Tree node */
err = devbus_get_timing_params(devbus, node, &r, &w);
if (err < 0)
return err;
/* Set the new timing parameters */
if (of_device_is_compatible(node, "marvell,orion-devbus"))
devbus_orion_set_timing_params(devbus, node, &r, &w);
else
devbus_armada_set_timing_params(devbus, node, &r, &w);
}
/*
* We need to create a child device explicitly from here to
* guarantee that the child will be probed after the timing
* parameters for the bus are written.
*/
err = of_platform_populate(node, NULL, NULL, dev);
if (err < 0)
return err;
return 0;
}
static const struct of_device_id mvebu_devbus_of_match[] = {
{ .compatible = "marvell,mvebu-devbus" },
{ .compatible = "marvell,orion-devbus" },
{},
};
MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match);
static struct platform_driver mvebu_devbus_driver = {
.probe = mvebu_devbus_probe,
.driver = {
.name = "mvebu-devbus",
.of_match_table = mvebu_devbus_of_match,
},
};
static int __init mvebu_devbus_init(void)
{
return platform_driver_register(&mvebu_devbus_driver);
}
module_init(mvebu_devbus_init);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");
+154
View File
@@ -0,0 +1,154 @@
/*
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/gfp.h>
#include <memory/jedec_ddr.h>
#include <linux/export.h>
#include "of_memory.h"
/**
* of_get_min_tck() - extract min timing values for ddr
* @np: pointer to ddr device tree node
* @device: device requesting for min timing values
*
* Populates the lpddr2_min_tck structure by extracting data
* from device tree node. Returns a pointer to the populated
* structure. If any error in populating the structure, returns
* default min timings provided by JEDEC.
*/
const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
struct device *dev)
{
int ret = 0;
struct lpddr2_min_tck *min;
min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
if (!min)
goto default_min_tck;
ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
if (ret) {
devm_kfree(dev, min);
goto default_min_tck;
}
return min;
default_min_tck:
dev_warn(dev, "%s: using default min-tck values\n", __func__);
return &lpddr2_jedec_min_tck;
}
EXPORT_SYMBOL(of_get_min_tck);
static int of_do_get_timings(struct device_node *np,
struct lpddr2_timings *tim)
{
int ret;
ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
ret |= of_property_read_u32(np, "tWR", &tim->tWR);
ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
ret |= of_property_read_u32(np, "tXP", &tim->tXP);
ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
ret |= of_property_read_u32(np, "tDQSCK-max-derated",
&tim->tDQSCK_max_derated);
return ret;
}
/**
* of_get_ddr_timings() - extracts the ddr timings and updates no of
* frequencies available.
* @np_ddr: Pointer to ddr device tree node
* @dev: Device requesting for ddr timings
* @device_type: Type of ddr(LPDDR2 S2/S4)
* @nr_frequencies: No of frequencies available for ddr
* (updated by this function)
*
* Populates lpddr2_timings structure by extracting data from device
* tree node. Returns pointer to populated structure. If any error
* while populating, returns default timings provided by JEDEC.
*/
const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies)
{
struct lpddr2_timings *timings = NULL;
u32 arr_sz = 0, i = 0;
struct device_node *np_tim;
char *tim_compat = NULL;
switch (device_type) {
case DDR_TYPE_LPDDR2_S2:
case DDR_TYPE_LPDDR2_S4:
tim_compat = "jedec,lpddr2-timings";
break;
default:
dev_warn(dev, "%s: un-supported memory type\n", __func__);
}
for_each_child_of_node(np_ddr, np_tim)
if (of_device_is_compatible(np_tim, tim_compat))
arr_sz++;
if (arr_sz)
timings = devm_kcalloc(dev, arr_sz, sizeof(*timings),
GFP_KERNEL);
if (!timings)
goto default_timings;
for_each_child_of_node(np_ddr, np_tim) {
if (of_device_is_compatible(np_tim, tim_compat)) {
if (of_do_get_timings(np_tim, &timings[i])) {
devm_kfree(dev, timings);
goto default_timings;
}
i++;
}
}
*nr_frequencies = arr_sz;
return timings;
default_timings:
dev_warn(dev, "%s: using default timings\n", __func__);
*nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
return lpddr2_jedec_timings;
}
EXPORT_SYMBOL(of_get_ddr_timings);
+36
View File
@@ -0,0 +1,36 @@
/*
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_MEMORY_OF_REG_H
#define __LINUX_MEMORY_OF_REG_H
#if defined(CONFIG_OF) && defined(CONFIG_DDR)
extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
struct device *dev);
extern const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies);
#else
static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev)
{
return NULL;
}
static inline const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */
File diff suppressed because it is too large. Load diff
+321
View File
@@ -0,0 +1,321 @@
/*
* Memory controller driver for ARM PrimeCell PL172
* PrimeCell MultiPort Memory Controller (PL172)
*
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
*
* Based on:
* TI AEMIF driver, Copyright (C) 2010 - 2013 Texas Instruments Inc.
*
* 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/amba/bus.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/time.h>
#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * n)
#define MPMC_STATIC_CFG_MW_8BIT 0x0
#define MPMC_STATIC_CFG_MW_16BIT 0x1
#define MPMC_STATIC_CFG_MW_32BIT 0x2
#define MPMC_STATIC_CFG_PM BIT(3)
#define MPMC_STATIC_CFG_PC BIT(6)
#define MPMC_STATIC_CFG_PB BIT(7)
#define MPMC_STATIC_CFG_EW BIT(8)
#define MPMC_STATIC_CFG_B BIT(19)
#define MPMC_STATIC_CFG_P BIT(20)
#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * n)
#define MPMC_STATIC_WAIT_WEN_MAX 0x0f
#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * n)
#define MPMC_STATIC_WAIT_OEN_MAX 0x0f
#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * n)
#define MPMC_STATIC_WAIT_RD_MAX 0x1f
#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * n)
#define MPMC_STATIC_WAIT_PAGE_MAX 0x1f
#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * n)
#define MPMC_STATIC_WAIT_WR_MAX 0x1f
#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * n)
#define MPMC_STATIC_WAIT_TURN_MAX 0x0f
/* Maximum number of static chip selects */
#define PL172_MAX_CS 4
struct pl172_data {
void __iomem *base;
unsigned long rate;
struct clk *clk;
};
static int pl172_timing_prop(struct amba_device *adev,
const struct device_node *np, const char *name,
u32 reg_offset, u32 max, int start)
{
struct pl172_data *pl172 = amba_get_drvdata(adev);
int cycles;
u32 val;
if (!of_property_read_u32(np, name, &val)) {
cycles = DIV_ROUND_UP(val * pl172->rate, NSEC_PER_MSEC) - start;
if (cycles < 0) {
cycles = 0;
} else if (cycles > max) {
dev_err(&adev->dev, "%s timing too tight\n", name);
return -EINVAL;
}
writel(cycles, pl172->base + reg_offset);
}
dev_dbg(&adev->dev, "%s: %u cycle(s)\n", name, start +
readl(pl172->base + reg_offset));
return 0;
}
static int pl172_setup_static(struct amba_device *adev,
struct device_node *np, u32 cs)
{
struct pl172_data *pl172 = amba_get_drvdata(adev);
u32 cfg;
int ret;
/* MPMC static memory configuration */
if (!of_property_read_u32(np, "mpmc,memory-width", &cfg)) {
if (cfg == 8) {
cfg = MPMC_STATIC_CFG_MW_8BIT;
} else if (cfg == 16) {
cfg = MPMC_STATIC_CFG_MW_16BIT;
} else if (cfg == 32) {
cfg = MPMC_STATIC_CFG_MW_32BIT;
} else {
dev_err(&adev->dev, "invalid memory width cs%u\n", cs);
return -EINVAL;
}
} else {
dev_err(&adev->dev, "memory-width property required\n");
return -EINVAL;
}
if (of_property_read_bool(np, "mpmc,async-page-mode"))
cfg |= MPMC_STATIC_CFG_PM;
if (of_property_read_bool(np, "mpmc,cs-active-high"))
cfg |= MPMC_STATIC_CFG_PC;
if (of_property_read_bool(np, "mpmc,byte-lane-low"))
cfg |= MPMC_STATIC_CFG_PB;
if (of_property_read_bool(np, "mpmc,extended-wait"))
cfg |= MPMC_STATIC_CFG_EW;
if (amba_part(adev) == 0x172 &&
of_property_read_bool(np, "mpmc,buffer-enable"))
cfg |= MPMC_STATIC_CFG_B;
if (of_property_read_bool(np, "mpmc,write-protect"))
cfg |= MPMC_STATIC_CFG_P;
writel(cfg, pl172->base + MPMC_STATIC_CFG(cs));
dev_dbg(&adev->dev, "mpmc static config cs%u: 0x%08x\n", cs, cfg);
/* MPMC static memory timing */
ret = pl172_timing_prop(adev, np, "mpmc,write-enable-delay",
MPMC_STATIC_WAIT_WEN(cs),
MPMC_STATIC_WAIT_WEN_MAX, 1);
if (ret)
goto fail;
ret = pl172_timing_prop(adev, np, "mpmc,output-enable-delay",
MPMC_STATIC_WAIT_OEN(cs),
MPMC_STATIC_WAIT_OEN_MAX, 0);
if (ret)
goto fail;
ret = pl172_timing_prop(adev, np, "mpmc,read-access-delay",
MPMC_STATIC_WAIT_RD(cs),
MPMC_STATIC_WAIT_RD_MAX, 1);
if (ret)
goto fail;
ret = pl172_timing_prop(adev, np, "mpmc,page-mode-read-delay",
MPMC_STATIC_WAIT_PAGE(cs),
MPMC_STATIC_WAIT_PAGE_MAX, 1);
if (ret)
goto fail;
ret = pl172_timing_prop(adev, np, "mpmc,write-access-delay",
MPMC_STATIC_WAIT_WR(cs),
MPMC_STATIC_WAIT_WR_MAX, 2);
if (ret)
goto fail;
ret = pl172_timing_prop(adev, np, "mpmc,turn-round-delay",
MPMC_STATIC_WAIT_TURN(cs),
MPMC_STATIC_WAIT_TURN_MAX, 1);
if (ret)
goto fail;
return 0;
fail:
dev_err(&adev->dev, "failed to configure cs%u\n", cs);
return ret;
}
static int pl172_parse_cs_config(struct amba_device *adev,
struct device_node *np)
{
u32 cs;
if (!of_property_read_u32(np, "mpmc,cs", &cs)) {
if (cs >= PL172_MAX_CS) {
dev_err(&adev->dev, "cs%u invalid\n", cs);
return -EINVAL;
}
return pl172_setup_static(adev, np, cs);
}
dev_err(&adev->dev, "cs property required\n");
return -EINVAL;
}
static const char * const pl172_revisions[] = {"r1", "r2", "r2p3", "r2p4"};
static const char * const pl175_revisions[] = {"r1"};
static const char * const pl176_revisions[] = {"r0"};
static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device_node *child_np, *np = adev->dev.of_node;
struct device *dev = &adev->dev;
static const char *rev = "?";
struct pl172_data *pl172;
int ret;
if (amba_part(adev) == 0x172) {
if (amba_rev(adev) < ARRAY_SIZE(pl172_revisions))
rev = pl172_revisions[amba_rev(adev)];
} else if (amba_part(adev) == 0x175) {
if (amba_rev(adev) < ARRAY_SIZE(pl175_revisions))
rev = pl175_revisions[amba_rev(adev)];
} else if (amba_part(adev) == 0x176) {
if (amba_rev(adev) < ARRAY_SIZE(pl176_revisions))
rev = pl176_revisions[amba_rev(adev)];
}
dev_info(dev, "ARM PL%x revision %s\n", amba_part(adev), rev);
pl172 = devm_kzalloc(dev, sizeof(*pl172), GFP_KERNEL);
if (!pl172)
return -ENOMEM;
pl172->clk = devm_clk_get(dev, "mpmcclk");
if (IS_ERR(pl172->clk)) {
dev_err(dev, "no mpmcclk provided clock\n");
return PTR_ERR(pl172->clk);
}
ret = clk_prepare_enable(pl172->clk);
if (ret) {
dev_err(dev, "unable to mpmcclk enable clock\n");
return ret;
}
pl172->rate = clk_get_rate(pl172->clk) / MSEC_PER_SEC;
if (!pl172->rate) {
dev_err(dev, "unable to get mpmcclk clock rate\n");
ret = -EINVAL;
goto err_clk_enable;
}
ret = amba_request_regions(adev, NULL);
if (ret) {
dev_err(dev, "unable to request AMBA regions\n");
goto err_clk_enable;
}
pl172->base = devm_ioremap(dev, adev->res.start,
resource_size(&adev->res));
if (!pl172->base) {
dev_err(dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_no_ioremap;
}
amba_set_drvdata(adev, pl172);
/*
* Loop through each child node, which represent a chip select, and
* configure parameters and timing. If successful; populate devices
* under that node.
*/
for_each_available_child_of_node(np, child_np) {
ret = pl172_parse_cs_config(adev, child_np);
if (ret)
continue;
of_platform_populate(child_np, NULL, NULL, dev);
}
return 0;
err_no_ioremap:
amba_release_regions(adev);
err_clk_enable:
clk_disable_unprepare(pl172->clk);
return ret;
}
static int pl172_remove(struct amba_device *adev)
{
struct pl172_data *pl172 = amba_get_drvdata(adev);
clk_disable_unprepare(pl172->clk);
amba_release_regions(adev);
return 0;
}
static const struct amba_id pl172_ids[] = {
/* PrimeCell MPMC PL172, EMC found on NXP LPC18xx and LPC43xx */
{
.id = 0x07041172,
.mask = 0x3f0fffff,
},
/* PrimeCell MPMC PL175, EMC found on NXP LPC32xx */
{
.id = 0x07041175,
.mask = 0x3f0fffff,
},
/* PrimeCell MPMC PL176 */
{
.id = 0x89041176,
.mask = 0xff0fffff,
},
{ 0, 0 },
};
MODULE_DEVICE_TABLE(amba, pl172_ids);
static struct amba_driver pl172_driver = {
.drv = {
.name = "memory-pl172",
},
.probe = pl172_probe,
.remove = pl172_remove,
.id_table = pl172_ids,
};
module_amba_driver(pl172_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("PL172 Memory Controller Driver");
MODULE_LICENSE("GPL v2");
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
config SAMSUNG_MC
bool "Samsung Exynos Memory Controller support" if COMPILE_TEST
help
Support for the Memory Controller (MC) devices found on
Samsung Exynos SoCs.
if SAMSUNG_MC
config EXYNOS_SROM
bool "Exynos SROM controller driver" if COMPILE_TEST
depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM)
endif
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_EXYNOS_SROM) += exynos-srom.o
+212
View File
@@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2015 Samsung Electronics Co., Ltd.
// http://www.samsung.com/
//
// EXYNOS - SROM Controller support
// Author: Pankaj Dubey <pankaj.dubey@samsung.com>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "exynos-srom.h"
static const unsigned long exynos_srom_offsets[] = {
/* SROM side */
EXYNOS_SROM_BW,
EXYNOS_SROM_BC0,
EXYNOS_SROM_BC1,
EXYNOS_SROM_BC2,
EXYNOS_SROM_BC3,
};
/**
* struct exynos_srom_reg_dump: register dump of SROM Controller registers.
* @offset: srom register offset from the controller base address.
* @value: the value of register under the offset.
*/
struct exynos_srom_reg_dump {
u32 offset;
u32 value;
};
/**
* struct exynos_srom: platform data for exynos srom controller driver.
* @dev: platform device pointer
* @reg_base: srom base address
* @reg_offset: exynos_srom_reg_dump pointer to hold offset and its value.
*/
struct exynos_srom {
struct device *dev;
void __iomem *reg_base;
struct exynos_srom_reg_dump *reg_offset;
};
static struct exynos_srom_reg_dump *exynos_srom_alloc_reg_dump(
const unsigned long *rdump,
unsigned long nr_rdump)
{
struct exynos_srom_reg_dump *rd;
unsigned int i;
rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
if (!rd)
return NULL;
for (i = 0; i < nr_rdump; ++i)
rd[i].offset = rdump[i];
return rd;
}
static int exynos_srom_configure_bank(struct exynos_srom *srom,
struct device_node *np)
{
u32 bank, width, pmc = 0;
u32 timing[6];
u32 cs, bw;
if (of_property_read_u32(np, "reg", &bank))
return -EINVAL;
if (of_property_read_u32(np, "reg-io-width", &width))
width = 1;
if (of_property_read_bool(np, "samsung,srom-page-mode"))
pmc = 1 << EXYNOS_SROM_BCX__PMC__SHIFT;
if (of_property_read_u32_array(np, "samsung,srom-timing", timing,
ARRAY_SIZE(timing)))
return -EINVAL;
bank *= 4; /* Convert bank into shift/offset */
cs = 1 << EXYNOS_SROM_BW__BYTEENABLE__SHIFT;
if (width == 2)
cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT;
bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW);
bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank);
writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW);
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;
}
static int exynos_srom_probe(struct platform_device *pdev)
{
struct device_node *np, *child;
struct exynos_srom *srom;
struct device *dev = &pdev->dev;
bool bad_bank_config = false;
np = dev->of_node;
if (!np) {
dev_err(&pdev->dev, "could not find device info\n");
return -EINVAL;
}
srom = devm_kzalloc(&pdev->dev,
sizeof(struct exynos_srom), GFP_KERNEL);
if (!srom)
return -ENOMEM;
srom->dev = dev;
srom->reg_base = of_iomap(np, 0);
if (!srom->reg_base) {
dev_err(&pdev->dev, "iomap of exynos srom controller failed\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, srom);
srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets,
ARRAY_SIZE(exynos_srom_offsets));
if (!srom->reg_offset) {
iounmap(srom->reg_base);
return -ENOMEM;
}
for_each_child_of_node(np, child) {
if (exynos_srom_configure_bank(srom, child)) {
dev_err(dev,
"Could not decode bank configuration for %s\n",
child->name);
bad_bank_config = true;
}
}
/*
* If any bank failed to configure, we still provide suspend/resume,
* but do not probe child devices
*/
if (bad_bank_config)
return 0;
return of_platform_populate(np, NULL, NULL, dev);
}
#ifdef CONFIG_PM_SLEEP
static void exynos_srom_save(void __iomem *base,
struct exynos_srom_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd)
rd->value = readl(base + rd->offset);
}
static void exynos_srom_restore(void __iomem *base,
const struct exynos_srom_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd)
writel(rd->value, base + rd->offset);
}
static int exynos_srom_suspend(struct device *dev)
{
struct exynos_srom *srom = dev_get_drvdata(dev);
exynos_srom_save(srom->reg_base, srom->reg_offset,
ARRAY_SIZE(exynos_srom_offsets));
return 0;
}
static int exynos_srom_resume(struct device *dev)
{
struct exynos_srom *srom = dev_get_drvdata(dev);
exynos_srom_restore(srom->reg_base, srom->reg_offset,
ARRAY_SIZE(exynos_srom_offsets));
return 0;
}
#endif
static const struct of_device_id of_exynos_srom_ids[] = {
{
.compatible = "samsung,exynos4210-srom",
},
{},
};
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,
.driver = {
.name = "exynos-srom",
.of_match_table = of_exynos_srom_ids,
.pm = &exynos_srom_pm_ops,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(exynos_srom_driver);
+48
View File
@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Exynos SROMC register definitions
*/
#ifndef __EXYNOS_SROM_H
#define __EXYNOS_SROM_H __FILE__
#define EXYNOS_SROMREG(x) (x)
#define EXYNOS_SROM_BW EXYNOS_SROMREG(0x0)
#define EXYNOS_SROM_BC0 EXYNOS_SROMREG(0x4)
#define EXYNOS_SROM_BC1 EXYNOS_SROMREG(0x8)
#define EXYNOS_SROM_BC2 EXYNOS_SROMREG(0xc)
#define EXYNOS_SROM_BC3 EXYNOS_SROMREG(0x10)
#define EXYNOS_SROM_BC4 EXYNOS_SROMREG(0x14)
#define EXYNOS_SROM_BC5 EXYNOS_SROMREG(0x18)
/* one register BW holds 4 x 4-bit packed settings for NCS0 - NCS3 */
#define EXYNOS_SROM_BW__DATAWIDTH__SHIFT 0
#define EXYNOS_SROM_BW__ADDRMODE__SHIFT 1
#define EXYNOS_SROM_BW__WAITENABLE__SHIFT 2
#define EXYNOS_SROM_BW__BYTEENABLE__SHIFT 3
#define EXYNOS_SROM_BW__CS_MASK 0xf
#define EXYNOS_SROM_BW__NCS0__SHIFT 0
#define EXYNOS_SROM_BW__NCS1__SHIFT 4
#define EXYNOS_SROM_BW__NCS2__SHIFT 8
#define EXYNOS_SROM_BW__NCS3__SHIFT 12
#define EXYNOS_SROM_BW__NCS4__SHIFT 16
#define EXYNOS_SROM_BW__NCS5__SHIFT 20
/* applies to same to BCS0 - BCS3 */
#define EXYNOS_SROM_BCX__PMC__SHIFT 0
#define EXYNOS_SROM_BCX__TACP__SHIFT 4
#define EXYNOS_SROM_BCX__TCAH__SHIFT 8
#define EXYNOS_SROM_BCX__TCOH__SHIFT 12
#define EXYNOS_SROM_BCX__TACC__SHIFT 16
#define EXYNOS_SROM_BCX__TCOS__SHIFT 24
#define EXYNOS_SROM_BCX__TACS__SHIFT 28
#endif /* __EXYNOS_SROM_H */
+17
View File
@@ -0,0 +1,17 @@
config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support"
default y
depends on ARCH_TEGRA
help
This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs.
config TEGRA124_EMC
bool "NVIDIA Tegra124 External Memory Controller driver"
default y
depends on TEGRA_MC && ARCH_TEGRA_124_SOC
help
This driver is for the External Memory Controller (EMC) found on
Tegra124 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
tegra-mc-y := mc.o
tegra-mc-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o
tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o
tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o
tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
+727
View File
@@ -0,0 +1,727 @@
/*
* Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
*
* 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/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <soc/tegra/fuse.h>
#include "mc.h"
#define MC_INTSTATUS 0x000
#define MC_INTMASK 0x004
#define MC_ERR_STATUS 0x08
#define MC_ERR_STATUS_TYPE_SHIFT 28
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
#define MC_ERR_STATUS_READABLE (1 << 27)
#define MC_ERR_STATUS_WRITABLE (1 << 26)
#define MC_ERR_STATUS_NONSECURE (1 << 25)
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
#define MC_ERR_STATUS_SECURITY (1 << 17)
#define MC_ERR_STATUS_RW (1 << 16)
#define MC_ERR_ADR 0x0c
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
#define MC_EMEM_ARB_MISC0 0xd8
#define MC_EMEM_ADR_CFG 0x54
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
static const struct of_device_id tegra_mc_of_match[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
{ .compatible = "nvidia,tegra20-mc", .data = &tegra20_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
{ .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
{ .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
{ .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc },
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
{ .compatible = "nvidia,tegra210-mc", .data = &tegra210_mc_soc },
#endif
{ }
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
static int terga_mc_block_dma_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) | BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static bool terga_mc_dma_idling_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0;
}
static int terga_mc_unblock_dma_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int terga_mc_reset_status_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0;
}
const struct tegra_mc_reset_ops terga_mc_reset_ops_common = {
.block_dma = terga_mc_block_dma_common,
.dma_idling = terga_mc_dma_idling_common,
.unblock_dma = terga_mc_unblock_dma_common,
.reset_status = terga_mc_reset_status_common,
};
static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct tegra_mc, reset);
}
static const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc,
unsigned long id)
{
unsigned int i;
for (i = 0; i < mc->soc->num_resets; i++)
if (mc->soc->resets[i].id == id)
return &mc->soc->resets[i];
return NULL;
}
static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
int retries = 500;
int err;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
if (rst_ops->block_dma) {
/* block clients DMA requests */
err = rst_ops->block_dma(mc, rst);
if (err) {
dev_err(mc->dev, "Failed to block %s DMA: %d\n",
rst->name, err);
return err;
}
}
if (rst_ops->dma_idling) {
/* wait for completion of the outstanding DMA requests */
while (!rst_ops->dma_idling(mc, rst)) {
if (!retries--) {
dev_err(mc->dev, "Failed to flush %s DMA\n",
rst->name);
return -EBUSY;
}
usleep_range(10, 100);
}
}
if (rst_ops->hotreset_assert) {
/* clear clients DMA requests sitting before arbitration */
err = rst_ops->hotreset_assert(mc, rst);
if (err) {
dev_err(mc->dev, "Failed to hot reset %s: %d\n",
rst->name, err);
return err;
}
}
return 0;
}
static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
int err;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
if (rst_ops->hotreset_deassert) {
/* take out client from hot reset */
err = rst_ops->hotreset_deassert(mc, rst);
if (err) {
dev_err(mc->dev, "Failed to deassert hot reset %s: %d\n",
rst->name, err);
return err;
}
}
if (rst_ops->unblock_dma) {
/* allow new DMA requests to proceed to arbitration */
err = rst_ops->unblock_dma(mc, rst);
if (err) {
dev_err(mc->dev, "Failed to unblock %s DMA : %d\n",
rst->name, err);
return err;
}
}
return 0;
}
static int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct tegra_mc *mc = reset_to_mc(rcdev);
const struct tegra_mc_reset_ops *rst_ops;
const struct tegra_mc_reset *rst;
rst = tegra_mc_reset_find(mc, id);
if (!rst)
return -ENODEV;
rst_ops = mc->soc->reset_ops;
if (!rst_ops)
return -ENODEV;
return rst_ops->reset_status(mc, rst);
}
static const struct reset_control_ops tegra_mc_reset_ops = {
.assert = tegra_mc_hotreset_assert,
.deassert = tegra_mc_hotreset_deassert,
.status = tegra_mc_hotreset_status,
};
static int tegra_mc_reset_setup(struct tegra_mc *mc)
{
int err;
mc->reset.ops = &tegra_mc_reset_ops;
mc->reset.owner = THIS_MODULE;
mc->reset.of_node = mc->dev->of_node;
mc->reset.of_reset_n_cells = 1;
mc->reset.nr_resets = mc->soc->num_resets;
err = reset_controller_register(&mc->reset);
if (err < 0)
return err;
return 0;
}
static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
{
unsigned long long tick;
unsigned int i;
u32 value;
/* compute the number of MC clock cycles per tick */
tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk);
do_div(tick, NSEC_PER_SEC);
value = readl(mc->regs + MC_EMEM_ARB_CFG);
value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK;
value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick);
writel(value, mc->regs + MC_EMEM_ARB_CFG);
/* write latency allowance defaults */
for (i = 0; i < mc->soc->num_clients; i++) {
const struct tegra_mc_la *la = &mc->soc->clients[i].la;
u32 value;
value = readl(mc->regs + la->reg);
value &= ~(la->mask << la->shift);
value |= (la->def & la->mask) << la->shift;
writel(value, mc->regs + la->reg);
}
return 0;
}
void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
{
unsigned int i;
struct tegra_mc_timing *timing = NULL;
for (i = 0; i < mc->num_timings; i++) {
if (mc->timings[i].rate == rate) {
timing = &mc->timings[i];
break;
}
}
if (!timing) {
dev_err(mc->dev, "no memory timing registered for rate %lu\n",
rate);
return;
}
for (i = 0; i < mc->soc->num_emem_regs; ++i)
mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
}
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
{
u8 dram_count;
dram_count = mc_readl(mc, MC_EMEM_ADR_CFG);
dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV;
dram_count++;
return dram_count;
}
static int load_one_timing(struct tegra_mc *mc,
struct tegra_mc_timing *timing,
struct device_node *node)
{
int err;
u32 tmp;
err = of_property_read_u32(node, "clock-frequency", &tmp);
if (err) {
dev_err(mc->dev,
"timing %s: failed to read rate\n", node->name);
return err;
}
timing->rate = tmp;
timing->emem_data = devm_kcalloc(mc->dev, mc->soc->num_emem_regs,
sizeof(u32), GFP_KERNEL);
if (!timing->emem_data)
return -ENOMEM;
err = of_property_read_u32_array(node, "nvidia,emem-configuration",
timing->emem_data,
mc->soc->num_emem_regs);
if (err) {
dev_err(mc->dev,
"timing %s: failed to read EMEM configuration\n",
node->name);
return err;
}
return 0;
}
static int load_timings(struct tegra_mc *mc, struct device_node *node)
{
struct device_node *child;
struct tegra_mc_timing *timing;
int child_count = of_get_child_count(node);
int i = 0, err;
mc->timings = devm_kcalloc(mc->dev, child_count, sizeof(*timing),
GFP_KERNEL);
if (!mc->timings)
return -ENOMEM;
mc->num_timings = child_count;
for_each_child_of_node(node, child) {
timing = &mc->timings[i++];
err = load_one_timing(mc, timing, child);
if (err) {
of_node_put(child);
return err;
}
}
return 0;
}
static int tegra_mc_setup_timings(struct tegra_mc *mc)
{
struct device_node *node;
u32 ram_code, node_ram_code;
int err;
ram_code = tegra_read_ram_code();
mc->num_timings = 0;
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))
continue;
err = load_timings(mc, node);
of_node_put(node);
if (err)
return err;
break;
}
if (mc->num_timings == 0)
dev_warn(mc->dev,
"no memory timings for RAM code %u registered\n",
ram_code);
return 0;
}
static const char *const status_names[32] = {
[ 1] = "External interrupt",
[ 6] = "EMEM address decode error",
[ 7] = "GART page fault",
[ 8] = "Security violation",
[ 9] = "EMEM arbitration error",
[10] = "Page fault",
[11] = "Invalid APB ASID update",
[12] = "VPR violation",
[13] = "Secure carveout violation",
[16] = "MTS carveout violation",
};
static const char *const error_names[8] = {
[2] = "EMEM decode error",
[3] = "TrustZone violation",
[4] = "Carveout violation",
[6] = "SMMU translation error",
};
static irqreturn_t tegra_mc_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status;
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
if (!status)
return IRQ_NONE;
for_each_set_bit(bit, &status, 32) {
const char *error = status_names[bit] ?: "unknown";
const char *client = "unknown", *desc;
const char *direction, *secure;
phys_addr_t addr = 0;
unsigned int i;
char perm[7];
u8 id, type;
u32 value;
value = mc_readl(mc, MC_ERR_STATUS);
#ifdef CONFIG_PHYS_ADDR_T_64BIT
if (mc->soc->num_address_bits > 32) {
addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) &
MC_ERR_STATUS_ADR_HI_MASK);
addr <<= 32;
}
#endif
if (value & MC_ERR_STATUS_RW)
direction = "write";
else
direction = "read";
if (value & MC_ERR_STATUS_SECURITY)
secure = "secure ";
else
secure = "";
id = value & mc->soc->client_id_mask;
for (i = 0; i < mc->soc->num_clients; i++) {
if (mc->soc->clients[i].id == id) {
client = mc->soc->clients[i].name;
break;
}
}
type = (value & MC_ERR_STATUS_TYPE_MASK) >>
MC_ERR_STATUS_TYPE_SHIFT;
desc = error_names[type];
switch (value & MC_ERR_STATUS_TYPE_MASK) {
case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE:
perm[0] = ' ';
perm[1] = '[';
if (value & MC_ERR_STATUS_READABLE)
perm[2] = 'R';
else
perm[2] = '-';
if (value & MC_ERR_STATUS_WRITABLE)
perm[3] = 'W';
else
perm[3] = '-';
if (value & MC_ERR_STATUS_NONSECURE)
perm[4] = '-';
else
perm[4] = 'S';
perm[5] = ']';
perm[6] = '\0';
break;
default:
perm[0] = '\0';
break;
}
value = mc_readl(mc, MC_ERR_ADR);
addr |= value;
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n",
client, secure, direction, &addr, error,
desc, perm);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
return IRQ_HANDLED;
}
static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status;
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask;
if (!status)
return IRQ_NONE;
for_each_set_bit(bit, &status, 32) {
const char *direction = "read", *secure = "";
const char *error = status_names[bit];
const char *client, *desc;
phys_addr_t addr;
u32 value, reg;
u8 id, type;
switch (BIT(bit)) {
case MC_INT_DECERR_EMEM:
reg = MC_DECERR_EMEM_OTHERS_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
desc = error_names[2];
if (value & BIT(31))
direction = "write";
break;
case MC_INT_INVALID_GART_PAGE:
dev_err_ratelimited(mc->dev, "%s\n", error);
continue;
case MC_INT_SECURITY_VIOLATION:
reg = MC_SECURITY_VIOLATION_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
type = (value & BIT(30)) ? 4 : 3;
desc = error_names[type];
secure = "secure ";
if (value & BIT(31))
direction = "write";
break;
default:
continue;
}
client = mc->soc->clients[id].name;
addr = mc_readl(mc, reg + sizeof(u32));
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
client, secure, direction, &addr, error,
desc);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
return IRQ_HANDLED;
}
static int tegra_mc_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct resource *res;
struct tegra_mc *mc;
void *isr;
int err;
match = of_match_node(tegra_mc_of_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
platform_set_drvdata(pdev, mc);
spin_lock_init(&mc->lock);
mc->soc = match->data;
mc->dev = &pdev->dev;
/* length of MC tick in nanoseconds */
mc->tick = 30;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mc->regs))
return PTR_ERR(mc->regs);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
if (mc->soc == &tegra20_mc_soc) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
mc->regs2 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mc->regs2))
return PTR_ERR(mc->regs2);
isr = tegra20_mc_irq;
} else
#endif
{
mc->clk = devm_clk_get(&pdev->dev, "mc");
if (IS_ERR(mc->clk)) {
dev_err(&pdev->dev, "failed to get MC clock: %ld\n",
PTR_ERR(mc->clk));
return PTR_ERR(mc->clk);
}
err = tegra_mc_setup_latency_allowance(mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup latency allowance: %d\n",
err);
return err;
}
isr = tegra_mc_irq;
err = tegra_mc_setup_timings(mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup timings: %d\n",
err);
return err;
}
}
mc->irq = platform_get_irq(pdev, 0);
if (mc->irq < 0) {
dev_err(&pdev->dev, "interrupt not specified\n");
return mc->irq;
}
WARN(!mc->soc->client_id_mask, "Missing client ID mask for this SoC\n");
mc_writel(mc, mc->soc->intmask, MC_INTMASK);
err = devm_request_irq(&pdev->dev, mc->irq, isr, IRQF_SHARED,
dev_name(&pdev->dev), mc);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq,
err);
return err;
}
err = tegra_mc_reset_setup(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to register reset controller: %d\n",
err);
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
if (IS_ERR(mc->smmu))
dev_err(&pdev->dev, "failed to probe SMMU: %ld\n",
PTR_ERR(mc->smmu));
}
return 0;
}
static struct platform_driver tegra_mc_driver = {
.driver = {
.name = "tegra-mc",
.of_match_table = tegra_mc_of_match,
.suppress_bind_attrs = true,
},
.prevent_deferred_probe = true,
.probe = tegra_mc_probe,
};
static int tegra_mc_init(void)
{
return platform_driver_register(&tegra_mc_driver);
}
arch_initcall(tegra_mc_init);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver");
MODULE_LICENSE("GPL v2");
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
*
* 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.
*/
#ifndef MEMORY_TEGRA_MC_H
#define MEMORY_TEGRA_MC_H
#include <linux/io.h>
#include <linux/types.h>
#include <soc/tegra/mc.h>
#define MC_INT_DECERR_MTS (1 << 16)
#define MC_INT_SECERR_SEC (1 << 13)
#define MC_INT_DECERR_VPR (1 << 12)
#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
#define MC_INT_ARBITRATION_EMEM (1 << 9)
#define MC_INT_SECURITY_VIOLATION (1 << 8)
#define MC_INT_INVALID_GART_PAGE (1 << 7)
#define MC_INT_DECERR_EMEM (1 << 6)
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
{
if (mc->regs2 && offset >= 0x24)
return readl(mc->regs2 + offset - 0x3c);
return readl(mc->regs + offset);
}
static inline void mc_writel(struct tegra_mc *mc, u32 value,
unsigned long offset)
{
if (mc->regs2 && offset >= 0x24)
return writel(value, mc->regs2 + offset - 0x3c);
writel(value, mc->regs + offset);
}
extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common;
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
extern const struct tegra_mc_soc tegra20_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
extern const struct tegra_mc_soc tegra30_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
extern const struct tegra_mc_soc tegra114_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_mc_soc tegra124_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
extern const struct tegra_mc_soc tegra132_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
extern const struct tegra_mc_soc tegra210_mc_soc;
#endif
#endif /* MEMORY_TEGRA_MC_H */
+981
View File
@@ -0,0 +1,981 @@
/*
* Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved.
*
* 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/of.h>
#include <linux/mm.h>
#include <dt-bindings/memory/tegra114-mc.h>
#include "mc.h"
static const struct tegra_mc_client tegra114_mc_clients[] = {
{
.id = 0x00,
.name = "ptcr",
.swgroup = TEGRA_SWGROUP_PTC,
}, {
.id = 0x01,
.name = "display0a",
.swgroup = TEGRA_SWGROUP_DC,
.smmu = {
.reg = 0x228,
.bit = 1,
},
.la = {
.reg = 0x2e8,
.shift = 0,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x02,
.name = "display0ab",
.swgroup = TEGRA_SWGROUP_DCB,
.smmu = {
.reg = 0x228,
.bit = 2,
},
.la = {
.reg = 0x2f4,
.shift = 0,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x03,
.name = "display0b",
.swgroup = TEGRA_SWGROUP_DC,
.smmu = {
.reg = 0x228,
.bit = 3,
},
.la = {
.reg = 0x2e8,
.shift = 16,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x04,
.name = "display0bb",
.swgroup = TEGRA_SWGROUP_DCB,
.smmu = {
.reg = 0x228,
.bit = 4,
},
.la = {
.reg = 0x2f4,
.shift = 16,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x05,
.name = "display0c",
.swgroup = TEGRA_SWGROUP_DC,
.smmu = {
.reg = 0x228,
.bit = 5,
},
.la = {
.reg = 0x2ec,
.shift = 0,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x06,
.name = "display0cb",
.swgroup = TEGRA_SWGROUP_DCB,
.smmu = {
.reg = 0x228,
.bit = 6,
},
.la = {
.reg = 0x2f8,
.shift = 0,
.mask = 0xff,
.def = 0x4e,
},
}, {
.id = 0x09,
.name = "eppup",
.swgroup = TEGRA_SWGROUP_EPP,
.smmu = {
.reg = 0x228,
.bit = 9,
},
.la = {
.reg = 0x300,
.shift = 0,
.mask = 0xff,
.def = 0x33,
},
}, {
.id = 0x0a,
.name = "g2pr",
.swgroup = TEGRA_SWGROUP_G2,
.smmu = {
.reg = 0x228,
.bit = 10,
},
.la = {
.reg = 0x308,
.shift = 0,
.mask = 0xff,
.def = 0x09,
},
}, {
.id = 0x0b,
.name = "g2sr",
.swgroup = TEGRA_SWGROUP_G2,
.smmu = {
.reg = 0x228,
.bit = 11,
},
.la = {
.reg = 0x308,
.shift = 16,
.mask = 0xff,
.def = 0x09,
},
}, {
.id = 0x0f,
.name = "avpcarm7r",
.swgroup = TEGRA_SWGROUP_AVPC,
.smmu = {
.reg = 0x228,
.bit = 15,
},
.la = {
.reg = 0x2e4,
.shift = 0,
.mask = 0xff,
.def = 0x04,
},
}, {
.id = 0x10,
.name = "displayhc",
.swgroup = TEGRA_SWGROUP_DC,
.smmu = {
.reg = 0x228,
.bit = 16,
},
.la = {
.reg = 0x2f0,
.shift = 0,
.mask = 0xff,
.def = 0x68,
},
}, {
.id = 0x11,
.name = "displayhcb",
.swgroup = TEGRA_SWGROUP_DCB,
.smmu = {
.reg = 0x228,
.bit = 17,
},
.la = {
.reg = 0x2fc,
.shift = 0,
.mask = 0xff,
.def = 0x68,
},
}, {
.id = 0x12,
.name = "fdcdrd",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x228,
.bit = 18,
},
.la = {
.reg = 0x334,
.shift = 0,
.mask = 0xff,
.def = 0x0c,
},
}, {
.id = 0x13,
.name = "fdcdrd2",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x228,
.bit = 19,
},
.la = {
.reg = 0x33c,
.shift = 0,
.mask = 0xff,
.def = 0x0c,
},
}, {
.id = 0x14,
.name = "g2dr",
.swgroup = TEGRA_SWGROUP_G2,
.smmu = {
.reg = 0x228,
.bit = 20,
},
.la = {
.reg = 0x30c,
.shift = 0,
.mask = 0xff,
.def = 0x0a,
},
}, {
.id = 0x15,
.name = "hdar",
.swgroup = TEGRA_SWGROUP_HDA,
.smmu = {
.reg = 0x228,
.bit = 21,
},
.la = {
.reg = 0x318,
.shift = 0,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x16,
.name = "host1xdmar",
.swgroup = TEGRA_SWGROUP_HC,
.smmu = {
.reg = 0x228,
.bit = 22,
},
.la = {
.reg = 0x310,
.shift = 0,
.mask = 0xff,
.def = 0x10,
},
}, {
.id = 0x17,
.name = "host1xr",
.swgroup = TEGRA_SWGROUP_HC,
.smmu = {
.reg = 0x228,
.bit = 23,
},
.la = {
.reg = 0x310,
.shift = 16,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x18,
.name = "idxsrd",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x228,
.bit = 24,
},
.la = {
.reg = 0x334,
.shift = 16,
.mask = 0xff,
.def = 0x0b,
},
}, {
.id = 0x1c,
.name = "msencsrd",
.swgroup = TEGRA_SWGROUP_MSENC,
.smmu = {
.reg = 0x228,
.bit = 28,
},
.la = {
.reg = 0x328,
.shift = 0,
.mask = 0xff,
.def = 0x80,
},
}, {
.id = 0x1d,
.name = "ppcsahbdmar",
.swgroup = TEGRA_SWGROUP_PPCS,
.smmu = {
.reg = 0x228,
.bit = 29,
},
.la = {
.reg = 0x344,
.shift = 0,
.mask = 0xff,
.def = 0x50,
},
}, {
.id = 0x1e,
.name = "ppcsahbslvr",
.swgroup = TEGRA_SWGROUP_PPCS,
.smmu = {
.reg = 0x228,
.bit = 30,
},
.la = {
.reg = 0x344,
.shift = 16,
.mask = 0xff,
.def = 0xe8,
},
}, {
.id = 0x20,
.name = "texl2srd",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x22c,
.bit = 0,
},
.la = {
.reg = 0x338,
.shift = 0,
.mask = 0xff,
.def = 0x0c,
},
}, {
.id = 0x22,
.name = "vdebsevr",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 2,
},
.la = {
.reg = 0x354,
.shift = 0,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x23,
.name = "vdember",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 3,
},
.la = {
.reg = 0x354,
.shift = 16,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x24,
.name = "vdemcer",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 4,
},
.la = {
.reg = 0x358,
.shift = 0,
.mask = 0xff,
.def = 0xb8,
},
}, {
.id = 0x25,
.name = "vdetper",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 5,
},
.la = {
.reg = 0x358,
.shift = 16,
.mask = 0xff,
.def = 0xee,
},
}, {
.id = 0x26,
.name = "mpcorelpr",
.swgroup = TEGRA_SWGROUP_MPCORELP,
.la = {
.reg = 0x324,
.shift = 0,
.mask = 0xff,
.def = 0x04,
},
}, {
.id = 0x27,
.name = "mpcorer",
.swgroup = TEGRA_SWGROUP_MPCORE,
.la = {
.reg = 0x320,
.shift = 0,
.mask = 0xff,
.def = 0x04,
},
}, {
.id = 0x28,
.name = "eppu",
.swgroup = TEGRA_SWGROUP_EPP,
.smmu = {
.reg = 0x22c,
.bit = 8,
},
.la = {
.reg = 0x300,
.shift = 16,
.mask = 0xff,
.def = 0x33,
},
}, {
.id = 0x29,
.name = "eppv",
.swgroup = TEGRA_SWGROUP_EPP,
.smmu = {
.reg = 0x22c,
.bit = 9,
},
.la = {
.reg = 0x304,
.shift = 0,
.mask = 0xff,
.def = 0x6c,
},
}, {
.id = 0x2a,
.name = "eppy",
.swgroup = TEGRA_SWGROUP_EPP,
.smmu = {
.reg = 0x22c,
.bit = 10,
},
.la = {
.reg = 0x304,
.shift = 16,
.mask = 0xff,
.def = 0x6c,
},
}, {
.id = 0x2b,
.name = "msencswr",
.swgroup = TEGRA_SWGROUP_MSENC,
.smmu = {
.reg = 0x22c,
.bit = 11,
},
.la = {
.reg = 0x328,
.shift = 16,
.mask = 0xff,
.def = 0x80,
},
}, {
.id = 0x2c,
.name = "viwsb",
.swgroup = TEGRA_SWGROUP_VI,
.smmu = {
.reg = 0x22c,
.bit = 12,
},
.la = {
.reg = 0x364,
.shift = 0,
.mask = 0xff,
.def = 0x47,
},
}, {
.id = 0x2d,
.name = "viwu",
.swgroup = TEGRA_SWGROUP_VI,
.smmu = {
.reg = 0x22c,
.bit = 13,
},
.la = {
.reg = 0x368,
.shift = 0,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x2e,
.name = "viwv",
.swgroup = TEGRA_SWGROUP_VI,
.smmu = {
.reg = 0x22c,
.bit = 14,
},
.la = {
.reg = 0x368,
.shift = 16,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x2f,
.name = "viwy",
.swgroup = TEGRA_SWGROUP_VI,
.smmu = {
.reg = 0x22c,
.bit = 15,
},
.la = {
.reg = 0x36c,
.shift = 0,
.mask = 0xff,
.def = 0x47,
},
}, {
.id = 0x30,
.name = "g2dw",
.swgroup = TEGRA_SWGROUP_G2,
.smmu = {
.reg = 0x22c,
.bit = 16,
},
.la = {
.reg = 0x30c,
.shift = 16,
.mask = 0xff,
.def = 0x9,
},
}, {
.id = 0x32,
.name = "avpcarm7w",
.swgroup = TEGRA_SWGROUP_AVPC,
.smmu = {
.reg = 0x22c,
.bit = 18,
},
.la = {
.reg = 0x2e4,
.shift = 16,
.mask = 0xff,
.def = 0x0e,
},
}, {
.id = 0x33,
.name = "fdcdwr",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x22c,
.bit = 19,
},
.la = {
.reg = 0x338,
.shift = 16,
.mask = 0xff,
.def = 0x10,
},
}, {
.id = 0x34,
.name = "fdcwr2",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x22c,
.bit = 20,
},
.la = {
.reg = 0x340,
.shift = 0,
.mask = 0xff,
.def = 0x10,
},
}, {
.id = 0x35,
.name = "hdaw",
.swgroup = TEGRA_SWGROUP_HDA,
.smmu = {
.reg = 0x22c,
.bit = 21,
},
.la = {
.reg = 0x318,
.shift = 16,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x36,
.name = "host1xw",
.swgroup = TEGRA_SWGROUP_HC,
.smmu = {
.reg = 0x22c,
.bit = 22,
},
.la = {
.reg = 0x314,
.shift = 0,
.mask = 0xff,
.def = 0x25,
},
}, {
.id = 0x37,
.name = "ispw",
.swgroup = TEGRA_SWGROUP_ISP,
.smmu = {
.reg = 0x22c,
.bit = 23,
},
.la = {
.reg = 0x31c,
.shift = 0,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x38,
.name = "mpcorelpw",
.swgroup = TEGRA_SWGROUP_MPCORELP,
.la = {
.reg = 0x324,
.shift = 16,
.mask = 0xff,
.def = 0x80,
},
}, {
.id = 0x39,
.name = "mpcorew",
.swgroup = TEGRA_SWGROUP_MPCORE,
.la = {
.reg = 0x320,
.shift = 16,
.mask = 0xff,
.def = 0x0e,
},
}, {
.id = 0x3b,
.name = "ppcsahbdmaw",
.swgroup = TEGRA_SWGROUP_PPCS,
.smmu = {
.reg = 0x22c,
.bit = 27,
},
.la = {
.reg = 0x348,
.shift = 0,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x3c,
.name = "ppcsahbslvw",
.swgroup = TEGRA_SWGROUP_PPCS,
.smmu = {
.reg = 0x22c,
.bit = 28,
},
.la = {
.reg = 0x348,
.shift = 16,
.mask = 0xff,
.def = 0xe8,
},
}, {
.id = 0x3e,
.name = "vdebsevw",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 30,
},
.la = {
.reg = 0x35c,
.shift = 0,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x3f,
.name = "vdedbgw",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x22c,
.bit = 31,
},
.la = {
.reg = 0x35c,
.shift = 16,
.mask = 0xff,
.def = 0xff,
},
}, {
.id = 0x40,
.name = "vdembew",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x230,
.bit = 0,
},
.la = {
.reg = 0x360,
.shift = 0,
.mask = 0xff,
.def = 0x89,
},
}, {
.id = 0x41,
.name = "vdetpmw",
.swgroup = TEGRA_SWGROUP_VDE,
.smmu = {
.reg = 0x230,
.bit = 1,
},
.la = {
.reg = 0x360,
.shift = 16,
.mask = 0xff,
.def = 0x59,
},
}, {
.id = 0x4a,
.name = "xusb_hostr",
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
.smmu = {
.reg = 0x230,
.bit = 10,
},
.la = {
.reg = 0x37c,
.shift = 0,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x4b,
.name = "xusb_hostw",
.swgroup = TEGRA_SWGROUP_XUSB_HOST,
.smmu = {
.reg = 0x230,
.bit = 11,
},
.la = {
.reg = 0x37c,
.shift = 16,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x4c,
.name = "xusb_devr",
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
.smmu = {
.reg = 0x230,
.bit = 12,
},
.la = {
.reg = 0x380,
.shift = 0,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x4d,
.name = "xusb_devw",
.swgroup = TEGRA_SWGROUP_XUSB_DEV,
.smmu = {
.reg = 0x230,
.bit = 13,
},
.la = {
.reg = 0x380,
.shift = 16,
.mask = 0xff,
.def = 0xa5,
},
}, {
.id = 0x4e,
.name = "fdcdwr3",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x230,
.bit = 14,
},
.la = {
.reg = 0x388,
.shift = 0,
.mask = 0xff,
.def = 0x10,
},
}, {
.id = 0x4f,
.name = "fdcdrd3",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x230,
.bit = 15,
},
.la = {
.reg = 0x384,
.shift = 0,
.mask = 0xff,
.def = 0x0c,
},
}, {
.id = 0x50,
.name = "fdcwr4",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x230,
.bit = 16,
},
.la = {
.reg = 0x388,
.shift = 16,
.mask = 0xff,
.def = 0x10,
},
}, {
.id = 0x51,
.name = "fdcrd4",
.swgroup = TEGRA_SWGROUP_NV,
.smmu = {
.reg = 0x230,
.bit = 17,
},
.la = {
.reg = 0x384,
.shift = 16,
.mask = 0xff,
.def = 0x0c,
},
}, {
.id = 0x52,
.name = "emucifr",
.swgroup = TEGRA_SWGROUP_EMUCIF,
.la = {
.reg = 0x38c,
.shift = 0,
.mask = 0xff,
.def = 0x04,
},
}, {
.id = 0x53,
.name = "emucifw",
.swgroup = TEGRA_SWGROUP_EMUCIF,
.la = {
.reg = 0x38c,
.shift = 16,
.mask = 0xff,
.def = 0x0e,
},
}, {
.id = 0x54,
.name = "tsecsrd",
.swgroup = TEGRA_SWGROUP_TSEC,
.smmu = {
.reg = 0x230,
.bit = 20,
},
.la = {
.reg = 0x390,
.shift = 0,
.mask = 0xff,
.def = 0x50,
},
}, {
.id = 0x55,
.name = "tsecswr",
.swgroup = TEGRA_SWGROUP_TSEC,
.smmu = {
.reg = 0x230,
.bit = 21,
},
.la = {
.reg = 0x390,
.shift = 16,
.mask = 0xff,
.def = 0x50,
},
},
};
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
{ .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
{ .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
{ .name = "xusb_host", .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
{ .name = "xusb_dev", .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
};
static const unsigned int tegra114_group_display[] = {
TEGRA_SWGROUP_DC,
TEGRA_SWGROUP_DCB,
};
static const struct tegra_smmu_group_soc tegra114_groups[] = {
{
.name = "display",
.swgroups = tegra114_group_display,
.num_swgroups = ARRAY_SIZE(tegra114_group_display),
},
};
static const struct tegra_smmu_soc tegra114_smmu_soc = {
.clients = tegra114_mc_clients,
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
.swgroups = tegra114_swgroups,
.num_swgroups = ARRAY_SIZE(tegra114_swgroups),
.groups = tegra114_groups,
.num_groups = ARRAY_SIZE(tegra114_groups),
.supports_round_robin_arbitration = false,
.supports_request_limit = false,
.num_tlb_lines = 32,
.num_asids = 4,
};
#define TEGRA114_MC_RESET(_name, _control, _status, _bit) \
{ \
.name = #_name, \
.id = TEGRA114_MC_RESET_##_name, \
.control = _control, \
.status = _status, \
.bit = _bit, \
}
static const struct tegra_mc_reset tegra114_mc_resets[] = {
TEGRA114_MC_RESET(AVPC, 0x200, 0x204, 1),
TEGRA114_MC_RESET(DC, 0x200, 0x204, 2),
TEGRA114_MC_RESET(DCB, 0x200, 0x204, 3),
TEGRA114_MC_RESET(EPP, 0x200, 0x204, 4),
TEGRA114_MC_RESET(2D, 0x200, 0x204, 5),
TEGRA114_MC_RESET(HC, 0x200, 0x204, 6),
TEGRA114_MC_RESET(HDA, 0x200, 0x204, 7),
TEGRA114_MC_RESET(ISP, 0x200, 0x204, 8),
TEGRA114_MC_RESET(MPCORE, 0x200, 0x204, 9),
TEGRA114_MC_RESET(MPCORELP, 0x200, 0x204, 10),
TEGRA114_MC_RESET(MPE, 0x200, 0x204, 11),
TEGRA114_MC_RESET(3D, 0x200, 0x204, 12),
TEGRA114_MC_RESET(3D2, 0x200, 0x204, 13),
TEGRA114_MC_RESET(PPCS, 0x200, 0x204, 14),
TEGRA114_MC_RESET(VDE, 0x200, 0x204, 16),
TEGRA114_MC_RESET(VI, 0x200, 0x204, 17),
};
const struct tegra_mc_soc tegra114_mc_soc = {
.clients = tegra114_mc_clients,
.num_clients = ARRAY_SIZE(tegra114_mc_clients),
.num_address_bits = 32,
.atom_size = 32,
.client_id_mask = 0x7f,
.smmu = &tegra114_smmu_soc,
.intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
MC_INT_DECERR_EMEM,
.reset_ops = &terga_mc_reset_ops_common,
.resets = tegra114_mc_resets,
.num_resets = ARRAY_SIZE(tegra114_mc_resets),
};
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+601
View File
@@ -0,0 +1,601 @@
/*
* Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
*
* 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/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <dt-bindings/memory/tegra186-mc.h>
struct tegra_mc {
struct device *dev;
void __iomem *regs;
};
struct tegra_mc_client {
const char *name;
unsigned int sid;
struct {
unsigned int override;
unsigned int security;
} regs;
};
static const struct tegra_mc_client tegra186_mc_clients[] = {
{
.name = "ptcr",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.override = 0x000,
.security = 0x004,
},
}, {
.name = "afir",
.sid = TEGRA186_SID_AFI,
.regs = {
.override = 0x070,
.security = 0x074,
},
}, {
.name = "hdar",
.sid = TEGRA186_SID_HDA,
.regs = {
.override = 0x0a8,
.security = 0x0ac,
},
}, {
.name = "host1xdmar",
.sid = TEGRA186_SID_HOST1X,
.regs = {
.override = 0x0b0,
.security = 0x0b4,
},
}, {
.name = "nvencsrd",
.sid = TEGRA186_SID_NVENC,
.regs = {
.override = 0x0e0,
.security = 0x0e4,
},
}, {
.name = "satar",
.sid = TEGRA186_SID_SATA,
.regs = {
.override = 0x0f8,
.security = 0x0fc,
},
}, {
.name = "mpcorer",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.override = 0x138,
.security = 0x13c,
},
}, {
.name = "nvencswr",
.sid = TEGRA186_SID_NVENC,
.regs = {
.override = 0x158,
.security = 0x15c,
},
}, {
.name = "afiw",
.sid = TEGRA186_SID_AFI,
.regs = {
.override = 0x188,
.security = 0x18c,
},
}, {
.name = "hdaw",
.sid = TEGRA186_SID_HDA,
.regs = {
.override = 0x1a8,
.security = 0x1ac,
},
}, {
.name = "mpcorew",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.override = 0x1c8,
.security = 0x1cc,
},
}, {
.name = "sataw",
.sid = TEGRA186_SID_SATA,
.regs = {
.override = 0x1e8,
.security = 0x1ec,
},
}, {
.name = "ispra",
.sid = TEGRA186_SID_ISP,
.regs = {
.override = 0x220,
.security = 0x224,
},
}, {
.name = "ispwa",
.sid = TEGRA186_SID_ISP,
.regs = {
.override = 0x230,
.security = 0x234,
},
}, {
.name = "ispwb",
.sid = TEGRA186_SID_ISP,
.regs = {
.override = 0x238,
.security = 0x23c,
},
}, {
.name = "xusb_hostr",
.sid = TEGRA186_SID_XUSB_HOST,
.regs = {
.override = 0x250,
.security = 0x254,
},
}, {
.name = "xusb_hostw",
.sid = TEGRA186_SID_XUSB_HOST,
.regs = {
.override = 0x258,
.security = 0x25c,
},
}, {
.name = "xusb_devr",
.sid = TEGRA186_SID_XUSB_DEV,
.regs = {
.override = 0x260,
.security = 0x264,
},
}, {
.name = "xusb_devw",
.sid = TEGRA186_SID_XUSB_DEV,
.regs = {
.override = 0x268,
.security = 0x26c,
},
}, {
.name = "tsecsrd",
.sid = TEGRA186_SID_TSEC,
.regs = {
.override = 0x2a0,
.security = 0x2a4,
},
}, {
.name = "tsecswr",
.sid = TEGRA186_SID_TSEC,
.regs = {
.override = 0x2a8,
.security = 0x2ac,
},
}, {
.name = "gpusrd",
.sid = TEGRA186_SID_GPU,
.regs = {
.override = 0x2c0,
.security = 0x2c4,
},
}, {
.name = "gpuswr",
.sid = TEGRA186_SID_GPU,
.regs = {
.override = 0x2c8,
.security = 0x2cc,
},
}, {
.name = "sdmmcra",
.sid = TEGRA186_SID_SDMMC1,
.regs = {
.override = 0x300,
.security = 0x304,
},
}, {
.name = "sdmmcraa",
.sid = TEGRA186_SID_SDMMC2,
.regs = {
.override = 0x308,
.security = 0x30c,
},
}, {
.name = "sdmmcr",
.sid = TEGRA186_SID_SDMMC3,
.regs = {
.override = 0x310,
.security = 0x314,
},
}, {
.name = "sdmmcrab",
.sid = TEGRA186_SID_SDMMC4,
.regs = {
.override = 0x318,
.security = 0x31c,
},
}, {
.name = "sdmmcwa",
.sid = TEGRA186_SID_SDMMC1,
.regs = {
.override = 0x320,
.security = 0x324,
},
}, {
.name = "sdmmcwaa",
.sid = TEGRA186_SID_SDMMC2,
.regs = {
.override = 0x328,
.security = 0x32c,
},
}, {
.name = "sdmmcw",
.sid = TEGRA186_SID_SDMMC3,
.regs = {
.override = 0x330,
.security = 0x334,
},
}, {
.name = "sdmmcwab",
.sid = TEGRA186_SID_SDMMC4,
.regs = {
.override = 0x338,
.security = 0x33c,
},
}, {
.name = "vicsrd",
.sid = TEGRA186_SID_VIC,
.regs = {
.override = 0x360,
.security = 0x364,
},
}, {
.name = "vicswr",
.sid = TEGRA186_SID_VIC,
.regs = {
.override = 0x368,
.security = 0x36c,
},
}, {
.name = "viw",
.sid = TEGRA186_SID_VI,
.regs = {
.override = 0x390,
.security = 0x394,
},
}, {
.name = "nvdecsrd",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.override = 0x3c0,
.security = 0x3c4,
},
}, {
.name = "nvdecswr",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.override = 0x3c8,
.security = 0x3cc,
},
}, {
.name = "aper",
.sid = TEGRA186_SID_APE,
.regs = {
.override = 0x3d0,
.security = 0x3d4,
},
}, {
.name = "apew",
.sid = TEGRA186_SID_APE,
.regs = {
.override = 0x3d8,
.security = 0x3dc,
},
}, {
.name = "nvjpgsrd",
.sid = TEGRA186_SID_NVJPG,
.regs = {
.override = 0x3f0,
.security = 0x3f4,
},
}, {
.name = "nvjpgswr",
.sid = TEGRA186_SID_NVJPG,
.regs = {
.override = 0x3f8,
.security = 0x3fc,
},
}, {
.name = "sesrd",
.sid = TEGRA186_SID_SE,
.regs = {
.override = 0x400,
.security = 0x404,
},
}, {
.name = "seswr",
.sid = TEGRA186_SID_SE,
.regs = {
.override = 0x408,
.security = 0x40c,
},
}, {
.name = "etrr",
.sid = TEGRA186_SID_ETR,
.regs = {
.override = 0x420,
.security = 0x424,
},
}, {
.name = "etrw",
.sid = TEGRA186_SID_ETR,
.regs = {
.override = 0x428,
.security = 0x42c,
},
}, {
.name = "tsecsrdb",
.sid = TEGRA186_SID_TSECB,
.regs = {
.override = 0x430,
.security = 0x434,
},
}, {
.name = "tsecswrb",
.sid = TEGRA186_SID_TSECB,
.regs = {
.override = 0x438,
.security = 0x43c,
},
}, {
.name = "gpusrd2",
.sid = TEGRA186_SID_GPU,
.regs = {
.override = 0x440,
.security = 0x444,
},
}, {
.name = "gpuswr2",
.sid = TEGRA186_SID_GPU,
.regs = {
.override = 0x448,
.security = 0x44c,
},
}, {
.name = "axisr",
.sid = TEGRA186_SID_GPCDMA_0,
.regs = {
.override = 0x460,
.security = 0x464,
},
}, {
.name = "axisw",
.sid = TEGRA186_SID_GPCDMA_0,
.regs = {
.override = 0x468,
.security = 0x46c,
},
}, {
.name = "eqosr",
.sid = TEGRA186_SID_EQOS,
.regs = {
.override = 0x470,
.security = 0x474,
},
}, {
.name = "eqosw",
.sid = TEGRA186_SID_EQOS,
.regs = {
.override = 0x478,
.security = 0x47c,
},
}, {
.name = "ufshcr",
.sid = TEGRA186_SID_UFSHC,
.regs = {
.override = 0x480,
.security = 0x484,
},
}, {
.name = "ufshcw",
.sid = TEGRA186_SID_UFSHC,
.regs = {
.override = 0x488,
.security = 0x48c,
},
}, {
.name = "nvdisplayr",
.sid = TEGRA186_SID_NVDISPLAY,
.regs = {
.override = 0x490,
.security = 0x494,
},
}, {
.name = "bpmpr",
.sid = TEGRA186_SID_BPMP,
.regs = {
.override = 0x498,
.security = 0x49c,
},
}, {
.name = "bpmpw",
.sid = TEGRA186_SID_BPMP,
.regs = {
.override = 0x4a0,
.security = 0x4a4,
},
}, {
.name = "bpmpdmar",
.sid = TEGRA186_SID_BPMP,
.regs = {
.override = 0x4a8,
.security = 0x4ac,
},
}, {
.name = "bpmpdmaw",
.sid = TEGRA186_SID_BPMP,
.regs = {
.override = 0x4b0,
.security = 0x4b4,
},
}, {
.name = "aonr",
.sid = TEGRA186_SID_AON,
.regs = {
.override = 0x4b8,
.security = 0x4bc,
},
}, {
.name = "aonw",
.sid = TEGRA186_SID_AON,
.regs = {
.override = 0x4c0,
.security = 0x4c4,
},
}, {
.name = "aondmar",
.sid = TEGRA186_SID_AON,
.regs = {
.override = 0x4c8,
.security = 0x4cc,
},
}, {
.name = "aondmaw",
.sid = TEGRA186_SID_AON,
.regs = {
.override = 0x4d0,
.security = 0x4d4,
},
}, {
.name = "scer",
.sid = TEGRA186_SID_SCE,
.regs = {
.override = 0x4d8,
.security = 0x4dc,
},
}, {
.name = "scew",
.sid = TEGRA186_SID_SCE,
.regs = {
.override = 0x4e0,
.security = 0x4e4,
},
}, {
.name = "scedmar",
.sid = TEGRA186_SID_SCE,
.regs = {
.override = 0x4e8,
.security = 0x4ec,
},
}, {
.name = "scedmaw",
.sid = TEGRA186_SID_SCE,
.regs = {
.override = 0x4f0,
.security = 0x4f4,
},
}, {
.name = "apedmar",
.sid = TEGRA186_SID_APE,
.regs = {
.override = 0x4f8,
.security = 0x4fc,
},
}, {
.name = "apedmaw",
.sid = TEGRA186_SID_APE,
.regs = {
.override = 0x500,
.security = 0x504,
},
}, {
.name = "nvdisplayr1",
.sid = TEGRA186_SID_NVDISPLAY,
.regs = {
.override = 0x508,
.security = 0x50c,
},
}, {
.name = "vicsrd1",
.sid = TEGRA186_SID_VIC,
.regs = {
.override = 0x510,
.security = 0x514,
},
}, {
.name = "nvdecsrd1",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.override = 0x518,
.security = 0x51c,
},
},
};
static int tegra186_mc_probe(struct platform_device *pdev)
{
struct resource *res;
struct tegra_mc *mc;
unsigned int i;
int err = 0;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mc->regs))
return PTR_ERR(mc->regs);
mc->dev = &pdev->dev;
for (i = 0; i < ARRAY_SIZE(tegra186_mc_clients); i++) {
const struct tegra_mc_client *client = &tegra186_mc_clients[i];
u32 override, security;
override = readl(mc->regs + client->regs.override);
security = readl(mc->regs + client->regs.security);
dev_dbg(&pdev->dev, "client %s: override: %x security: %x\n",
client->name, override, security);
dev_dbg(&pdev->dev, "setting SID %u for %s\n", client->sid,
client->name);
writel(client->sid, mc->regs + client->regs.override);
override = readl(mc->regs + client->regs.override);
security = readl(mc->regs + client->regs.security);
dev_dbg(&pdev->dev, "client %s: override: %x security: %x\n",
client->name, override, security);
}
platform_set_drvdata(pdev, mc);
return err;
}
static const struct of_device_id tegra186_mc_of_match[] = {
{ .compatible = "nvidia,tegra186-mc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_mc_of_match);
static struct platform_driver tegra186_mc_driver = {
.driver = {
.name = "tegra186-mc",
.of_match_table = tegra186_mc_of_match,
.suppress_bind_attrs = true,
},
.prevent_deferred_probe = true,
.probe = tegra186_mc_probe,
};
module_platform_driver(tegra186_mc_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 Memory Controller driver");
MODULE_LICENSE("GPL v2");
+296
View File
@@ -0,0 +1,296 @@
/*
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* 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 <dt-bindings/memory/tegra20-mc.h>
#include "mc.h"
static const struct tegra_mc_client tegra20_mc_clients[] = {
{
.id = 0x00,
.name = "display0a",
}, {
.id = 0x01,
.name = "display0ab",
}, {
.id = 0x02,
.name = "display0b",
}, {
.id = 0x03,
.name = "display0bb",
}, {
.id = 0x04,
.name = "display0c",
}, {
.id = 0x05,
.name = "display0cb",
}, {
.id = 0x06,
.name = "display1b",
}, {
.id = 0x07,
.name = "display1bb",
}, {
.id = 0x08,
.name = "eppup",
}, {
.id = 0x09,
.name = "g2pr",
}, {
.id = 0x0a,
.name = "g2sr",
}, {
.id = 0x0b,
.name = "mpeunifbr",
}, {
.id = 0x0c,
.name = "viruv",
}, {
.id = 0x0d,
.name = "avpcarm7r",
}, {
.id = 0x0e,
.name = "displayhc",
}, {
.id = 0x0f,
.name = "displayhcb",
}, {
.id = 0x10,
.name = "fdcdrd",
}, {
.id = 0x11,
.name = "g2dr",
}, {
.id = 0x12,
.name = "host1xdmar",
}, {
.id = 0x13,
.name = "host1xr",
}, {
.id = 0x14,
.name = "idxsrd",
}, {
.id = 0x15,
.name = "mpcorer",
}, {
.id = 0x16,
.name = "mpe_ipred",
}, {
.id = 0x17,
.name = "mpeamemrd",
}, {
.id = 0x18,
.name = "mpecsrd",
}, {
.id = 0x19,
.name = "ppcsahbdmar",
}, {
.id = 0x1a,
.name = "ppcsahbslvr",
}, {
.id = 0x1b,
.name = "texsrd",
}, {
.id = 0x1c,
.name = "vdebsevr",
}, {
.id = 0x1d,
.name = "vdember",
}, {
.id = 0x1e,
.name = "vdemcer",
}, {
.id = 0x1f,
.name = "vdetper",
}, {
.id = 0x20,
.name = "eppu",
}, {
.id = 0x21,
.name = "eppv",
}, {
.id = 0x22,
.name = "eppy",
}, {
.id = 0x23,
.name = "mpeunifbw",
}, {
.id = 0x24,
.name = "viwsb",
}, {
.id = 0x25,
.name = "viwu",
}, {
.id = 0x26,
.name = "viwv",
}, {
.id = 0x27,
.name = "viwy",
}, {
.id = 0x28,
.name = "g2dw",
}, {
.id = 0x29,
.name = "avpcarm7w",
}, {
.id = 0x2a,
.name = "fdcdwr",
}, {
.id = 0x2b,
.name = "host1xw",
}, {
.id = 0x2c,
.name = "ispw",
}, {
.id = 0x2d,
.name = "mpcorew",
}, {
.id = 0x2e,
.name = "mpecswr",
}, {
.id = 0x2f,
.name = "ppcsahbdmaw",
}, {
.id = 0x30,
.name = "ppcsahbslvw",
}, {
.id = 0x31,
.name = "vdebsevw",
}, {
.id = 0x32,
.name = "vdembew",
}, {
.id = 0x33,
.name = "vdetpmw",
},
};
#define TEGRA20_MC_RESET(_name, _control, _status, _reset, _bit) \
{ \
.name = #_name, \
.id = TEGRA20_MC_RESET_##_name, \
.control = _control, \
.status = _status, \
.reset = _reset, \
.bit = _bit, \
}
static const struct tegra_mc_reset tegra20_mc_resets[] = {
TEGRA20_MC_RESET(AVPC, 0x100, 0x140, 0x104, 0),
TEGRA20_MC_RESET(DC, 0x100, 0x144, 0x104, 1),
TEGRA20_MC_RESET(DCB, 0x100, 0x148, 0x104, 2),
TEGRA20_MC_RESET(EPP, 0x100, 0x14c, 0x104, 3),
TEGRA20_MC_RESET(2D, 0x100, 0x150, 0x104, 4),
TEGRA20_MC_RESET(HC, 0x100, 0x154, 0x104, 5),
TEGRA20_MC_RESET(ISP, 0x100, 0x158, 0x104, 6),
TEGRA20_MC_RESET(MPCORE, 0x100, 0x15c, 0x104, 7),
TEGRA20_MC_RESET(MPEA, 0x100, 0x160, 0x104, 8),
TEGRA20_MC_RESET(MPEB, 0x100, 0x164, 0x104, 9),
TEGRA20_MC_RESET(MPEC, 0x100, 0x168, 0x104, 10),
TEGRA20_MC_RESET(3D, 0x100, 0x16c, 0x104, 11),
TEGRA20_MC_RESET(PPCS, 0x100, 0x170, 0x104, 12),
TEGRA20_MC_RESET(VDE, 0x100, 0x174, 0x104, 13),
TEGRA20_MC_RESET(VI, 0x100, 0x178, 0x104, 14),
};
static int terga20_mc_hotreset_assert(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->reset);
mc_writel(mc, value & ~BIT(rst->bit), rst->reset);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int terga20_mc_hotreset_deassert(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->reset);
mc_writel(mc, value | BIT(rst->bit), rst->reset);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int terga20_mc_block_dma(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static bool terga20_mc_dma_idling(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return mc_readl(mc, rst->status) == 0;
}
static int terga20_mc_reset_status(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->reset) & BIT(rst->bit)) == 0;
}
static int terga20_mc_unblock_dma(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) | BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
const struct tegra_mc_reset_ops terga20_mc_reset_ops = {
.hotreset_assert = terga20_mc_hotreset_assert,
.hotreset_deassert = terga20_mc_hotreset_deassert,
.block_dma = terga20_mc_block_dma,
.dma_idling = terga20_mc_dma_idling,
.unblock_dma = terga20_mc_unblock_dma,
.reset_status = terga20_mc_reset_status,
};
const struct tegra_mc_soc tegra20_mc_soc = {
.clients = tegra20_mc_clients,
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
.num_address_bits = 32,
.client_id_mask = 0x3f,
.intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
MC_INT_DECERR_EMEM,
.reset_ops = &terga20_mc_reset_ops,
.resets = tegra20_mc_resets,
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
};
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+458
View File
@@ -0,0 +1,458 @@
/*
* TI AEMIF driver
*
* Copyright (C) 2010 - 2013 Texas Instruments Incorporated. http://www.ti.com/
*
* Authors:
* Murali Karicheri <m-karicheri2@ti.com>
* Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
*
* 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_platform.h>
#include <linux/platform_device.h>
#include <linux/platform_data/ti-aemif.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 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
#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)
/**
* struct aemif_cs_data: structure to hold cs parameters
* @cs: chip-select number
* @wstrobe: write strobe width, ns
* @rstrobe: read strobe width, ns
* @wsetup: write setup width, ns
* @whold: write hold width, ns
* @rsetup: read setup width, ns
* @rhold: read hold width, ns
* @ta: minimum turn around time, ns
* @enable_ss: enable/disable select strobe mode
* @enable_ew: enable/disable extended wait mode
* @asize: width of the asynchronous device's data bus
*/
struct 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 aemif_device: structure to hold device data
* @base: base address of AEMIF registers
* @clk: source clock
* @clk_rate: clock's rate in kHz
* @num_cs: number of assigned chip-selects
* @cs_offset: start number of cs nodes
* @cs_data: array of chip-select settings
*/
struct aemif_device {
void __iomem *base;
struct clk *clk;
unsigned long clk_rate;
u8 num_cs;
int cs_offset;
struct aemif_cs_data cs_data[NUM_CS];
};
/**
* aemif_calc_rate - calculate timing data.
* @pdev: platform device to calculate for
* @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(struct platform_device *pdev, int wanted,
unsigned long clk, int max)
{
int result;
result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
dev_dbg(&pdev->dev, "%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;
}
/**
* aemif_config_abus - configure async bus parameters
* @pdev: platform device to configure for
* @csnum: aemif chip select number
*
* 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 aemif_config_abus(struct platform_device *pdev, int csnum)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
struct aemif_cs_data *data = &aemif->cs_data[csnum];
int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
unsigned long clk_rate = aemif->clk_rate;
unsigned offset;
u32 set, val;
offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
whold < 0 || wstrobe < 0 || wsetup < 0) {
dev_err(&pdev->dev, "%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;
}
static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate)
{
return ((val + 1) * NSEC_PER_MSEC) / clk_rate;
}
/**
* aemif_get_hw_params - function to read hw register values
* @pdev: platform device to read for
* @csnum: aemif chip select number
*
* 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 aemif_get_hw_params(struct platform_device *pdev, int csnum)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
struct aemif_cs_data *data = &aemif->cs_data[csnum];
unsigned long clk_rate = aemif->clk_rate;
u32 val, offset;
offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
val = readl(aemif->base + offset);
data->ta = aemif_cycles_to_nsec(TA_VAL(val), clk_rate);
data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val), clk_rate);
data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val), clk_rate);
data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val), clk_rate);
data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val), clk_rate);
data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate);
data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate);
data->enable_ew = EW_VAL(val);
data->enable_ss = SS_VAL(val);
data->asize = val & ASIZE_MAX;
}
/**
* of_aemif_parse_abus_config - parse CS configuration from DT
* @pdev: platform device to parse for
* @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_aemif_parse_abus_config(struct platform_device *pdev,
struct device_node *np)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
struct aemif_cs_data *data;
u32 cs;
u32 val;
if (of_property_read_u32(np, "ti,cs-chipselect", &cs)) {
dev_dbg(&pdev->dev, "cs property is required");
return -EINVAL;
}
if (cs - aemif->cs_offset >= NUM_CS || cs < aemif->cs_offset) {
dev_dbg(&pdev->dev, "cs number is incorrect %d", cs);
return -EINVAL;
}
if (aemif->num_cs >= NUM_CS) {
dev_dbg(&pdev->dev, "cs count is more than %d", NUM_CS);
return -EINVAL;
}
data = &aemif->cs_data[aemif->num_cs];
data->cs = cs;
/* read the current value in the hw register */
aemif_get_hw_params(pdev, aemif->num_cs++);
/* override the values from device node */
if (!of_property_read_u32(np, "ti,cs-min-turnaround-ns", &val))
data->ta = val;
if (!of_property_read_u32(np, "ti,cs-read-hold-ns", &val))
data->rhold = val;
if (!of_property_read_u32(np, "ti,cs-read-strobe-ns", &val))
data->rstrobe = val;
if (!of_property_read_u32(np, "ti,cs-read-setup-ns", &val))
data->rsetup = val;
if (!of_property_read_u32(np, "ti,cs-write-hold-ns", &val))
data->whold = val;
if (!of_property_read_u32(np, "ti,cs-write-strobe-ns", &val))
data->wstrobe = val;
if (!of_property_read_u32(np, "ti,cs-write-setup-ns", &val))
data->wsetup = val;
if (!of_property_read_u32(np, "ti,cs-bus-width", &val))
if (val == 16)
data->asize = 1;
data->enable_ew = of_property_read_bool(np, "ti,cs-extended-wait-mode");
data->enable_ss = of_property_read_bool(np, "ti,cs-select-strobe-mode");
return 0;
}
static const struct of_device_id aemif_of_match[] = {
{ .compatible = "ti,davinci-aemif", },
{ .compatible = "ti,da850-aemif", },
{},
};
MODULE_DEVICE_TABLE(of, aemif_of_match);
static int aemif_probe(struct platform_device *pdev)
{
int i;
int ret = -ENODEV;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child_np;
struct aemif_device *aemif;
struct aemif_platform_data *pdata;
struct of_dev_auxdata *dev_lookup;
aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
if (!aemif)
return -ENOMEM;
pdata = dev_get_platdata(&pdev->dev);
dev_lookup = pdata ? pdata->dev_lookup : NULL;
platform_set_drvdata(pdev, aemif);
aemif->clk = devm_clk_get(dev, NULL);
if (IS_ERR(aemif->clk)) {
dev_err(dev, "cannot get clock 'aemif'\n");
return PTR_ERR(aemif->clk);
}
ret = clk_prepare_enable(aemif->clk);
if (ret)
return ret;
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
if (np && of_device_is_compatible(np, "ti,da850-aemif"))
aemif->cs_offset = 2;
else if (pdata)
aemif->cs_offset = pdata->cs_offset;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
aemif->base = devm_ioremap_resource(dev, res);
if (IS_ERR(aemif->base)) {
ret = PTR_ERR(aemif->base);
goto error;
}
if (np) {
/*
* 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.
*/
for_each_available_child_of_node(np, child_np) {
ret = of_aemif_parse_abus_config(pdev, child_np);
if (ret < 0) {
of_node_put(child_np);
goto error;
}
}
} else if (pdata && pdata->num_abus_data > 0) {
for (i = 0; i < pdata->num_abus_data; i++, aemif->num_cs++) {
aemif->cs_data[i].cs = pdata->abus_data[i].cs;
aemif_get_hw_params(pdev, i);
}
}
for (i = 0; i < aemif->num_cs; i++) {
ret = aemif_config_abus(pdev, i);
if (ret < 0) {
dev_err(dev, "Error configuring chip select %d\n",
aemif->cs_data[i].cs);
goto error;
}
}
/*
* Create a child devices explicitly from here to guarantee that the
* child will be probed after the AEMIF timing parameters are set.
*/
if (np) {
for_each_available_child_of_node(np, child_np) {
ret = of_platform_populate(child_np, NULL,
dev_lookup, dev);
if (ret < 0) {
of_node_put(child_np);
goto error;
}
}
} else if (pdata) {
for (i = 0; i < pdata->num_sub_devices; i++) {
pdata->sub_devices[i].dev.parent = dev;
ret = platform_device_register(&pdata->sub_devices[i]);
if (ret) {
dev_warn(dev, "Error register sub device %s\n",
pdata->sub_devices[i].name);
}
}
}
return 0;
error:
clk_disable_unprepare(aemif->clk);
return ret;
}
static int aemif_remove(struct platform_device *pdev)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
clk_disable_unprepare(aemif->clk);
return 0;
}
static struct platform_driver aemif_driver = {
.probe = aemif_probe,
.remove = aemif_remove,
.driver = {
.name = "ti-aemif",
.of_match_table = of_match_ptr(aemif_of_match),
},
};
module_platform_driver(aemif_driver);
MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
MODULE_DESCRIPTION("Texas Instruments AEMIF driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
+356
View File
@@ -0,0 +1,356 @@
/*
* TI AM33XX SRAM EMIF Driver
*
* Copyright (C) 2016-2017 Texas Instruments Inc.
* Dave Gerlach
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/sram.h>
#include <linux/ti-emif-sram.h>
#include "emif.h"
#define TI_EMIF_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
(unsigned long)&ti_emif_sram)
#define EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES 0x00a0
struct ti_emif_data {
phys_addr_t ti_emif_sram_phys;
phys_addr_t ti_emif_sram_data_phys;
unsigned long ti_emif_sram_virt;
unsigned long ti_emif_sram_data_virt;
struct gen_pool *sram_pool_code;
struct gen_pool *sram_pool_data;
struct ti_emif_pm_data pm_data;
struct ti_emif_pm_functions pm_functions;
};
static struct ti_emif_data *emif_instance;
static u32 sram_suspend_address(struct ti_emif_data *emif_data,
unsigned long addr)
{
return (emif_data->ti_emif_sram_virt +
TI_EMIF_SRAM_SYMBOL_OFFSET(addr));
}
static phys_addr_t sram_resume_address(struct ti_emif_data *emif_data,
unsigned long addr)
{
return ((unsigned long)emif_data->ti_emif_sram_phys +
TI_EMIF_SRAM_SYMBOL_OFFSET(addr));
}
static void ti_emif_free_sram(struct ti_emif_data *emif_data)
{
gen_pool_free(emif_data->sram_pool_code, emif_data->ti_emif_sram_virt,
ti_emif_sram_sz);
gen_pool_free(emif_data->sram_pool_data,
emif_data->ti_emif_sram_data_virt,
sizeof(struct emif_regs_amx3));
}
static int ti_emif_alloc_sram(struct device *dev,
struct ti_emif_data *emif_data)
{
struct device_node *np = dev->of_node;
int ret;
emif_data->sram_pool_code = of_gen_pool_get(np, "sram", 0);
if (!emif_data->sram_pool_code) {
dev_err(dev, "Unable to get sram pool for ocmcram code\n");
return -ENODEV;
}
emif_data->ti_emif_sram_virt =
gen_pool_alloc(emif_data->sram_pool_code,
ti_emif_sram_sz);
if (!emif_data->ti_emif_sram_virt) {
dev_err(dev, "Unable to allocate code memory from ocmcram\n");
return -ENOMEM;
}
/* Save physical address to calculate resume offset during pm init */
emif_data->ti_emif_sram_phys =
gen_pool_virt_to_phys(emif_data->sram_pool_code,
emif_data->ti_emif_sram_virt);
/* Get sram pool for data section and allocate space */
emif_data->sram_pool_data = of_gen_pool_get(np, "sram", 1);
if (!emif_data->sram_pool_data) {
dev_err(dev, "Unable to get sram pool for ocmcram data\n");
ret = -ENODEV;
goto err_free_sram_code;
}
emif_data->ti_emif_sram_data_virt =
gen_pool_alloc(emif_data->sram_pool_data,
sizeof(struct emif_regs_amx3));
if (!emif_data->ti_emif_sram_data_virt) {
dev_err(dev, "Unable to allocate data memory from ocmcram\n");
ret = -ENOMEM;
goto err_free_sram_code;
}
/* Save physical address to calculate resume offset during pm init */
emif_data->ti_emif_sram_data_phys =
gen_pool_virt_to_phys(emif_data->sram_pool_data,
emif_data->ti_emif_sram_data_virt);
/*
* These functions are called during suspend path while MMU is
* still on so add virtual base to offset for absolute address
*/
emif_data->pm_functions.save_context =
sram_suspend_address(emif_data,
(unsigned long)ti_emif_save_context);
emif_data->pm_functions.enter_sr =
sram_suspend_address(emif_data,
(unsigned long)ti_emif_enter_sr);
emif_data->pm_functions.abort_sr =
sram_suspend_address(emif_data,
(unsigned long)ti_emif_abort_sr);
/*
* These are called during resume path when MMU is not enabled
* so physical address is used instead
*/
emif_data->pm_functions.restore_context =
sram_resume_address(emif_data,
(unsigned long)ti_emif_restore_context);
emif_data->pm_functions.exit_sr =
sram_resume_address(emif_data,
(unsigned long)ti_emif_exit_sr);
emif_data->pm_data.regs_virt =
(struct emif_regs_amx3 *)emif_data->ti_emif_sram_data_virt;
emif_data->pm_data.regs_phys = emif_data->ti_emif_sram_data_phys;
return 0;
err_free_sram_code:
gen_pool_free(emif_data->sram_pool_code, emif_data->ti_emif_sram_virt,
ti_emif_sram_sz);
return ret;
}
static int ti_emif_push_sram(struct device *dev, struct ti_emif_data *emif_data)
{
void *copy_addr;
u32 data_addr;
copy_addr = sram_exec_copy(emif_data->sram_pool_code,
(void *)emif_data->ti_emif_sram_virt,
&ti_emif_sram, ti_emif_sram_sz);
if (!copy_addr) {
dev_err(dev, "Cannot copy emif code to sram\n");
return -ENODEV;
}
data_addr = sram_suspend_address(emif_data,
(unsigned long)&ti_emif_pm_sram_data);
copy_addr = sram_exec_copy(emif_data->sram_pool_code,
(void *)data_addr,
&emif_data->pm_data,
sizeof(emif_data->pm_data));
if (!copy_addr) {
dev_err(dev, "Cannot copy emif data to code sram\n");
return -ENODEV;
}
return 0;
}
/*
* Due to Usage Note 3.1.2 "DDR3: JEDEC Compliance for Maximum
* Self-Refresh Command Limit" found in AM335x Silicon Errata
* (Document SPRZ360F Revised November 2013) we must configure
* the self refresh delay timer to 0xA (8192 cycles) to avoid
* generating too many refresh command from the EMIF.
*/
static void ti_emif_configure_sr_delay(struct ti_emif_data *emif_data)
{
writel(EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES,
(emif_data->pm_data.ti_emif_base_addr_virt +
EMIF_POWER_MANAGEMENT_CONTROL));
writel(EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES,
(emif_data->pm_data.ti_emif_base_addr_virt +
EMIF_POWER_MANAGEMENT_CTRL_SHDW));
}
/**
* ti_emif_copy_pm_function_table - copy mapping of pm funcs in sram
* @sram_pool: pointer to struct gen_pool where dst resides
* @dst: void * to address that table should be copied
*
* Returns 0 if success other error code if table is not available
*/
int ti_emif_copy_pm_function_table(struct gen_pool *sram_pool, void *dst)
{
void *copy_addr;
if (!emif_instance)
return -ENODEV;
copy_addr = sram_exec_copy(sram_pool, dst,
&emif_instance->pm_functions,
sizeof(emif_instance->pm_functions));
if (!copy_addr)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(ti_emif_copy_pm_function_table);
/**
* ti_emif_get_mem_type - return type for memory type in use
*
* Returns memory type value read from EMIF or error code if fails
*/
int ti_emif_get_mem_type(void)
{
unsigned long temp;
if (!emif_instance)
return -ENODEV;
temp = readl(emif_instance->pm_data.ti_emif_base_addr_virt +
EMIF_SDRAM_CONFIG);
temp = (temp & SDRAM_TYPE_MASK) >> SDRAM_TYPE_SHIFT;
return temp;
}
EXPORT_SYMBOL_GPL(ti_emif_get_mem_type);
static const struct of_device_id ti_emif_of_match[] = {
{ .compatible = "ti,emif-am3352", .data =
(void *)EMIF_SRAM_AM33_REG_LAYOUT, },
{ .compatible = "ti,emif-am4372", .data =
(void *)EMIF_SRAM_AM43_REG_LAYOUT, },
{},
};
MODULE_DEVICE_TABLE(of, ti_emif_of_match);
#ifdef CONFIG_PM_SLEEP
static int ti_emif_resume(struct device *dev)
{
unsigned long tmp =
__raw_readl((void *)emif_instance->ti_emif_sram_virt);
/*
* Check to see if what we are copying is already present in the
* first byte at the destination, only copy if it is not which
* indicates we have lost context and sram no longer contains
* the PM code
*/
if (tmp != ti_emif_sram)
ti_emif_push_sram(dev, emif_instance);
return 0;
}
static int ti_emif_suspend(struct device *dev)
{
/*
* The contents will be present in DDR hence no need to
* explicitly save
*/
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static int ti_emif_probe(struct platform_device *pdev)
{
int ret;
struct resource *res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct ti_emif_data *emif_data;
emif_data = devm_kzalloc(dev, sizeof(*emif_data), GFP_KERNEL);
if (!emif_data)
return -ENOMEM;
match = of_match_device(ti_emif_of_match, &pdev->dev);
if (!match)
return -ENODEV;
emif_data->pm_data.ti_emif_sram_config = (unsigned long)match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
emif_data->pm_data.ti_emif_base_addr_virt = devm_ioremap_resource(dev,
res);
if (IS_ERR(emif_data->pm_data.ti_emif_base_addr_virt)) {
ret = PTR_ERR(emif_data->pm_data.ti_emif_base_addr_virt);
return ret;
}
emif_data->pm_data.ti_emif_base_addr_phys = res->start;
ti_emif_configure_sr_delay(emif_data);
ret = ti_emif_alloc_sram(dev, emif_data);
if (ret)
return ret;
ret = ti_emif_push_sram(dev, emif_data);
if (ret)
goto fail_free_sram;
emif_instance = emif_data;
return 0;
fail_free_sram:
ti_emif_free_sram(emif_data);
return ret;
}
static int ti_emif_remove(struct platform_device *pdev)
{
struct ti_emif_data *emif_data = emif_instance;
emif_instance = NULL;
ti_emif_free_sram(emif_data);
return 0;
}
static const struct dev_pm_ops ti_emif_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ti_emif_suspend, ti_emif_resume)
};
static struct platform_driver ti_emif_driver = {
.probe = ti_emif_probe,
.remove = ti_emif_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = of_match_ptr(ti_emif_of_match),
.pm = &ti_emif_pm_ops,
},
};
module_platform_driver(ti_emif_driver);
MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
MODULE_DESCRIPTION("Texas Instruments SRAM EMIF driver");
MODULE_LICENSE("GPL v2");
+334
View File
@@ -0,0 +1,334 @@
/*
* Low level PM code for TI EMIF
*
* Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
* Dave Gerlach
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <generated/ti-emif-asm-offsets.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/memory.h>
#include "emif.h"
#define EMIF_POWER_MGMT_WAIT_SELF_REFRESH_8192_CYCLES 0x00a0
#define EMIF_POWER_MGMT_SR_TIMER_MASK 0x00f0
#define EMIF_POWER_MGMT_SELF_REFRESH_MODE 0x0200
#define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK 0x0700
#define EMIF_SDCFG_TYPE_DDR2 0x2 << SDRAM_TYPE_SHIFT
#define EMIF_STATUS_READY 0x4
#define AM43XX_EMIF_PHY_CTRL_REG_COUNT 0x120
#define EMIF_AM437X_REGISTERS 0x1
.arm
.align 3
ENTRY(ti_emif_sram)
/*
* void ti_emif_save_context(void)
*
* Used during suspend to save the context of all required EMIF registers
* to local memory if the EMIF is going to lose context during the sleep
* transition. Operates on the VIRTUAL address of the EMIF.
*/
ENTRY(ti_emif_save_context)
stmfd sp!, {r4 - r11, lr} @ save registers on stack
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_VIRT_OFFSET]
ldr r2, [r4, #EMIF_PM_REGS_VIRT_OFFSET]
/* Save EMIF configuration */
ldr r1, [r0, #EMIF_SDRAM_CONFIG]
str r1, [r2, #EMIF_SDCFG_VAL_OFFSET]
ldr r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
str r1, [r2, #EMIF_REF_CTRL_VAL_OFFSET]
ldr r1, [r0, #EMIF_SDRAM_TIMING_1]
str r1, [r2, #EMIF_TIMING1_VAL_OFFSET]
ldr r1, [r0, #EMIF_SDRAM_TIMING_2]
str r1, [r2, #EMIF_TIMING2_VAL_OFFSET]
ldr r1, [r0, #EMIF_SDRAM_TIMING_3]
str r1, [r2, #EMIF_TIMING3_VAL_OFFSET]
ldr r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
str r1, [r2, #EMIF_PMCR_VAL_OFFSET]
ldr r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
str r1, [r2, #EMIF_PMCR_SHDW_VAL_OFFSET]
ldr r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
str r1, [r2, #EMIF_ZQCFG_VAL_OFFSET]
ldr r1, [r0, #EMIF_DDR_PHY_CTRL_1]
str r1, [r2, #EMIF_DDR_PHY_CTLR_1_OFFSET]
ldr r1, [r0, #EMIF_COS_CONFIG]
str r1, [r2, #EMIF_COS_CONFIG_OFFSET]
ldr r1, [r0, #EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING]
str r1, [r2, #EMIF_PRIORITY_TO_COS_MAPPING_OFFSET]
ldr r1, [r0, #EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING]
str r1, [r2, #EMIF_CONNECT_ID_SERV_1_MAP_OFFSET]
ldr r1, [r0, #EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING]
str r1, [r2, #EMIF_CONNECT_ID_SERV_2_MAP_OFFSET]
ldr r1, [r0, #EMIF_OCP_CONFIG]
str r1, [r2, #EMIF_OCP_CONFIG_VAL_OFFSET]
ldr r5, [r4, #EMIF_PM_CONFIG_OFFSET]
cmp r5, #EMIF_SRAM_AM43_REG_LAYOUT
bne emif_skip_save_extra_regs
ldr r1, [r0, #EMIF_READ_WRITE_LEVELING_RAMP_CONTROL]
str r1, [r2, #EMIF_RD_WR_LEVEL_RAMP_CTRL_OFFSET]
ldr r1, [r0, #EMIF_READ_WRITE_EXECUTION_THRESHOLD]
str r1, [r2, #EMIF_RD_WR_EXEC_THRESH_OFFSET]
ldr r1, [r0, #EMIF_LPDDR2_NVM_TIMING]
str r1, [r2, #EMIF_LPDDR2_NVM_TIM_OFFSET]
ldr r1, [r0, #EMIF_LPDDR2_NVM_TIMING_SHDW]
str r1, [r2, #EMIF_LPDDR2_NVM_TIM_SHDW_OFFSET]
ldr r1, [r0, #EMIF_DLL_CALIB_CTRL]
str r1, [r2, #EMIF_DLL_CALIB_CTRL_VAL_OFFSET]
ldr r1, [r0, #EMIF_DLL_CALIB_CTRL_SHDW]
str r1, [r2, #EMIF_DLL_CALIB_CTRL_VAL_SHDW_OFFSET]
/* Loop and save entire block of emif phy regs */
mov r5, #0x0
add r4, r2, #EMIF_EXT_PHY_CTRL_VALS_OFFSET
add r3, r0, #EMIF_EXT_PHY_CTRL_1
ddr_phy_ctrl_save:
ldr r1, [r3, r5]
str r1, [r4, r5]
add r5, r5, #0x4
cmp r5, #AM43XX_EMIF_PHY_CTRL_REG_COUNT
bne ddr_phy_ctrl_save
emif_skip_save_extra_regs:
ldmfd sp!, {r4 - r11, pc} @ restore regs and return
ENDPROC(ti_emif_save_context)
/*
* void ti_emif_restore_context(void)
*
* Used during resume to restore the context of all required EMIF registers
* from local memory after the EMIF has lost context during a sleep transition.
* Operates on the PHYSICAL address of the EMIF.
*/
ENTRY(ti_emif_restore_context)
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_PHYS_OFFSET]
ldr r2, [r4, #EMIF_PM_REGS_PHYS_OFFSET]
/* Config EMIF Timings */
ldr r1, [r2, #EMIF_DDR_PHY_CTLR_1_OFFSET]
str r1, [r0, #EMIF_DDR_PHY_CTRL_1]
str r1, [r0, #EMIF_DDR_PHY_CTRL_1_SHDW]
ldr r1, [r2, #EMIF_TIMING1_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_TIMING_1]
str r1, [r0, #EMIF_SDRAM_TIMING_1_SHDW]
ldr r1, [r2, #EMIF_TIMING2_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_TIMING_2]
str r1, [r0, #EMIF_SDRAM_TIMING_2_SHDW]
ldr r1, [r2, #EMIF_TIMING3_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_TIMING_3]
str r1, [r0, #EMIF_SDRAM_TIMING_3_SHDW]
ldr r1, [r2, #EMIF_REF_CTRL_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_REFRESH_CONTROL]
str r1, [r0, #EMIF_SDRAM_REFRESH_CTRL_SHDW]
ldr r1, [r2, #EMIF_PMCR_VAL_OFFSET]
str r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
ldr r1, [r2, #EMIF_PMCR_SHDW_VAL_OFFSET]
str r1, [r0, #EMIF_POWER_MANAGEMENT_CTRL_SHDW]
ldr r1, [r2, #EMIF_COS_CONFIG_OFFSET]
str r1, [r0, #EMIF_COS_CONFIG]
ldr r1, [r2, #EMIF_PRIORITY_TO_COS_MAPPING_OFFSET]
str r1, [r0, #EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING]
ldr r1, [r2, #EMIF_CONNECT_ID_SERV_1_MAP_OFFSET]
str r1, [r0, #EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING]
ldr r1, [r2, #EMIF_CONNECT_ID_SERV_2_MAP_OFFSET]
str r1, [r0, #EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING]
ldr r1, [r2, #EMIF_OCP_CONFIG_VAL_OFFSET]
str r1, [r0, #EMIF_OCP_CONFIG]
ldr r5, [r4, #EMIF_PM_CONFIG_OFFSET]
cmp r5, #EMIF_SRAM_AM43_REG_LAYOUT
bne emif_skip_restore_extra_regs
ldr r1, [r2, #EMIF_RD_WR_LEVEL_RAMP_CTRL_OFFSET]
str r1, [r0, #EMIF_READ_WRITE_LEVELING_RAMP_CONTROL]
ldr r1, [r2, #EMIF_RD_WR_EXEC_THRESH_OFFSET]
str r1, [r0, #EMIF_READ_WRITE_EXECUTION_THRESHOLD]
ldr r1, [r2, #EMIF_LPDDR2_NVM_TIM_OFFSET]
str r1, [r0, #EMIF_LPDDR2_NVM_TIMING]
ldr r1, [r2, #EMIF_LPDDR2_NVM_TIM_SHDW_OFFSET]
str r1, [r0, #EMIF_LPDDR2_NVM_TIMING_SHDW]
ldr r1, [r2, #EMIF_DLL_CALIB_CTRL_VAL_OFFSET]
str r1, [r0, #EMIF_DLL_CALIB_CTRL]
ldr r1, [r2, #EMIF_DLL_CALIB_CTRL_VAL_SHDW_OFFSET]
str r1, [r0, #EMIF_DLL_CALIB_CTRL_SHDW]
ldr r1, [r2, #EMIF_ZQCFG_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
/* Loop and restore entire block of emif phy regs */
mov r5, #0x0
/* Load ti_emif_regs_amx3 + EMIF_EXT_PHY_CTRL_VALS_OFFSET for address
* to phy register save space
*/
add r3, r2, #EMIF_EXT_PHY_CTRL_VALS_OFFSET
add r4, r0, #EMIF_EXT_PHY_CTRL_1
ddr_phy_ctrl_restore:
ldr r1, [r3, r5]
str r1, [r4, r5]
add r5, r5, #0x4
cmp r5, #AM43XX_EMIF_PHY_CTRL_REG_COUNT
bne ddr_phy_ctrl_restore
emif_skip_restore_extra_regs:
/*
* Output impedence calib needed only for DDR3
* but since the initial state of this will be
* disabled for DDR2 no harm in restoring the
* old configuration
*/
ldr r1, [r2, #EMIF_ZQCFG_VAL_OFFSET]
str r1, [r0, #EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG]
/* Write to sdcfg last for DDR2 only */
ldr r1, [r2, #EMIF_SDCFG_VAL_OFFSET]
and r2, r1, #SDRAM_TYPE_MASK
cmp r2, #EMIF_SDCFG_TYPE_DDR2
streq r1, [r0, #EMIF_SDRAM_CONFIG]
mov pc, lr
ENDPROC(ti_emif_restore_context)
/*
* void ti_emif_enter_sr(void)
*
* Programs the EMIF to tell the SDRAM to enter into self-refresh
* mode during a sleep transition. Operates on the VIRTUAL address
* of the EMIF.
*/
ENTRY(ti_emif_enter_sr)
stmfd sp!, {r4 - r11, lr} @ save registers on stack
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_VIRT_OFFSET]
ldr r2, [r4, #EMIF_PM_REGS_VIRT_OFFSET]
ldr r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
bic r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
orr r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
str r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
ldmfd sp!, {r4 - r11, pc} @ restore regs and return
ENDPROC(ti_emif_enter_sr)
/*
* void ti_emif_exit_sr(void)
*
* Programs the EMIF to tell the SDRAM to exit self-refresh mode
* after a sleep transition. Operates on the PHYSICAL address of
* the EMIF.
*/
ENTRY(ti_emif_exit_sr)
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_PHYS_OFFSET]
ldr r2, [r4, #EMIF_PM_REGS_PHYS_OFFSET]
/*
* Toggle EMIF to exit refresh mode:
* if EMIF lost context, PWR_MGT_CTRL is currently 0, writing disable
* (0x0), wont do diddly squat! so do a toggle from SR(0x2) to disable
* (0x0) here.
* *If* EMIF did not lose context, nothing broken as we write the same
* value(0x2) to reg before we write a disable (0x0).
*/
ldr r1, [r2, #EMIF_PMCR_VAL_OFFSET]
bic r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
orr r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE
str r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
bic r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
str r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
/* Wait for EMIF to become ready */
1: ldr r1, [r0, #EMIF_STATUS]
tst r1, #EMIF_STATUS_READY
beq 1b
mov pc, lr
ENDPROC(ti_emif_exit_sr)
/*
* void ti_emif_abort_sr(void)
*
* Disables self-refresh after a failed transition to a low-power
* state so the kernel can jump back to DDR and follow abort path.
* Operates on the VIRTUAL address of the EMIF.
*/
ENTRY(ti_emif_abort_sr)
stmfd sp!, {r4 - r11, lr} @ save registers on stack
adr r4, ti_emif_pm_sram_data
ldr r0, [r4, #EMIF_PM_BASE_ADDR_VIRT_OFFSET]
ldr r2, [r4, #EMIF_PM_REGS_VIRT_OFFSET]
ldr r1, [r2, #EMIF_PMCR_VAL_OFFSET]
bic r1, r1, #EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK
str r1, [r0, #EMIF_POWER_MANAGEMENT_CONTROL]
/* Wait for EMIF to become ready */
1: ldr r1, [r0, #EMIF_STATUS]
tst r1, #EMIF_STATUS_READY
beq 1b
ldmfd sp!, {r4 - r11, pc} @ restore regs and return
ENDPROC(ti_emif_abort_sr)
.align 3
ENTRY(ti_emif_pm_sram_data)
.space EMIF_PM_DATA_SIZE
ENTRY(ti_emif_sram_sz)
.word . - ti_emif_save_context