Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

96
drivers/memory/Kconfig Normal file
View 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
View 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

View 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
View 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 = &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

File diff suppressed because it is too large Load diff

589
drivers/memory/emif.h Normal file
View 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 */

View 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");

View 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

View 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
View 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
View 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");

View 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
View 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);

View file

@ -0,0 +1,36 @@
/*
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_MEMORY_OF_REG_H
#define __LINUX_MEMORY_OF_REG_H
#if defined(CONFIG_OF) && defined(CONFIG_DDR)
extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
struct device *dev);
extern const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies);
#else
static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev)
{
return NULL;
}
static inline const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */

255
drivers/memory/tegra20-mc.c Normal file
View 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
View 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
View 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);