mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
96
drivers/memory/Kconfig
Normal file
96
drivers/memory/Kconfig
Normal file
|
@ -0,0 +1,96 @@
|
|||
#
|
||||
# Memory devices
|
||||
#
|
||||
|
||||
menuconfig MEMORY
|
||||
bool "Memory Controller drivers"
|
||||
default y
|
||||
|
||||
if MEMORY
|
||||
|
||||
config EXYNOS_MCOMP
|
||||
bool "Exynos memory compressor"
|
||||
default y
|
||||
depends on ARCH_EXYNOS
|
||||
help
|
||||
Memory compressor
|
||||
|
||||
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 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 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 TEGRA20_MC
|
||||
bool "Tegra20 Memory Controller(MC) driver"
|
||||
default y
|
||||
depends on ARCH_TEGRA_2x_SOC
|
||||
help
|
||||
This driver is for the Memory Controller(MC) module available
|
||||
in Tegra20 SoCs, mainly for a address translation fault
|
||||
analysis, especially for IOMMU/GART(Graphics Address
|
||||
Relocation Table) module.
|
||||
|
||||
config TEGRA30_MC
|
||||
bool "Tegra30 Memory Controller(MC) driver"
|
||||
default y
|
||||
depends on ARCH_TEGRA_3x_SOC
|
||||
help
|
||||
This driver is for the Memory Controller(MC) module available
|
||||
in Tegra30 SoCs, mainly for a address translation fault
|
||||
analysis, especially for IOMMU/SMMU(System Memory Management
|
||||
Unit) module.
|
||||
|
||||
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
|
||||
|
||||
endif
|
17
drivers/memory/Makefile
Normal file
17
drivers/memory/Makefile
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Makefile for memory devices
|
||||
#
|
||||
|
||||
ifeq ($(CONFIG_DDR),y)
|
||||
obj-$(CONFIG_OF) += of_memory.o
|
||||
endif
|
||||
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
|
||||
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
|
||||
obj-$(CONFIG_TI_EMIF) += emif.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_TEGRA20_MC) += tegra20-mc.o
|
||||
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
|
||||
obj-$(CONFIG_EXYNOS_MCOMP) += exynos-mcomp.o mcomp_test.o
|
||||
obj-$(CONFIG_SAMSUNG_CORE_TEST) += core_reg.o
|
98
drivers/memory/atmel-sdramc.c
Normal file
98
drivers/memory/atmel-sdramc.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Atmel (Multi-port DDR-)SDRAM Controller driver
|
||||
*
|
||||
* 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/module.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, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
|
||||
|
||||
static int atmel_ramc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct at91_ramc_caps *caps;
|
||||
struct clk *clk;
|
||||
|
||||
match = of_match_device(atmel_ramc_of_match, &pdev->dev);
|
||||
caps = match->data;
|
||||
|
||||
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",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = atmel_ramc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init atmel_ramc_init(void)
|
||||
{
|
||||
return platform_driver_register(&atmel_ramc_driver);
|
||||
}
|
||||
module_init(atmel_ramc_init);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
|
310
drivers/memory/core_reg.c
Normal file
310
drivers/memory/core_reg.c
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* EXYNOS - Showing system control registers of ARM/MNGS core
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <asm/core_regs.h>
|
||||
|
||||
struct device *dev;
|
||||
static int core;
|
||||
static struct task_struct *task;
|
||||
|
||||
#define DEF_REG_STRUCT(n) \
|
||||
struct register_type reg_##n = { \
|
||||
.name = ""#n, \
|
||||
.read_reg = mrs_##n##_read \
|
||||
};
|
||||
|
||||
/* armv8 */
|
||||
DEF_REG_STRUCT(SCTLR)
|
||||
DEF_REG_STRUCT(MAIR)
|
||||
DEF_REG_STRUCT(CPUACTLR)
|
||||
DEF_REG_STRUCT(CPUECTLR)
|
||||
DEF_REG_STRUCT(L2CTLR)
|
||||
DEF_REG_STRUCT(L2ACTLR)
|
||||
DEF_REG_STRUCT(L2ECTLR)
|
||||
DEF_REG_STRUCT(MPIDR)
|
||||
DEF_REG_STRUCT(MIDR)
|
||||
DEF_REG_STRUCT(REVIDR)
|
||||
|
||||
/* mongoose */
|
||||
DEF_REG_STRUCT(FEACTLR)
|
||||
DEF_REG_STRUCT(FEACTLR2)
|
||||
DEF_REG_STRUCT(FEACTLR3)
|
||||
DEF_REG_STRUCT(FPACTLR)
|
||||
DEF_REG_STRUCT(FPACTLR2)
|
||||
DEF_REG_STRUCT(LSACTLR)
|
||||
DEF_REG_STRUCT(LSACTLR2)
|
||||
DEF_REG_STRUCT(LSACTLR3)
|
||||
DEF_REG_STRUCT(LSACTLR4)
|
||||
DEF_REG_STRUCT(CKACTLR)
|
||||
DEF_REG_STRUCT(MCACTLR2)
|
||||
|
||||
#define SET_CORE_REG(r, v) { .reg = ®_##r, .val = v }
|
||||
|
||||
static struct core_register a53_regs[] = {
|
||||
SET_CORE_REG(SCTLR, 0),
|
||||
SET_CORE_REG(MAIR, 0),
|
||||
SET_CORE_REG(MPIDR, 0),
|
||||
SET_CORE_REG(MIDR, 0),
|
||||
SET_CORE_REG(REVIDR, 0),
|
||||
SET_CORE_REG(CPUACTLR, 1),
|
||||
SET_CORE_REG(CPUECTLR, 1),
|
||||
SET_CORE_REG(L2CTLR, 1),
|
||||
SET_CORE_REG(L2ACTLR, 1),
|
||||
SET_CORE_REG(L2ECTLR, 1),
|
||||
{},
|
||||
};
|
||||
|
||||
static struct core_register a57_regs[] = {
|
||||
SET_CORE_REG(SCTLR, 0),
|
||||
SET_CORE_REG(MAIR, 0),
|
||||
SET_CORE_REG(MPIDR, 0),
|
||||
SET_CORE_REG(MIDR, 0),
|
||||
SET_CORE_REG(REVIDR, 0),
|
||||
SET_CORE_REG(CPUACTLR, 1),
|
||||
SET_CORE_REG(CPUECTLR, 1),
|
||||
SET_CORE_REG(L2CTLR, 1),
|
||||
SET_CORE_REG(L2ACTLR, 1),
|
||||
SET_CORE_REG(L2ECTLR, 1),
|
||||
{},
|
||||
};
|
||||
|
||||
static struct core_register mngs_regs[] = {
|
||||
SET_CORE_REG(SCTLR, 0),
|
||||
SET_CORE_REG(MAIR, 0),
|
||||
SET_CORE_REG(MPIDR, 0),
|
||||
SET_CORE_REG(MIDR, 0),
|
||||
SET_CORE_REG(REVIDR, 0),
|
||||
SET_CORE_REG(CPUACTLR, 1),
|
||||
SET_CORE_REG(CPUECTLR, 1),
|
||||
SET_CORE_REG(L2CTLR, 1),
|
||||
SET_CORE_REG(L2ACTLR, 1),
|
||||
SET_CORE_REG(L2ECTLR, 1),
|
||||
SET_CORE_REG(FEACTLR, 1),
|
||||
SET_CORE_REG(FEACTLR2, 1),
|
||||
SET_CORE_REG(FEACTLR3, 1),
|
||||
SET_CORE_REG(FPACTLR, 1),
|
||||
SET_CORE_REG(FPACTLR2, 1),
|
||||
SET_CORE_REG(LSACTLR, 1),
|
||||
SET_CORE_REG(LSACTLR2, 1),
|
||||
SET_CORE_REG(LSACTLR3, 1),
|
||||
SET_CORE_REG(LSACTLR4, 1),
|
||||
SET_CORE_REG(CKACTLR, 1),
|
||||
SET_CORE_REG(MCACTLR2, 0),
|
||||
{},
|
||||
};
|
||||
|
||||
enum armv8_core_type {
|
||||
A57_CORE,
|
||||
A53_CORE,
|
||||
MNGS_CORE,
|
||||
};
|
||||
|
||||
static char *core_names[] = {"cortex-a57", "cortex-a53", "mongoose"};
|
||||
|
||||
static enum armv8_core_type get_core_type(void)
|
||||
{
|
||||
u32 midr = (u32)mrs_MIDR_read();
|
||||
if ((midr >> 24) == 0x53) {
|
||||
if ((midr & 0xfff0) == 0x10)
|
||||
return MNGS_CORE;
|
||||
} else if ((midr >> 24) == 0x41) {
|
||||
if ((midr & 0xfff0) == 0xD070)
|
||||
return A57_CORE;
|
||||
else if ((midr & 0xfff0) == 0xD030)
|
||||
return A53_CORE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 get_guide_value_from_dt(int core, const char *name)
|
||||
{
|
||||
struct device_node *dn = NULL;
|
||||
u64 value;
|
||||
int ret;
|
||||
enum armv8_core_type ct = get_core_type();
|
||||
char *core_name = core_names[ct];
|
||||
|
||||
dn = dev->of_node;
|
||||
dn = of_get_child_by_name(dn, core_name);
|
||||
if (!dn) {
|
||||
pr_info("cannot find node:%s\n", core_name);
|
||||
return -1;
|
||||
}
|
||||
dn = of_get_child_by_name(dn, name);
|
||||
if (!dn) {
|
||||
pr_info("cannot find node:%s\n", name);
|
||||
return -1;
|
||||
}
|
||||
ret = of_property_read_u64(dn, "guide", &value);
|
||||
if (ret) {
|
||||
pr_info("cannot read guide value of :%s\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int coretest_init(void)
|
||||
{
|
||||
u64 val;
|
||||
int diff_cnt = 0;
|
||||
enum armv8_core_type core_type = get_core_type();
|
||||
struct core_register *regs;
|
||||
u32 midr = (u32)mrs_MIDR_read();
|
||||
u32 revidr = (u32)mrs_REVIDR_read();
|
||||
|
||||
pr_info("[Core type: %s, Rev: R%xP%x (revidr:0x%08x)]\n",
|
||||
core_names[core_type],
|
||||
(midr & 0xf00000) >> 20,
|
||||
midr & 0xf,
|
||||
revidr);
|
||||
|
||||
switch (core_type) {
|
||||
case A57_CORE:
|
||||
regs = a57_regs;
|
||||
break;
|
||||
case A53_CORE:
|
||||
regs = a53_regs;
|
||||
break;
|
||||
case MNGS_CORE:
|
||||
regs = mngs_regs;
|
||||
break;
|
||||
}
|
||||
|
||||
while (regs->reg) {
|
||||
if (regs->val == 1) {
|
||||
regs->val = get_guide_value_from_dt(core, regs->reg->name);
|
||||
}
|
||||
val = regs->reg->read_reg();
|
||||
if (regs->val != 0 && val != regs->val) {
|
||||
pr_info("%10s: 0x%016llX (0x%016llX)\n",
|
||||
regs->reg->name, val, regs->val);
|
||||
diff_cnt++;
|
||||
} else {
|
||||
pr_info("%10s: 0x%016llX\n", regs->reg->name, val);
|
||||
}
|
||||
regs++;
|
||||
}
|
||||
|
||||
if (diff_cnt == 0)
|
||||
pr_info("all values ok\n");
|
||||
else
|
||||
pr_info("need to be checked: %d\n", diff_cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thread_func(void *data)
|
||||
{
|
||||
coretest_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NODE_STORE(name) \
|
||||
static ssize_t name##_store(struct kobject *kobj, struct kobj_attribute *attr, \
|
||||
const char *buf, size_t count)
|
||||
#define NODE_SHOW(name) \
|
||||
static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
#define NODE_ATTR(name) \
|
||||
static struct kobj_attribute name##_attribute = __ATTR(name, S_IWUSR|S_IRUGO, name##_show, name##_store);
|
||||
|
||||
NODE_STORE(core)
|
||||
{
|
||||
sscanf(buf, "%d", &core);
|
||||
|
||||
task = kthread_create(thread_func, NULL, "thread%u", 0);
|
||||
kthread_bind(task, core);
|
||||
wake_up_process(task);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
NODE_SHOW(core)
|
||||
{
|
||||
return sprintf(buf, "core = %d\n", core);
|
||||
}
|
||||
|
||||
NODE_ATTR(core)
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&core_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static struct kobject *core_kobj;
|
||||
|
||||
static int core_reg_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev = &pdev->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int core_reg_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id core_reg_dt_match[] = {
|
||||
{ .compatible = "samsung,exynos-core", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver core_reg_driver = {
|
||||
.probe = core_reg_probe,
|
||||
.remove = core_reg_remove,
|
||||
.driver = {
|
||||
.name = "exynos-core",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = core_reg_dt_match,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init core_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
core_kobj = kobject_create_and_add("core_reg", kernel_kobj);
|
||||
if (!core_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_group(core_kobj, &attr_group);
|
||||
if (ret)
|
||||
kobject_put(core_kobj);
|
||||
|
||||
ret = platform_driver_register(&core_reg_driver);
|
||||
if (!ret)
|
||||
pr_info("%s: init\n", core_reg_driver.driver.name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit core_exit(void)
|
||||
{
|
||||
kobject_put(core_kobj);
|
||||
platform_driver_unregister(&core_reg_driver);
|
||||
}
|
||||
|
||||
module_init(core_init);
|
||||
module_exit(core_exit);
|
||||
|
||||
MODULE_AUTHOR("Jungwook Kim");
|
||||
MODULE_DESCRIPTION("showing system-control registers of ARM/MNGS core");
|
||||
MODULE_LICENSE("GPL");
|
1940
drivers/memory/emif.c
Normal file
1940
drivers/memory/emif.c
Normal file
File diff suppressed because it is too large
Load diff
589
drivers/memory/emif.h
Normal file
589
drivers/memory/emif.h
Normal file
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
* 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)
|
||||
|
||||
#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;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __EMIF_H */
|
474
drivers/memory/exynos-mcomp.c
Normal file
474
drivers/memory/exynos-mcomp.c
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.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/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/memblock.h>
|
||||
#include "exynos-mcomp.h"
|
||||
|
||||
/* file rw */
|
||||
#include <linux/file.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define CHK_SIZE 4096
|
||||
|
||||
static const char driver_name[] = "exynos-mcomp";
|
||||
|
||||
/* irq, SFR base, address information and tasklet */
|
||||
struct memory_comp mem_comp;
|
||||
struct tasklet_struct tasklet;
|
||||
static unsigned short *comp_info;
|
||||
static struct cout_pool {
|
||||
struct list_head node;
|
||||
unsigned char *buf;
|
||||
bool is_used;
|
||||
} couts[10];
|
||||
static LIST_HEAD(cout_list);
|
||||
DEFINE_SPINLOCK(cout_lock);
|
||||
|
||||
static int memory_decomp(unsigned char *decout_data, const unsigned char *comp_data,
|
||||
unsigned int comp_len, unsigned char* Cout_data)
|
||||
{
|
||||
register unsigned int Chdr_data;
|
||||
register unsigned int Dhdin; // inner header of DAE
|
||||
unsigned char* start_comp_data;
|
||||
unsigned char* start_Cout_data;
|
||||
unsigned char* start_decout_data;
|
||||
|
||||
start_comp_data = (unsigned char *)comp_data;
|
||||
start_Cout_data = Cout_data;
|
||||
start_decout_data = decout_data;
|
||||
|
||||
do {
|
||||
Chdr_data = *comp_data;
|
||||
comp_data++;
|
||||
|
||||
if(~Chdr_data & 0x01) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data+=2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
|
||||
if(~Chdr_data & 0x02) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data+=2;
|
||||
comp_data+=2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x04) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data+=2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x08) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x10) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x20) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x40) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
if(~Chdr_data & 0x80) {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)comp_data);
|
||||
Cout_data += 2;
|
||||
comp_data += 2;
|
||||
} else {
|
||||
*((unsigned short*)Cout_data) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
|
||||
if((*comp_data) >> 7) {
|
||||
*((unsigned short*)(Cout_data)) = *((unsigned short*)(Cout_data - ((*comp_data) & 0x7f)));
|
||||
Cout_data += 2;
|
||||
}
|
||||
comp_data++;
|
||||
}
|
||||
|
||||
} while(comp_data - start_comp_data < comp_len);
|
||||
|
||||
Cout_data = start_Cout_data;
|
||||
|
||||
do {
|
||||
Dhdin = (*Cout_data);
|
||||
Cout_data += ((Dhdin & 0x01));
|
||||
*decout_data = ((Dhdin >> 0 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 1 & 0x01));
|
||||
*(decout_data + 1) = ((Dhdin >> 1 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 2 & 0x01));
|
||||
*(decout_data + 2) = ((Dhdin >> 2 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 3 & 0x01));
|
||||
*(decout_data + 3) = ((Dhdin >> 3 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 4 & 0x01));
|
||||
*(decout_data + 4) = ((Dhdin >> 4 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 5 & 0x01));
|
||||
*(decout_data + 5) = ((Dhdin >> 5 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 6 & 0x01));
|
||||
*(decout_data + 6) = ((Dhdin >> 6 & 0x01)) * (*Cout_data);
|
||||
Cout_data += ((Dhdin >> 7 & 0x01));
|
||||
*(decout_data + 7) = ((Dhdin >> 7 & 0x01)) * (*Cout_data);
|
||||
decout_data += 8;
|
||||
Cout_data += 1;
|
||||
} while(decout_data - start_decout_data < CHK_SIZE);
|
||||
|
||||
Cout_data = start_Cout_data;
|
||||
decout_data = start_decout_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mem_comp_irq - irq clear and scheduling the sswap thread
|
||||
*/
|
||||
static irqreturn_t mem_comp_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
int done_st = 0;
|
||||
|
||||
/* check the incorrect access */
|
||||
done_st = readl(mem_comp.base + ISR) & (0x1);
|
||||
|
||||
if (!done_st) {
|
||||
pr_debug("%s: interrupt does not happen\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* clear interrupt */
|
||||
writel(ISR_CLEAR, mem_comp.base + ISR);
|
||||
/* scheduling the sswap thread */
|
||||
tasklet_hi_schedule(&tasklet);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* memory_comp_start_compress - let HW IP for compressing start
|
||||
* @disk_num: disk number of sswap disk.
|
||||
* @nr_pages: the number of pages which request compressing
|
||||
*
|
||||
* Write the address of sswap disk, compbuf, compinfo into SFR.
|
||||
* and let HW IP start by writing CMD register, or return EBUSY
|
||||
* if HW IP is busy compressing another disk.
|
||||
*/
|
||||
static int memory_comp_start_compress(u32 disk_num, u32 nr_pages)
|
||||
{
|
||||
struct memory_comp *mc = &mem_comp;
|
||||
phys_addr_t page;
|
||||
|
||||
/* check for device whether or not HW IP is compressing */
|
||||
while ((readl(mc->base + CMD) & 0x1) == 0x1);
|
||||
|
||||
/* if 0, compress the maximum size, 8MB */
|
||||
if (!nr_pages)
|
||||
nr_pages = SZ_8M >> PAGE_SHIFT;
|
||||
|
||||
/* input align addr */
|
||||
page = mc->sswap_disk_addr[disk_num];
|
||||
page = page >> 12;
|
||||
pr_debug("disk_addr = %llx\n", page);
|
||||
__raw_writel(page, mc->base + DISK_ADDR);
|
||||
|
||||
page = mc->comp_buf_addr[disk_num];
|
||||
page = page >> 12;
|
||||
pr_debug("comp_addr = %llx\n", page);
|
||||
__raw_writel(page, mc->base + COMPBUF_ADDR);
|
||||
|
||||
page = mc->comp_info_addr[disk_num];
|
||||
page = page >> 12;
|
||||
pr_debug("comp_info_addr = %llx\n", page);
|
||||
__raw_writel(page, mc->base + COMPINFO_ADDR);
|
||||
|
||||
/* set cmd of start */
|
||||
writel((nr_pages << CMD_PAGES) | CMD_START, mc->base + CMD);
|
||||
|
||||
while ((readl(mc->base + CMD) & 0x1) == 0x1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_comp_len(unsigned short *comp_info, int nr_page)
|
||||
{
|
||||
int i;
|
||||
int len = 0;
|
||||
int count = (nr_page % 8 == 0)? nr_page/8 : nr_page/8 + 1;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
len += comp_info[i];
|
||||
}
|
||||
|
||||
pr_debug("comp_info count: %d nr_page = %d comp_len = %d\n",
|
||||
count, nr_page, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int mcomp_compress_page(int disk_num, const unsigned char *in, unsigned char *out)
|
||||
{
|
||||
int comp_len;
|
||||
|
||||
pr_debug("%s in=%p out=%p\n", __func__, in, out);
|
||||
mem_comp.sswap_disk_addr[disk_num] = virt_to_phys(in);
|
||||
mem_comp.comp_buf_addr[disk_num] = virt_to_phys(out);
|
||||
mem_comp.comp_info_addr[disk_num] = virt_to_phys(comp_info);
|
||||
memory_comp_start_compress(disk_num, 1);
|
||||
|
||||
comp_len = get_comp_len(comp_info, 1);
|
||||
|
||||
return comp_len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcomp_compress_page);
|
||||
|
||||
int mcomp_decompress_page(const unsigned char *in, int comp_len, unsigned char *out)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct cout_pool *cout;
|
||||
unsigned char *buf;
|
||||
|
||||
spin_lock_irqsave(&cout_lock, flags);
|
||||
list_for_each_entry(cout, &cout_list, node) {
|
||||
if (!cout->is_used) {
|
||||
buf = cout->buf;
|
||||
cout->is_used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&cout_lock, flags);
|
||||
|
||||
memory_decomp(out, in, comp_len, buf);
|
||||
|
||||
spin_lock_irqsave(&cout_lock, flags);
|
||||
cout->is_used = false;
|
||||
spin_unlock_irqrestore(&cout_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mcomp_decompress_page);
|
||||
|
||||
static int memory_compressor_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
int irq, i;
|
||||
u32 temp;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
pr_debug("Fail to get map register\n");
|
||||
return -1;
|
||||
}
|
||||
mem_comp.base = devm_ioremap_resource(dev, res);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
pr_debug("Fail to get IRQ resource \n");
|
||||
return -1;
|
||||
}
|
||||
mem_comp.irq = irq;
|
||||
|
||||
/* interrupt register */
|
||||
ret = request_irq(mem_comp.irq, mem_comp_irq_handler, IRQF_DISABLED,
|
||||
driver_name, NULL);
|
||||
|
||||
/* mem_comp struct reset */
|
||||
for (i = 0; i < NUM_DISK; i++) {
|
||||
mem_comp.comp_info_addr[i] = 0;
|
||||
mem_comp.comp_buf_addr[i] = 0;
|
||||
mem_comp.sswap_disk_addr[i] = 0;
|
||||
}
|
||||
|
||||
/* control */
|
||||
temp = readl(mem_comp.base + CONTROL);
|
||||
temp |= (0x1 << CONTROL_ARUSER);
|
||||
temp |= (0x1 << CONTROL_AWUSER);
|
||||
temp |= (0xFF << CONTROL_THRESHOLD);
|
||||
temp |= (0x2 << CONTROL_ARCACHE);
|
||||
temp |= (0x2 << CONTROL_AWCACHE);
|
||||
writel(temp, mem_comp.base + CONTROL);
|
||||
|
||||
comp_info = (unsigned short *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!comp_info) {
|
||||
pr_info("page alloc fail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(couts); i++) {
|
||||
couts[i].buf = (unsigned char *)page_address(alloc_pages(GFP_ATOMIC, 1));
|
||||
if (!couts[i].buf) {
|
||||
pr_info("page alloc fail: cout\n");
|
||||
goto err_cout_alloc;
|
||||
}
|
||||
couts[i].is_used = false;
|
||||
list_add(&couts[i].node, &cout_list);
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Loaded driver for Mcomp\n");
|
||||
|
||||
return ret;
|
||||
|
||||
err_cout_alloc:
|
||||
for (i = 0; i < ARRAY_SIZE(couts); i++) {
|
||||
if (couts[i].buf)
|
||||
free_pages((unsigned long)couts[i].buf, 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int memory_compressor_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* unmapping */
|
||||
iounmap(mem_comp.base);
|
||||
|
||||
/* remove an interrupt handler */
|
||||
free_irq(mem_comp.irq, NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(couts); i++) {
|
||||
if (couts[i].buf)
|
||||
free_pages((unsigned long)couts[i].buf, 1);
|
||||
}
|
||||
|
||||
free_page((unsigned long)comp_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mcomp_dt_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos-mcomp",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver mem_comp_driver = {
|
||||
.probe = memory_compressor_probe,
|
||||
.remove = memory_compressor_remove,
|
||||
.driver = {
|
||||
.name = "exynos-mcomp",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mcomp_dt_match,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init memory_compressor_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = platform_driver_register(&mem_comp_driver);
|
||||
if (!ret)
|
||||
pr_info("%s: init\n",
|
||||
mem_comp_driver.driver.name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit memory_compressor_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mem_comp_driver);
|
||||
}
|
||||
late_initcall(memory_compressor_init);
|
||||
module_exit(memory_compressor_exit);
|
||||
|
||||
MODULE_DESCRIPTION("memory_compressor");
|
||||
MODULE_AUTHOR("Samsung");
|
||||
MODULE_LICENSE("GPL");
|
58
drivers/memory/exynos-mcomp.h
Normal file
58
drivers/memory/exynos-mcomp.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.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.
|
||||
*/
|
||||
|
||||
#ifndef __EXYNOS_MEMOMY_COMPRESSOR_H
|
||||
#define __EXYNOS_MEMOMY_COMPRESSOR_H
|
||||
|
||||
#ifndef NUM_DISK
|
||||
#define NUM_DISK 4
|
||||
#endif
|
||||
struct memory_comp {
|
||||
unsigned int irq;
|
||||
void __iomem *base;
|
||||
phys_addr_t sswap_disk_addr[NUM_DISK];
|
||||
phys_addr_t comp_buf_addr[NUM_DISK];
|
||||
phys_addr_t comp_info_addr[NUM_DISK];
|
||||
};
|
||||
|
||||
int mcomp_compress_page(int disk_num, const unsigned char *in, unsigned char *out);
|
||||
int mcomp_decompress_page(const unsigned char *in, int comp_len, unsigned char *out);
|
||||
#endif
|
||||
|
||||
/* regs */
|
||||
#ifndef __EXYNOS__REGS_MEMORY_COMPRESSOR_H
|
||||
#define __EXYNOS__REGS_MEMORY_COMPRESSOR_H
|
||||
|
||||
#define CMD 0x0000
|
||||
#define CMD_START 0x1
|
||||
#define CMD_PAGES 16
|
||||
|
||||
#define IER 0x0004
|
||||
#define IER_ENABLE 0x1
|
||||
|
||||
#define ISR 0x0008
|
||||
#define ISR_CLEAR 1
|
||||
|
||||
#define DISK_ADDR 0x000C
|
||||
#define COMPBUF_ADDR 0x0010
|
||||
#define COMPINFO_ADDR 0x0014
|
||||
|
||||
#define CONTROL 0x0018
|
||||
#define CONTROL_DRCG_DISABLE 8
|
||||
#define CONTROL_ARCACHE 12
|
||||
#define CONTROL_AWCACHE 16
|
||||
#define CONTROL_THRESHOLD 20
|
||||
#define CONTROL_AWUSER 28
|
||||
#define CONTROL_ARUSER 29
|
||||
|
||||
#define BUS_CONFIG 0x001C
|
||||
#define VERSION 0x0F00
|
||||
|
||||
#endif
|
251
drivers/memory/fsl-corenet-cf.c
Normal file
251
drivers/memory/fsl-corenet-cf.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
static const struct ccf_info ccf1_info = {
|
||||
.version = CCF1,
|
||||
.err_reg_offs = 0xa00,
|
||||
};
|
||||
|
||||
static const struct ccf_info ccf2_info = {
|
||||
.version = CCF2,
|
||||
.err_reg_offs = 0xe40,
|
||||
};
|
||||
|
||||
static const struct of_device_id ccf_matches[] = {
|
||||
{
|
||||
.compatible = "fsl,corenet1-cf",
|
||||
.data = &ccf1_info,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,corenet2-cf",
|
||||
.data = &ccf2_info,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
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_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;
|
||||
};
|
||||
|
||||
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 (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;
|
||||
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;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, ccf);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "%s: no irq\n", __func__);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
switch (ccf->info->version) {
|
||||
case CCF1:
|
||||
/* On CCF1 this register enables rather than disables. */
|
||||
iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errdis);
|
||||
break;
|
||||
|
||||
case CCF2:
|
||||
iowrite32be(0, &ccf->err_regs->errdis);
|
||||
iowrite32be(ERRDET_LAE | ERRDET_CV, &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,
|
||||
.owner = THIS_MODULE,
|
||||
.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");
|
309
drivers/memory/fsl_ifc.c
Normal file
309
drivers/memory/fsl_ifc.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* 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/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 <asm/prom.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->regs)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) {
|
||||
u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->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_regs __iomem *ifc = ctrl->regs;
|
||||
|
||||
/*
|
||||
* Clear all the common status and event registers
|
||||
*/
|
||||
if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
|
||||
out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
|
||||
|
||||
/* enable all error and events */
|
||||
out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN);
|
||||
|
||||
/* enable all error and event interrupts */
|
||||
out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN);
|
||||
out_be32(&ifc->cm_erattr0, 0x0);
|
||||
out_be32(&ifc->cm_erattr1, 0x0);
|
||||
|
||||
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->regs);
|
||||
|
||||
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_regs __iomem *ifc = ctrl->regs;
|
||||
unsigned long flags;
|
||||
u32 stat;
|
||||
|
||||
spin_lock_irqsave(&nand_irq_lock, flags);
|
||||
|
||||
stat = in_be32(&ifc->ifc_nand.nand_evter_stat);
|
||||
if (stat) {
|
||||
out_be32(&ifc->ifc_nand.nand_evter_stat, 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_regs __iomem *ifc = ctrl->regs;
|
||||
u32 err_axiid, err_srcid, status, cs_err, err_addr;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
/* read for chip select error */
|
||||
cs_err = in_be32(&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 */
|
||||
out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
|
||||
|
||||
/* read error attribute registers print the error information */
|
||||
status = in_be32(&ifc->cm_erattr0);
|
||||
err_addr = in_be32(&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;
|
||||
|
||||
|
||||
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->regs = of_iomap(dev->dev.of_node, 0);
|
||||
if (!fsl_ifc_ctrl_dev->regs) {
|
||||
dev_err(&dev->dev, "failed to get memory region\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* 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 == NO_IRQ) {
|
||||
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");
|
198
drivers/memory/mcomp_test.c
Normal file
198
drivers/memory/mcomp_test.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* EXYNOS - Memory Compressor Unit Test
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include "exynos-mcomp.h"
|
||||
|
||||
static int core;
|
||||
static char file[255];
|
||||
static char str[SZ_4K];
|
||||
static char buf[SZ_4K];
|
||||
|
||||
static struct task_struct *task;
|
||||
|
||||
static int thread_func(void *data)
|
||||
{
|
||||
size_t size = PAGE_SIZE;
|
||||
unsigned char *disk, *comp, *decomp;
|
||||
int comp_len;
|
||||
int i;
|
||||
int fd = -1;
|
||||
|
||||
/* set string to disk buffer */
|
||||
if (str[0] == 0 && file[0] == 0) {
|
||||
pr_info("set string or file_name first\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file[0] != 0) {
|
||||
fd = sys_open(file, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
pr_info("file open failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (sys_read(fd, buf, size) < 0) {
|
||||
pr_info("file open failed\n");
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
}
|
||||
pr_info("file opened: %s\n", file);
|
||||
} else if (str[0] != 0) {
|
||||
memcpy(buf, str, size);
|
||||
}
|
||||
|
||||
/* allocate buffers */
|
||||
disk = (unsigned char *)get_zeroed_page(GFP_KERNEL);
|
||||
comp = (unsigned char *)get_zeroed_page(GFP_KERNEL);
|
||||
decomp = (unsigned char *)get_zeroed_page(GFP_KERNEL);
|
||||
|
||||
if (!disk || !comp || !decomp) {
|
||||
pr_info("page alloc fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* original data */
|
||||
memcpy(disk, buf, size);
|
||||
|
||||
/* compress */
|
||||
comp_len = mcomp_compress_page(0, disk, comp);
|
||||
|
||||
if (comp_len == 0) {
|
||||
pr_info("comp_len failed\n");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
if (comp_len == size) {
|
||||
pr_info("not compressed\n");
|
||||
pr_info("disk=%s\n", disk);
|
||||
pr_info("comp=%s\n", comp);
|
||||
} else {
|
||||
/* decompress */
|
||||
mcomp_decompress_page(comp, comp_len, decomp);
|
||||
|
||||
/* verify original data */
|
||||
for (i = 0; i < size; i++) {
|
||||
if (disk[i] != decomp[i]) {
|
||||
pr_info("verification failed: disk[%d]=%d decomp[%d]=%d\n",
|
||||
i, disk[i], i, decomp[i]);
|
||||
goto error_free;
|
||||
}
|
||||
}
|
||||
pr_info("verification ok\n");
|
||||
}
|
||||
|
||||
error_free:
|
||||
if (fd >= 0)
|
||||
sys_close(fd);
|
||||
|
||||
free_page((unsigned long)decomp);
|
||||
free_page((unsigned long)comp);
|
||||
free_page((unsigned long)disk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* file */
|
||||
static ssize_t file_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%s", file);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t file_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "file = %s\n", file);
|
||||
}
|
||||
|
||||
static struct kobj_attribute file_attribute = __ATTR(file, S_IWUSR|S_IRUGO, file_show, file_store);
|
||||
|
||||
/* str */
|
||||
static ssize_t str_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%s", str);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t str_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "str = %s\n", str);
|
||||
}
|
||||
|
||||
static struct kobj_attribute str_attribute = __ATTR(str, S_IWUSR|S_IRUGO, str_show, str_store);
|
||||
|
||||
/* core */
|
||||
static ssize_t core_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%d", &core);
|
||||
|
||||
task = kthread_create(thread_func, NULL, "thread%u", 0);
|
||||
kthread_bind(task, core);
|
||||
wake_up_process(task);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t core_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "core = %d\n", core);
|
||||
}
|
||||
|
||||
static struct kobj_attribute core_attribute = __ATTR(core, S_IWUSR|S_IRUGO, core_show, core_store);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&core_attribute.attr,
|
||||
&str_attribute.attr,
|
||||
&file_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static struct kobject *core_kobj;
|
||||
|
||||
static int __init core_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
core_kobj = kobject_create_and_add("mcomp_test", kernel_kobj);
|
||||
if (!core_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_group(core_kobj, &attr_group);
|
||||
if (ret)
|
||||
kobject_put(core_kobj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit core_exit(void)
|
||||
{
|
||||
kobject_put(core_kobj);
|
||||
}
|
||||
|
||||
module_init(core_init);
|
||||
module_exit(core_exit);
|
||||
|
||||
MODULE_AUTHOR("Jungwook Kim");
|
||||
MODULE_LICENSE("GPL");
|
362
drivers/memory/mvebu-devbus.c
Normal file
362
drivers/memory/mvebu-devbus.c
Normal file
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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, "%s has no '%s' property\n",
|
||||
name, node->full_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,
|
||||
"%s has no 'devbus,bus-width' property\n",
|
||||
node->full_name);
|
||||
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,
|
||||
"%s has no 'devbus,sync-enable' property\n",
|
||||
node->full_name);
|
||||
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",
|
||||
.owner = THIS_MODULE,
|
||||
.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");
|
153
drivers/memory/of_memory.c
Normal file
153
drivers/memory/of_memory.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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_kzalloc(dev, sizeof(*timings) * arr_sz,
|
||||
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
drivers/memory/of_memory.h
Normal file
36
drivers/memory/of_memory.h
Normal 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_ */
|
255
drivers/memory/tegra20-mc.c
Normal file
255
drivers/memory/tegra20-mc.c
Normal file
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* Tegra20 Memory Controller
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra20-mc"
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
|
||||
#define MC_INT_ERR_SHIFT 6
|
||||
#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
|
||||
|
||||
#define MC_GART_ERROR_REQ 0x30
|
||||
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
|
||||
#define MC_SECURITY_VIOLATION_STATUS 0x74
|
||||
|
||||
#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
|
||||
|
||||
#define MC_CLIENT_ID_MASK 0x3f
|
||||
|
||||
#define NUM_MC_REG_BANKS 2
|
||||
|
||||
struct tegra20_mc {
|
||||
void __iomem *regs[NUM_MC_REG_BANKS];
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (offs < 0x24)
|
||||
val = readl(mc->regs[0] + offs);
|
||||
else if (offs < 0x400)
|
||||
val = readl(mc->regs[1] + offs - 0x3c);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
|
||||
{
|
||||
if (offs < 0x24)
|
||||
writel(val, mc->regs[0] + offs);
|
||||
else if (offs < 0x400)
|
||||
writel(val, mc->regs[1] + offs - 0x3c);
|
||||
}
|
||||
|
||||
static const char * const tegra20_mc_client[] = {
|
||||
"cbr_display0a",
|
||||
"cbr_display0ab",
|
||||
"cbr_display0b",
|
||||
"cbr_display0bb",
|
||||
"cbr_display0c",
|
||||
"cbr_display0cb",
|
||||
"cbr_display1b",
|
||||
"cbr_display1bb",
|
||||
"cbr_eppup",
|
||||
"cbr_g2pr",
|
||||
"cbr_g2sr",
|
||||
"cbr_mpeunifbr",
|
||||
"cbr_viruv",
|
||||
"csr_avpcarm7r",
|
||||
"csr_displayhc",
|
||||
"csr_displayhcb",
|
||||
"csr_fdcdrd",
|
||||
"csr_g2dr",
|
||||
"csr_host1xdmar",
|
||||
"csr_host1xr",
|
||||
"csr_idxsrd",
|
||||
"csr_mpcorer",
|
||||
"csr_mpe_ipred",
|
||||
"csr_mpeamemrd",
|
||||
"csr_mpecsrd",
|
||||
"csr_ppcsahbdmar",
|
||||
"csr_ppcsahbslvr",
|
||||
"csr_texsrd",
|
||||
"csr_vdebsevr",
|
||||
"csr_vdember",
|
||||
"csr_vdemcer",
|
||||
"csr_vdetper",
|
||||
"cbw_eppu",
|
||||
"cbw_eppv",
|
||||
"cbw_eppy",
|
||||
"cbw_mpeunifbw",
|
||||
"cbw_viwsb",
|
||||
"cbw_viwu",
|
||||
"cbw_viwv",
|
||||
"cbw_viwy",
|
||||
"ccw_g2dw",
|
||||
"csw_avpcarm7w",
|
||||
"csw_fdcdwr",
|
||||
"csw_host1xw",
|
||||
"csw_ispw",
|
||||
"csw_mpcorew",
|
||||
"csw_mpecswr",
|
||||
"csw_ppcsahbdmaw",
|
||||
"csw_ppcsahbslvw",
|
||||
"csw_vdebsevw",
|
||||
"csw_vdembew",
|
||||
"csw_vdetpmw",
|
||||
};
|
||||
|
||||
static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
|
||||
{
|
||||
u32 addr, req;
|
||||
const char *client = "Unknown";
|
||||
int idx, cid;
|
||||
const struct reg_info {
|
||||
u32 offset;
|
||||
u32 write_bit; /* 0=READ, 1=WRITE */
|
||||
int cid_shift;
|
||||
char *message;
|
||||
} reg[] = {
|
||||
{
|
||||
.offset = MC_DECERR_EMEM_OTHERS_STATUS,
|
||||
.write_bit = 31,
|
||||
.message = "MC_DECERR",
|
||||
},
|
||||
{
|
||||
.offset = MC_GART_ERROR_REQ,
|
||||
.cid_shift = 1,
|
||||
.message = "MC_GART_ERR",
|
||||
|
||||
},
|
||||
{
|
||||
.offset = MC_SECURITY_VIOLATION_STATUS,
|
||||
.write_bit = 31,
|
||||
.message = "MC_SECURITY_ERR",
|
||||
},
|
||||
};
|
||||
|
||||
idx = n - MC_INT_ERR_SHIFT;
|
||||
if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) {
|
||||
dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
|
||||
BIT(n));
|
||||
return;
|
||||
}
|
||||
|
||||
req = mc_readl(mc, reg[idx].offset);
|
||||
cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK;
|
||||
if (cid < ARRAY_SIZE(tegra20_mc_client))
|
||||
client = tegra20_mc_client[cid];
|
||||
|
||||
addr = mc_readl(mc, reg[idx].offset + sizeof(u32));
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n",
|
||||
reg[idx].message, req, addr, client,
|
||||
(req & BIT(reg[idx].write_bit)) ? "write" : "read",
|
||||
(reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ?
|
||||
((req & SECURITY_VIOLATION_TYPE) ?
|
||||
"carveout" : "trustzone") : "");
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra20_mc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-mc", },
|
||||
{},
|
||||
};
|
||||
|
||||
static irqreturn_t tegra20_mc_isr(int irq, void *data)
|
||||
{
|
||||
u32 stat, mask, bit;
|
||||
struct tegra20_mc *mc = data;
|
||||
|
||||
stat = mc_readl(mc, MC_INTSTATUS);
|
||||
mask = mc_readl(mc, MC_INTMASK);
|
||||
mask &= stat;
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
while ((bit = ffs(mask)) != 0) {
|
||||
tegra20_mc_decode(mc, bit - 1);
|
||||
mask &= ~BIT(bit - 1);
|
||||
}
|
||||
|
||||
mc_writel(mc, stat, MC_INTSTATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tegra20_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *irq;
|
||||
struct tegra20_mc *mc;
|
||||
int i, err;
|
||||
u32 intmask;
|
||||
|
||||
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mc->regs[i]))
|
||||
return PTR_ERR(mc->regs[i]);
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), mc);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, mc);
|
||||
|
||||
intmask = MC_INT_INVALID_GART_PAGE |
|
||||
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
|
||||
mc_writel(mc, intmask, MC_INTMASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra20_mc_driver = {
|
||||
.probe = tegra20_mc_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra20_mc_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra20_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra20 MC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
378
drivers/memory/tegra30-mc.c
Normal file
378
drivers/memory/tegra30-mc.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Tegra30 Memory Controller
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra30-mc"
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
|
||||
#define MC_INT_ERR_SHIFT 6
|
||||
#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
|
||||
#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4)
|
||||
|
||||
#define MC_ERR_STATUS 0x8
|
||||
#define MC_ERR_ADR 0xc
|
||||
|
||||
#define MC_ERR_TYPE_SHIFT 28
|
||||
#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT)
|
||||
#define MC_ERR_TYPE_DECERR_EMEM 2
|
||||
#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
|
||||
#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
|
||||
#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
|
||||
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT)
|
||||
#define MC_ERR_RW_SHIFT 16
|
||||
#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT)
|
||||
#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1)
|
||||
|
||||
#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
|
||||
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
|
||||
#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
|
||||
#define MC_CLIENT_ID_MASK 0x7f
|
||||
|
||||
#define NUM_MC_REG_BANKS 4
|
||||
|
||||
struct tegra30_mc {
|
||||
void __iomem *regs[NUM_MC_REG_BANKS];
|
||||
struct device *dev;
|
||||
u32 ctx[0];
|
||||
};
|
||||
|
||||
static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (offs < 0x10)
|
||||
val = readl(mc->regs[0] + offs);
|
||||
else if (offs < 0x1f0)
|
||||
val = readl(mc->regs[1] + offs - 0x3c);
|
||||
else if (offs < 0x228)
|
||||
val = readl(mc->regs[2] + offs - 0x200);
|
||||
else if (offs < 0x400)
|
||||
val = readl(mc->regs[3] + offs - 0x284);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
|
||||
{
|
||||
if (offs < 0x10)
|
||||
writel(val, mc->regs[0] + offs);
|
||||
else if (offs < 0x1f0)
|
||||
writel(val, mc->regs[1] + offs - 0x3c);
|
||||
else if (offs < 0x228)
|
||||
writel(val, mc->regs[2] + offs - 0x200);
|
||||
else if (offs < 0x400)
|
||||
writel(val, mc->regs[3] + offs - 0x284);
|
||||
}
|
||||
|
||||
static const char * const tegra30_mc_client[] = {
|
||||
"csr_ptcr",
|
||||
"cbr_display0a",
|
||||
"cbr_display0ab",
|
||||
"cbr_display0b",
|
||||
"cbr_display0bb",
|
||||
"cbr_display0c",
|
||||
"cbr_display0cb",
|
||||
"cbr_display1b",
|
||||
"cbr_display1bb",
|
||||
"cbr_eppup",
|
||||
"cbr_g2pr",
|
||||
"cbr_g2sr",
|
||||
"cbr_mpeunifbr",
|
||||
"cbr_viruv",
|
||||
"csr_afir",
|
||||
"csr_avpcarm7r",
|
||||
"csr_displayhc",
|
||||
"csr_displayhcb",
|
||||
"csr_fdcdrd",
|
||||
"csr_fdcdrd2",
|
||||
"csr_g2dr",
|
||||
"csr_hdar",
|
||||
"csr_host1xdmar",
|
||||
"csr_host1xr",
|
||||
"csr_idxsrd",
|
||||
"csr_idxsrd2",
|
||||
"csr_mpe_ipred",
|
||||
"csr_mpeamemrd",
|
||||
"csr_mpecsrd",
|
||||
"csr_ppcsahbdmar",
|
||||
"csr_ppcsahbslvr",
|
||||
"csr_satar",
|
||||
"csr_texsrd",
|
||||
"csr_texsrd2",
|
||||
"csr_vdebsevr",
|
||||
"csr_vdember",
|
||||
"csr_vdemcer",
|
||||
"csr_vdetper",
|
||||
"csr_mpcorelpr",
|
||||
"csr_mpcorer",
|
||||
"cbw_eppu",
|
||||
"cbw_eppv",
|
||||
"cbw_eppy",
|
||||
"cbw_mpeunifbw",
|
||||
"cbw_viwsb",
|
||||
"cbw_viwu",
|
||||
"cbw_viwv",
|
||||
"cbw_viwy",
|
||||
"ccw_g2dw",
|
||||
"csw_afiw",
|
||||
"csw_avpcarm7w",
|
||||
"csw_fdcdwr",
|
||||
"csw_fdcdwr2",
|
||||
"csw_hdaw",
|
||||
"csw_host1xw",
|
||||
"csw_ispw",
|
||||
"csw_mpcorelpw",
|
||||
"csw_mpcorew",
|
||||
"csw_mpecswr",
|
||||
"csw_ppcsahbdmaw",
|
||||
"csw_ppcsahbslvw",
|
||||
"csw_sataw",
|
||||
"csw_vdebsevw",
|
||||
"csw_vdedbgw",
|
||||
"csw_vdembew",
|
||||
"csw_vdetpmw",
|
||||
};
|
||||
|
||||
static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
|
||||
{
|
||||
u32 err, addr;
|
||||
const char * const mc_int_err[] = {
|
||||
"MC_DECERR",
|
||||
"Unknown",
|
||||
"MC_SECURITY_ERR",
|
||||
"MC_ARBITRATION_EMEM",
|
||||
"MC_SMMU_ERR",
|
||||
};
|
||||
const char * const err_type[] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"DECERR_EMEM",
|
||||
"SECURITY_TRUSTZONE",
|
||||
"SECURITY_CARVEOUT",
|
||||
"Unknown",
|
||||
"INVALID_SMMU_PAGE",
|
||||
"Unknown",
|
||||
};
|
||||
char attr[6];
|
||||
int cid, perm, type, idx;
|
||||
const char *client = "Unknown";
|
||||
|
||||
idx = n - MC_INT_ERR_SHIFT;
|
||||
if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) {
|
||||
dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
|
||||
BIT(n));
|
||||
return;
|
||||
}
|
||||
|
||||
err = mc_readl(mc, MC_ERR_STATUS);
|
||||
|
||||
type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
|
||||
perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
|
||||
MC_ERR_INVALID_SMMU_PAGE_SHIFT;
|
||||
if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE)
|
||||
sprintf(attr, "%c-%c-%c",
|
||||
(perm & BIT(2)) ? 'R' : '-',
|
||||
(perm & BIT(1)) ? 'W' : '-',
|
||||
(perm & BIT(0)) ? 'S' : '-');
|
||||
else
|
||||
attr[0] = '\0';
|
||||
|
||||
cid = err & MC_CLIENT_ID_MASK;
|
||||
if (cid < ARRAY_SIZE(tegra30_mc_client))
|
||||
client = tegra30_mc_client[cid];
|
||||
|
||||
addr = mc_readl(mc, MC_ERR_ADR);
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
|
||||
mc_int_err[idx], err, addr, client,
|
||||
(err & MC_ERR_SECURITY) ? "secure" : "non-secure",
|
||||
(err & MC_ERR_RW) ? "write" : "read",
|
||||
err_type[type], attr);
|
||||
}
|
||||
|
||||
static const u32 tegra30_mc_ctx[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_MISC1,
|
||||
MC_EMEM_ARB_RING3_THROTTLE,
|
||||
MC_EMEM_ARB_OVERRIDE,
|
||||
MC_INTMASK,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tegra30_mc_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_mc_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]);
|
||||
|
||||
mc_writel(mc, 1, MC_TIMING_CONTROL);
|
||||
/* Read-back to ensure that write reached */
|
||||
mc_readl(mc, MC_TIMING_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
|
||||
tegra30_mc_suspend,
|
||||
tegra30_mc_resume, NULL);
|
||||
|
||||
static const struct of_device_id tegra30_mc_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra30-mc", },
|
||||
{},
|
||||
};
|
||||
|
||||
static irqreturn_t tegra30_mc_isr(int irq, void *data)
|
||||
{
|
||||
u32 stat, mask, bit;
|
||||
struct tegra30_mc *mc = data;
|
||||
|
||||
stat = mc_readl(mc, MC_INTSTATUS);
|
||||
mask = mc_readl(mc, MC_INTMASK);
|
||||
mask &= stat;
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
while ((bit = ffs(mask)) != 0) {
|
||||
tegra30_mc_decode(mc, bit - 1);
|
||||
mask &= ~BIT(bit - 1);
|
||||
}
|
||||
|
||||
mc_writel(mc, stat, MC_INTSTATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tegra30_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *irq;
|
||||
struct tegra30_mc *mc;
|
||||
size_t bytes;
|
||||
int err, i;
|
||||
u32 intmask;
|
||||
|
||||
bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx);
|
||||
mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mc->regs[i]))
|
||||
return PTR_ERR(mc->regs[i]);
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), mc);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, mc);
|
||||
|
||||
intmask = MC_INT_INVALID_SMMU_PAGE |
|
||||
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
|
||||
mc_writel(mc, intmask, MC_INTMASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra30_mc_driver = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra30_mc_of_match,
|
||||
.pm = &tegra30_mc_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra30_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra30 MC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
427
drivers/memory/ti-aemif.c
Normal file
427
drivers/memory/ti-aemif.c
Normal file
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#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", },
|
||||
{},
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
if (np == NULL)
|
||||
return 0;
|
||||
|
||||
aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
|
||||
if (!aemif)
|
||||
return -ENOMEM;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
clk_prepare_enable(aemif->clk);
|
||||
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
|
||||
|
||||
if (of_device_is_compatible(np, "ti,da850-aemif"))
|
||||
aemif->cs_offset = 2;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
goto error;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
ret = of_platform_populate(child_np, NULL, NULL, dev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
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 = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.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);
|
Loading…
Add table
Add a link
Reference in a new issue