Commit bccb5d53 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'memory-controller-drv-5.16-2' of...

Merge tag 'memory-controller-drv-5.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.16, part two

1. Convert LPDDR2 bindings to dtschema and extend them with new
   properties.
2. Tegra 20 EMC: support matching timings by LPDDR2 configuration from
   devicetree.

* tag 'memory-controller-drv-5.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl:
  memory: tegra20-emc: Add runtime dependency on devfreq governor module
  memory: tegra20-emc: Support matching timings by LPDDR2 configuration
  memory: Add LPDDR2-info helpers
  dt-bindings: memory: tegra20: emc: Document new LPDDR2 sub-node
  dt-bindings: Add vendor prefix for Elpida Memory
  dt-bindings: memory: lpddr2: Document Elpida B8132B2PB-6D-F
  dt-bindings: memory: lpddr2: Add revision-id properties
  dt-bindings: memory: lpddr2: Convert to schema
  dt-bindings: Relocate DDR bindings

Link: https://lore.kernel.org/r/20211021093002.118192-1-krzysztof.kozlowski@canonical.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents c13d3398 14b43c20
* LPDDR2 SDRAM memories compliant to JEDEC JESD209-2
Required properties:
- compatible : Should be one of - "jedec,lpddr2-nvm", "jedec,lpddr2-s2",
"jedec,lpddr2-s4"
"ti,jedec-lpddr2-s2" should be listed if the memory part is LPDDR2-S2 type
"ti,jedec-lpddr2-s4" should be listed if the memory part is LPDDR2-S4 type
"ti,jedec-lpddr2-nvm" should be listed if the memory part is LPDDR2-NVM type
- density : <u32> representing density in Mb (Mega bits)
- io-width : <u32> representing bus width. Possible values are 8, 16, and 32
Optional properties:
The following optional properties represent the minimum value of some AC
timing parameters of the DDR device in terms of number of clock cycles.
These values shall be obtained from the device data-sheet.
- tRRD-min-tck
- tWTR-min-tck
- tXP-min-tck
- tRTP-min-tck
- tCKE-min-tck
- tRPab-min-tck
- tRCD-min-tck
- tWR-min-tck
- tRASmin-min-tck
- tCKESR-min-tck
- tFAW-min-tck
Child nodes:
- The lpddr2 node may have one or more child nodes of type "lpddr2-timings".
"lpddr2-timings" provides AC timing parameters of the device for
a given speed-bin. The user may provide the timings for as many
speed-bins as is required. Please see Documentation/devicetree/
bindings/ddr/lpddr2-timings.txt for more information on "lpddr2-timings"
Example:
elpida_ECB240ABACN : lpddr2 {
compatible = "Elpida,ECB240ABACN","jedec,lpddr2-s4";
density = <2048>;
io-width = <32>;
tRPab-min-tck = <3>;
tRCD-min-tck = <3>;
tWR-min-tck = <3>;
tRASmin-min-tck = <3>;
tRRD-min-tck = <2>;
tWTR-min-tck = <2>;
tXP-min-tck = <2>;
tRTP-min-tck = <2>;
tCKE-min-tck = <3>;
tCKESR-min-tck = <3>;
tFAW-min-tck = <8>;
timings_elpida_ECB240ABACN_400mhz: lpddr2-timings@0 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <400000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <7500>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
timings_elpida_ECB240ABACN_200mhz: lpddr2-timings@1 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <200000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <10000>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
}
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/ddr/jedec,lpddr2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: LPDDR2 SDRAM compliant to JEDEC JESD209-2
maintainers:
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
properties:
compatible:
oneOf:
- items:
- enum:
- elpida,ECB240ABACN
- elpida,B8132B2PB-6D-F
- enum:
- jedec,lpddr2-s4
- items:
- enum:
- jedec,lpddr2-s2
- items:
- enum:
- jedec,lpddr2-nvm
revision-id1:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 255
description: |
Revision 1 value of SDRAM chip. Obtained from device datasheet.
revision-id2:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 255
description: |
Revision 2 value of SDRAM chip. Obtained from device datasheet.
density:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Density in megabits of SDRAM chip. Obtained from device datasheet.
enum:
- 64
- 128
- 256
- 512
- 1024
- 2048
- 4096
- 8192
- 16384
- 32768
io-width:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
IO bus width in bits of SDRAM chip. Obtained from device datasheet.
enum:
- 32
- 16
- 8
tRRD-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Active bank a to active bank b in terms of number of clock cycles.
Obtained from device datasheet.
tWTR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Internal WRITE-to-READ command delay in terms of number of clock cycles.
Obtained from device datasheet.
tXP-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Exit power-down to next valid command delay in terms of number of clock
cycles. Obtained from device datasheet.
tRTP-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Internal READ to PRECHARGE command delay in terms of number of clock
cycles. Obtained from device datasheet.
tCKE-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
CKE minimum pulse width (HIGH and LOW pulse width) in terms of number
of clock cycles. Obtained from device datasheet.
tRPab-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Row precharge time (all banks) in terms of number of clock cycles.
Obtained from device datasheet.
tRCD-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
RAS-to-CAS delay in terms of number of clock cycles. Obtained from
device datasheet.
tWR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
WRITE recovery time in terms of number of clock cycles. Obtained from
device datasheet.
tRASmin-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Row active time in terms of number of clock cycles. Obtained from device
datasheet.
tCKESR-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
CKE minimum pulse width during SELF REFRESH (low pulse width during
SELF REFRESH) in terms of number of clock cycles. Obtained from device
datasheet.
tFAW-min-tck:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 16
description: |
Four-bank activate window in terms of number of clock cycles. Obtained
from device datasheet.
patternProperties:
"^lpddr2-timings":
type: object
description: |
The lpddr2 node may have one or more child nodes of type "lpddr2-timings".
"lpddr2-timings" provides AC timing parameters of the device for
a given speed-bin. The user may provide the timings for as many
speed-bins as is required. Please see Documentation/devicetree/
bindings/memory-controllers/ddr/lpddr2-timings.txt for more information
on "lpddr2-timings".
required:
- compatible
- density
- io-width
additionalProperties: false
examples:
- |
elpida_ECB240ABACN: lpddr2 {
compatible = "elpida,ECB240ABACN", "jedec,lpddr2-s4";
density = <2048>;
io-width = <32>;
revision-id1 = <1>;
revision-id2 = <0>;
tRPab-min-tck = <3>;
tRCD-min-tck = <3>;
tWR-min-tck = <3>;
tRASmin-min-tck = <3>;
tRRD-min-tck = <2>;
tWTR-min-tck = <2>;
tXP-min-tck = <2>;
tRTP-min-tck = <2>;
tCKE-min-tck = <3>;
tCKESR-min-tck = <3>;
tFAW-min-tck = <8>;
timings_elpida_ECB240ABACN_400mhz: lpddr2-timings0 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <400000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <7500>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
timings_elpida_ECB240ABACN_200mhz: lpddr2-timings1 {
compatible = "jedec,lpddr2-timings";
min-freq = <10000000>;
max-freq = <200000000>;
tRPab = <21000>;
tRCD = <18000>;
tWR = <15000>;
tRAS-min = <42000>;
tRRD = <10000>;
tWTR = <10000>;
tXP = <7500>;
tRTP = <7500>;
tCKESR = <15000>;
tDQSCK-max = <5500>;
tFAW = <50000>;
tZQCS = <90000>;
tZQCL = <360000>;
tZQinit = <1000000>;
tRAS-max-ns = <70000>;
};
};
...@@ -43,8 +43,9 @@ These values shall be obtained from the device data-sheet. ...@@ -43,8 +43,9 @@ These values shall be obtained from the device data-sheet.
Child nodes: Child nodes:
- The lpddr3 node may have one or more child nodes of type "lpddr3-timings". - The lpddr3 node may have one or more child nodes of type "lpddr3-timings".
"lpddr3-timings" provides AC timing parameters of the device for "lpddr3-timings" provides AC timing parameters of the device for
a given speed-bin. Please see Documentation/devicetree/ a given speed-bin. Please see
bindings/ddr/lpddr3-timings.txt for more information on "lpddr3-timings" Documentation/devicetree/bindings/memory-controllers/ddr/lpddr3-timings.txt
for more information on "lpddr3-timings"
Example: Example:
......
...@@ -164,12 +164,20 @@ patternProperties: ...@@ -164,12 +164,20 @@ patternProperties:
"#size-cells": "#size-cells":
const: 0 const: 0
lpddr2:
$ref: "ddr/jedec,lpddr2.yaml#"
type: object
patternProperties: patternProperties:
"^emc-table@[0-9]+$": "^emc-table@[0-9]+$":
$ref: "#/$defs/emc-table" $ref: "#/$defs/emc-table"
required: oneOf:
- nvidia,ram-code - required:
- nvidia,ram-code
- required:
- lpddr2
additionalProperties: false additionalProperties: false
...@@ -227,4 +235,15 @@ examples: ...@@ -227,4 +235,15 @@ examples:
0x00000000 0x00000000 0x00000000 0x00000000>; 0x00000000 0x00000000 0x00000000 0x00000000>;
}; };
}; };
emc-tables@1 {
reg = <1>;
lpddr2 {
compatible = "elpida,B8132B2PB-6D-F", "jedec,lpddr2-s4";
revision-id1 = <1>;
density = <2048>;
io-width = <16>;
};
};
}; };
...@@ -51,7 +51,8 @@ properties: ...@@ -51,7 +51,8 @@ properties:
$ref: '/schemas/types.yaml#/definitions/phandle' $ref: '/schemas/types.yaml#/definitions/phandle'
description: | description: |
phandle of the connected DRAM memory device. For more information please phandle of the connected DRAM memory device. For more information please
refer to documentation file: Documentation/devicetree/bindings/ddr/lpddr3.txt refer to documentation file:
Documentation/devicetree/bindings/memory-controllers/ddr/lpddr3.txt
operating-points-v2: true operating-points-v2: true
......
...@@ -353,6 +353,8 @@ patternProperties: ...@@ -353,6 +353,8 @@ patternProperties:
description: Shenzhen Elida Technology Co., Ltd. description: Shenzhen Elida Technology Co., Ltd.
"^elimo,.*": "^elimo,.*":
description: Elimo Engineering Ltd. description: Elimo Engineering Ltd.
"^elpida,.*":
description: Elpida Memory, Inc.
"^embest,.*": "^embest,.*":
description: Shenzhen Embest Technology Co., Ltd. description: Shenzhen Embest Technology Co., Ltd.
"^emlid,.*": "^emlid,.*":
......
...@@ -112,6 +112,26 @@ ...@@ -112,6 +112,26 @@
#define NUM_DDR_ADDR_TABLE_ENTRIES 11 #define NUM_DDR_ADDR_TABLE_ENTRIES 11
#define NUM_DDR_TIMING_TABLE_ENTRIES 4 #define NUM_DDR_TIMING_TABLE_ENTRIES 4
#define LPDDR2_MANID_SAMSUNG 1
#define LPDDR2_MANID_QIMONDA 2
#define LPDDR2_MANID_ELPIDA 3
#define LPDDR2_MANID_ETRON 4
#define LPDDR2_MANID_NANYA 5
#define LPDDR2_MANID_HYNIX 6
#define LPDDR2_MANID_MOSEL 7
#define LPDDR2_MANID_WINBOND 8
#define LPDDR2_MANID_ESMT 9
#define LPDDR2_MANID_SPANSION 11
#define LPDDR2_MANID_SST 12
#define LPDDR2_MANID_ZMOS 13
#define LPDDR2_MANID_INTEL 14
#define LPDDR2_MANID_NUMONYX 254
#define LPDDR2_MANID_MICRON 255
#define LPDDR2_TYPE_S4 0
#define LPDDR2_TYPE_S2 1
#define LPDDR2_TYPE_NVM 2
/* Structure for DDR addressing info from the JEDEC spec */ /* Structure for DDR addressing info from the JEDEC spec */
struct lpddr2_addressing { struct lpddr2_addressing {
u32 num_banks; u32 num_banks;
...@@ -170,6 +190,33 @@ extern const struct lpddr2_timings ...@@ -170,6 +190,33 @@ extern const struct lpddr2_timings
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; extern const struct lpddr2_min_tck lpddr2_jedec_min_tck;
/* Structure of MR8 */
union lpddr2_basic_config4 {
u32 value;
struct {
unsigned int arch_type : 2;
unsigned int density : 4;
unsigned int io_width : 2;
} __packed;
};
/*
* Structure for information about LPDDR2 chip. All parameters are
* matching raw values of standard mode register bitfields or set to
* -ENOENT if info unavailable.
*/
struct lpddr2_info {
int arch_type;
int density;
int io_width;
int manufacturer_id;
int revision_id1;
int revision_id2;
};
const char *lpddr2_jedec_manufacturer(unsigned int manufacturer_id);
/* /*
* Structure for timings for LPDDR3 based on LPDDR2 plus additional fields. * Structure for timings for LPDDR3 based on LPDDR2 plus additional fields.
* All parameters are in pico seconds(ps) excluding max_freq, min_freq which * All parameters are in pico seconds(ps) excluding max_freq, min_freq which
......
...@@ -131,3 +131,44 @@ const struct lpddr2_min_tck lpddr2_jedec_min_tck = { ...@@ -131,3 +131,44 @@ const struct lpddr2_min_tck lpddr2_jedec_min_tck = {
.tFAW = 8 .tFAW = 8
}; };
EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck); EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck);
const char *lpddr2_jedec_manufacturer(unsigned int manufacturer_id)
{
switch (manufacturer_id) {
case LPDDR2_MANID_SAMSUNG:
return "Samsung";
case LPDDR2_MANID_QIMONDA:
return "Qimonda";
case LPDDR2_MANID_ELPIDA:
return "Elpida";
case LPDDR2_MANID_ETRON:
return "Etron";
case LPDDR2_MANID_NANYA:
return "Nanya";
case LPDDR2_MANID_HYNIX:
return "Hynix";
case LPDDR2_MANID_MOSEL:
return "Mosel";
case LPDDR2_MANID_WINBOND:
return "Winbond";
case LPDDR2_MANID_ESMT:
return "ESMT";
case LPDDR2_MANID_SPANSION:
return "Spansion";
case LPDDR2_MANID_SST:
return "SST";
case LPDDR2_MANID_ZMOS:
return "ZMOS";
case LPDDR2_MANID_INTEL:
return "Intel";
case LPDDR2_MANID_NUMONYX:
return "Numonyx";
case LPDDR2_MANID_MICRON:
return "Micron";
default:
break;
}
return "invalid";
}
EXPORT_SYMBOL_GPL(lpddr2_jedec_manufacturer);
...@@ -298,3 +298,90 @@ const struct lpddr3_timings ...@@ -298,3 +298,90 @@ const struct lpddr3_timings
return NULL; return NULL;
} }
EXPORT_SYMBOL(of_lpddr3_get_ddr_timings); EXPORT_SYMBOL(of_lpddr3_get_ddr_timings);
/**
* of_lpddr2_get_info() - extracts information about the lpddr2 chip.
* @np: Pointer to device tree node containing lpddr2 info
* @dev: Device requesting info
*
* Populates lpddr2_info structure by extracting data from device
* tree node. Returns pointer to populated structure. If error
* happened while populating, returns NULL. If property is missing
* in a device-tree, then the corresponding value is set to -ENOENT.
*/
const struct lpddr2_info
*of_lpddr2_get_info(struct device_node *np, struct device *dev)
{
struct lpddr2_info *ret_info, info = {};
struct property *prop;
const char *cp;
int err;
err = of_property_read_u32(np, "revision-id1", &info.revision_id1);
if (err)
info.revision_id1 = -ENOENT;
err = of_property_read_u32(np, "revision-id2", &info.revision_id2);
if (err)
info.revision_id2 = -ENOENT;
err = of_property_read_u32(np, "io-width", &info.io_width);
if (err)
return NULL;
info.io_width = 32 / info.io_width - 1;
err = of_property_read_u32(np, "density", &info.density);
if (err)
return NULL;
info.density = ffs(info.density) - 7;
if (of_device_is_compatible(np, "jedec,lpddr2-s4"))
info.arch_type = LPDDR2_TYPE_S4;
else if (of_device_is_compatible(np, "jedec,lpddr2-s2"))
info.arch_type = LPDDR2_TYPE_S2;
else if (of_device_is_compatible(np, "jedec,lpddr2-nvm"))
info.arch_type = LPDDR2_TYPE_NVM;
else
return NULL;
prop = of_find_property(np, "compatible", NULL);
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp)) {
#define OF_LPDDR2_VENDOR_CMP(compat, ID) \
if (!of_compat_cmp(cp, compat ",", strlen(compat ","))) { \
info.manufacturer_id = LPDDR2_MANID_##ID; \
break; \
}
OF_LPDDR2_VENDOR_CMP("samsung", SAMSUNG)
OF_LPDDR2_VENDOR_CMP("qimonda", QIMONDA)
OF_LPDDR2_VENDOR_CMP("elpida", ELPIDA)
OF_LPDDR2_VENDOR_CMP("etron", ETRON)
OF_LPDDR2_VENDOR_CMP("nanya", NANYA)
OF_LPDDR2_VENDOR_CMP("hynix", HYNIX)
OF_LPDDR2_VENDOR_CMP("mosel", MOSEL)
OF_LPDDR2_VENDOR_CMP("winbond", WINBOND)
OF_LPDDR2_VENDOR_CMP("esmt", ESMT)
OF_LPDDR2_VENDOR_CMP("spansion", SPANSION)
OF_LPDDR2_VENDOR_CMP("sst", SST)
OF_LPDDR2_VENDOR_CMP("zmos", ZMOS)
OF_LPDDR2_VENDOR_CMP("intel", INTEL)
OF_LPDDR2_VENDOR_CMP("numonyx", NUMONYX)
OF_LPDDR2_VENDOR_CMP("micron", MICRON)
#undef OF_LPDDR2_VENDOR_CMP
}
if (!info.manufacturer_id)
info.manufacturer_id = -ENOENT;
ret_info = devm_kzalloc(dev, sizeof(*ret_info), GFP_KERNEL);
if (ret_info)
*ret_info = info;
return ret_info;
}
EXPORT_SYMBOL(of_lpddr2_get_info);
...@@ -20,6 +20,9 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, ...@@ -20,6 +20,9 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np,
const struct lpddr3_timings * const struct lpddr3_timings *
of_lpddr3_get_ddr_timings(struct device_node *np_ddr, of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies); struct device *dev, u32 device_type, u32 *nr_frequencies);
const struct lpddr2_info *of_lpddr2_get_info(struct device_node *np,
struct device *dev);
#else #else
static inline const struct lpddr2_min_tck static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev) *of_get_min_tck(struct device_node *np, struct device *dev)
...@@ -46,6 +49,12 @@ static inline const struct lpddr3_timings ...@@ -46,6 +49,12 @@ static inline const struct lpddr3_timings
{ {
return NULL; return NULL;
} }
static inline const struct lpddr2_info
*of_lpddr2_get_info(struct device_node *np, struct device *dev)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */ #endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */ #endif /* __LINUX_MEMORY_OF_REG_ */
...@@ -16,6 +16,7 @@ config TEGRA20_EMC ...@@ -16,6 +16,7 @@ config TEGRA20_EMC
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ select PM_DEVFREQ
select DDR
help help
This driver is for the External Memory Controller (EMC) found on This driver is for the External Memory Controller (EMC) found on
Tegra20 chips. The EMC controls the external DRAM on the board. Tegra20 chips. The EMC controls the external DRAM on the board.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Author: Dmitry Osipenko <digetx@gmail.com> * Author: Dmitry Osipenko <digetx@gmail.com>
*/ */
#include <linux/bitfield.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/tegra.h> #include <linux/clk/tegra.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
...@@ -27,11 +28,15 @@ ...@@ -27,11 +28,15 @@
#include <soc/tegra/common.h> #include <soc/tegra/common.h>
#include <soc/tegra/fuse.h> #include <soc/tegra/fuse.h>
#include "../jedec_ddr.h"
#include "../of_memory.h"
#include "mc.h" #include "mc.h"
#define EMC_INTSTATUS 0x000 #define EMC_INTSTATUS 0x000
#define EMC_INTMASK 0x004 #define EMC_INTMASK 0x004
#define EMC_DBG 0x008 #define EMC_DBG 0x008
#define EMC_ADR_CFG_0 0x010
#define EMC_TIMING_CONTROL 0x028 #define EMC_TIMING_CONTROL 0x028
#define EMC_RC 0x02c #define EMC_RC 0x02c
#define EMC_RFC 0x030 #define EMC_RFC 0x030
...@@ -68,6 +73,7 @@ ...@@ -68,6 +73,7 @@
#define EMC_QUSE_EXTRA 0x0ac #define EMC_QUSE_EXTRA 0x0ac
#define EMC_ODT_WRITE 0x0b0 #define EMC_ODT_WRITE 0x0b0
#define EMC_ODT_READ 0x0b4 #define EMC_ODT_READ 0x0b4
#define EMC_MRR 0x0ec
#define EMC_FBIO_CFG5 0x104 #define EMC_FBIO_CFG5 0x104
#define EMC_FBIO_CFG6 0x114 #define EMC_FBIO_CFG6 0x114
#define EMC_STAT_CONTROL 0x160 #define EMC_STAT_CONTROL 0x160
...@@ -94,6 +100,7 @@ ...@@ -94,6 +100,7 @@
#define EMC_REFRESH_OVERFLOW_INT BIT(3) #define EMC_REFRESH_OVERFLOW_INT BIT(3)
#define EMC_CLKCHANGE_COMPLETE_INT BIT(4) #define EMC_CLKCHANGE_COMPLETE_INT BIT(4)
#define EMC_MRR_DIVLD_INT BIT(5)
#define EMC_DBG_READ_MUX_ASSEMBLY BIT(0) #define EMC_DBG_READ_MUX_ASSEMBLY BIT(0)
#define EMC_DBG_WRITE_MUX_ACTIVE BIT(1) #define EMC_DBG_WRITE_MUX_ACTIVE BIT(1)
...@@ -102,11 +109,25 @@ ...@@ -102,11 +109,25 @@
#define EMC_DBG_CFG_PRIORITY BIT(24) #define EMC_DBG_CFG_PRIORITY BIT(24)
#define EMC_FBIO_CFG5_DRAM_WIDTH_X16 BIT(4) #define EMC_FBIO_CFG5_DRAM_WIDTH_X16 BIT(4)
#define EMC_FBIO_CFG5_DRAM_TYPE GENMASK(1, 0)
#define EMC_MRR_DEV_SELECTN GENMASK(31, 30)
#define EMC_MRR_MRR_MA GENMASK(23, 16)
#define EMC_MRR_MRR_DATA GENMASK(15, 0)
#define EMC_ADR_CFG_0_EMEM_NUMDEV GENMASK(25, 24)
#define EMC_PWR_GATHER_CLEAR (1 << 8) #define EMC_PWR_GATHER_CLEAR (1 << 8)
#define EMC_PWR_GATHER_DISABLE (2 << 8) #define EMC_PWR_GATHER_DISABLE (2 << 8)
#define EMC_PWR_GATHER_ENABLE (3 << 8) #define EMC_PWR_GATHER_ENABLE (3 << 8)
enum emc_dram_type {
DRAM_TYPE_RESERVED,
DRAM_TYPE_DDR1,
DRAM_TYPE_LPDDR2,
DRAM_TYPE_DDR2,
};
static const u16 emc_timing_registers[] = { static const u16 emc_timing_registers[] = {
EMC_RC, EMC_RC,
EMC_RFC, EMC_RFC,
...@@ -201,6 +222,14 @@ struct tegra_emc { ...@@ -201,6 +222,14 @@ struct tegra_emc {
struct mutex rate_lock; struct mutex rate_lock;
struct devfreq_simple_ondemand_data ondemand_data; struct devfreq_simple_ondemand_data ondemand_data;
/* memory chip identity information */
union lpddr2_basic_config4 basic_conf4;
unsigned int manufacturer_id;
unsigned int revision_id1;
unsigned int revision_id2;
bool mrr_error;
}; };
static irqreturn_t tegra_emc_isr(int irq, void *data) static irqreturn_t tegra_emc_isr(int irq, void *data)
...@@ -397,15 +426,19 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, ...@@ -397,15 +426,19 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
if (!emc->timings) if (!emc->timings)
return -ENOMEM; return -ENOMEM;
emc->num_timings = child_count;
timing = emc->timings; timing = emc->timings;
for_each_child_of_node(node, child) { for_each_child_of_node(node, child) {
if (of_node_name_eq(child, "lpddr2"))
continue;
err = load_one_timing_from_dt(emc, timing++, child); err = load_one_timing_from_dt(emc, timing++, child);
if (err) { if (err) {
of_node_put(child); of_node_put(child);
return err; return err;
} }
emc->num_timings++;
} }
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
...@@ -422,12 +455,18 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, ...@@ -422,12 +455,18 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
} }
static struct device_node * static struct device_node *
tegra_emc_find_node_by_ram_code(struct device *dev) tegra_emc_find_node_by_ram_code(struct tegra_emc *emc)
{ {
struct device *dev = emc->dev;
struct device_node *np; struct device_node *np;
u32 value, ram_code; u32 value, ram_code;
int err; int err;
if (emc->mrr_error) {
dev_warn(dev, "memory timings skipped due to MRR error\n");
return NULL;
}
if (of_get_child_count(dev->of_node) == 0) { if (of_get_child_count(dev->of_node) == 0) {
dev_info_once(dev, "device-tree doesn't have memory timings\n"); dev_info_once(dev, "device-tree doesn't have memory timings\n");
return NULL; return NULL;
...@@ -442,8 +481,49 @@ tegra_emc_find_node_by_ram_code(struct device *dev) ...@@ -442,8 +481,49 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
np = of_find_node_by_name(np, "emc-tables")) { np = of_find_node_by_name(np, "emc-tables")) {
err = of_property_read_u32(np, "nvidia,ram-code", &value); err = of_property_read_u32(np, "nvidia,ram-code", &value);
if (err || value != ram_code) { if (err || value != ram_code) {
of_node_put(np); struct device_node *lpddr2_np;
continue; bool cfg_mismatches = false;
lpddr2_np = of_find_node_by_name(np, "lpddr2");
if (lpddr2_np) {
const struct lpddr2_info *info;
info = of_lpddr2_get_info(lpddr2_np, dev);
if (info) {
if (info->manufacturer_id >= 0 &&
info->manufacturer_id != emc->manufacturer_id)
cfg_mismatches = true;
if (info->revision_id1 >= 0 &&
info->revision_id1 != emc->revision_id1)
cfg_mismatches = true;
if (info->revision_id2 >= 0 &&
info->revision_id2 != emc->revision_id2)
cfg_mismatches = true;
if (info->density != emc->basic_conf4.density)
cfg_mismatches = true;
if (info->io_width != emc->basic_conf4.io_width)
cfg_mismatches = true;
if (info->arch_type != emc->basic_conf4.arch_type)
cfg_mismatches = true;
} else {
dev_err(dev, "failed to parse %pOF\n", lpddr2_np);
cfg_mismatches = true;
}
of_node_put(lpddr2_np);
} else {
cfg_mismatches = true;
}
if (cfg_mismatches) {
of_node_put(np);
continue;
}
} }
return np; return np;
...@@ -455,10 +535,72 @@ tegra_emc_find_node_by_ram_code(struct device *dev) ...@@ -455,10 +535,72 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
return NULL; return NULL;
} }
static int emc_read_lpddr_mode_register(struct tegra_emc *emc,
unsigned int emem_dev,
unsigned int register_addr,
unsigned int *register_data)
{
u32 memory_dev = emem_dev + 1;
u32 val, mr_mask = 0xff;
int err;
/* clear data-valid interrupt status */
writel_relaxed(EMC_MRR_DIVLD_INT, emc->regs + EMC_INTSTATUS);
/* issue mode register read request */
val = FIELD_PREP(EMC_MRR_DEV_SELECTN, memory_dev);
val |= FIELD_PREP(EMC_MRR_MRR_MA, register_addr);
writel_relaxed(val, emc->regs + EMC_MRR);
/* wait for the LPDDR2 data-valid interrupt */
err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, val,
val & EMC_MRR_DIVLD_INT,
1, 100);
if (err) {
dev_err(emc->dev, "mode register %u read failed: %d\n",
register_addr, err);
emc->mrr_error = true;
return err;
}
/* read out mode register data */
val = readl_relaxed(emc->regs + EMC_MRR);
*register_data = FIELD_GET(EMC_MRR_MRR_DATA, val) & mr_mask;
return 0;
}
static void emc_read_lpddr_sdram_info(struct tegra_emc *emc,
unsigned int emem_dev,
bool print_out)
{
/* these registers are standard for all LPDDR JEDEC memory chips */
emc_read_lpddr_mode_register(emc, emem_dev, 5, &emc->manufacturer_id);
emc_read_lpddr_mode_register(emc, emem_dev, 6, &emc->revision_id1);
emc_read_lpddr_mode_register(emc, emem_dev, 7, &emc->revision_id2);
emc_read_lpddr_mode_register(emc, emem_dev, 8, &emc->basic_conf4.value);
if (!print_out)
return;
dev_info(emc->dev, "SDRAM[dev%u]: manufacturer: 0x%x (%s) rev1: 0x%x rev2: 0x%x prefetch: S%u density: %uMbit iowidth: %ubit\n",
emem_dev, emc->manufacturer_id,
lpddr2_jedec_manufacturer(emc->manufacturer_id),
emc->revision_id1, emc->revision_id2,
4 >> emc->basic_conf4.arch_type,
64 << emc->basic_conf4.density,
32 >> emc->basic_conf4.io_width);
}
static int emc_setup_hw(struct tegra_emc *emc) static int emc_setup_hw(struct tegra_emc *emc)
{ {
u32 emc_cfg, emc_dbg, emc_fbio, emc_adr_cfg;
u32 intmask = EMC_REFRESH_OVERFLOW_INT; u32 intmask = EMC_REFRESH_OVERFLOW_INT;
u32 emc_cfg, emc_dbg, emc_fbio; static bool print_sdram_info_once;
enum emc_dram_type dram_type;
const char *dram_type_str;
unsigned int emem_numdev;
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
...@@ -496,7 +638,36 @@ static int emc_setup_hw(struct tegra_emc *emc) ...@@ -496,7 +638,36 @@ static int emc_setup_hw(struct tegra_emc *emc)
else else
emc->dram_bus_width = 32; emc->dram_bus_width = 32;
dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width); dram_type = FIELD_GET(EMC_FBIO_CFG5_DRAM_TYPE, emc_fbio);
switch (dram_type) {
case DRAM_TYPE_RESERVED:
dram_type_str = "INVALID";
break;
case DRAM_TYPE_DDR1:
dram_type_str = "DDR1";
break;
case DRAM_TYPE_LPDDR2:
dram_type_str = "LPDDR2";
break;
case DRAM_TYPE_DDR2:
dram_type_str = "DDR2";
break;
}
emc_adr_cfg = readl_relaxed(emc->regs + EMC_ADR_CFG_0);
emem_numdev = FIELD_GET(EMC_ADR_CFG_0_EMEM_NUMDEV, emc_adr_cfg) + 1;
dev_info_once(emc->dev, "%ubit DRAM bus, %u %s %s attached\n",
emc->dram_bus_width, emem_numdev, dram_type_str,
emem_numdev == 2 ? "devices" : "device");
if (dram_type == DRAM_TYPE_LPDDR2) {
while (emem_numdev--)
emc_read_lpddr_sdram_info(emc, emem_numdev,
!print_sdram_info_once);
print_sdram_info_once = true;
}
return 0; return 0;
} }
...@@ -1049,14 +1220,6 @@ static int tegra_emc_probe(struct platform_device *pdev) ...@@ -1049,14 +1220,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
emc->clk_nb.notifier_call = tegra_emc_clk_change_notify; emc->clk_nb.notifier_call = tegra_emc_clk_change_notify;
emc->dev = &pdev->dev; emc->dev = &pdev->dev;
np = tegra_emc_find_node_by_ram_code(&pdev->dev);
if (np) {
err = tegra_emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
}
emc->regs = devm_platform_ioremap_resource(pdev, 0); emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(emc->regs)) if (IS_ERR(emc->regs))
return PTR_ERR(emc->regs); return PTR_ERR(emc->regs);
...@@ -1065,6 +1228,14 @@ static int tegra_emc_probe(struct platform_device *pdev) ...@@ -1065,6 +1228,14 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (err) if (err)
return err; return err;
np = tegra_emc_find_node_by_ram_code(emc);
if (np) {
err = tegra_emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
}
err = devm_request_irq(&pdev->dev, irq, tegra_emc_isr, 0, err = devm_request_irq(&pdev->dev, irq, tegra_emc_isr, 0,
dev_name(&pdev->dev), emc); dev_name(&pdev->dev), emc);
if (err) { if (err) {
...@@ -1117,4 +1288,5 @@ module_platform_driver(tegra_emc_driver); ...@@ -1117,4 +1288,5 @@ module_platform_driver(tegra_emc_driver);
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra20 EMC driver"); MODULE_DESCRIPTION("NVIDIA Tegra20 EMC driver");
MODULE_SOFTDEP("pre: governor_simpleondemand");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment