mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-10 07:50:04 +00:00
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. -----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAmFxMrMQHGtyemtAa2Vy bmVsLm9yZwAKCRDBN2bmhouD1zN4EACUUda2z/Ico+Y0J8ygDdHFa9CE2Q0rbWPc 4cCeQSae+joWSTGxZUGIKd2SDCz2Ai0U2AOe4v9YqbmW4J6izdeUqaJHSorC7g+8 /Or307HKwOi0kV8kwSC0AABPlkTFDTR5wjG908vAfEU4KfmkHXpKB7EJn2vpM/Km JzZ9K+v0Bm6VH5CLQZpcFiffXQJDeN1Cqve45g50BfpFITUche/TR8FRfJPJ90n0 fJ2kjlMGT87U8tb4JKpYy6UoRCToxJq+uy/0nUAzUXAgBzM1zF9tVFGpw2WNUu1a j+PwFAA7eybX53BKFm0LfC/Z3PNJ+GYeDzUv+3VaSL+x5aNWRa1ffBBgWNAnnoWD QO0QGnZUxM+JEtTkgeByVblP4Aq4hmlSOJ3ErZ0NyH7iJyREqJEpJkXoSm0QIY4F TxiyrHJg0rSF4VTFU1qVBzn1m1VbfWR36RqOW29t8GJoMri8vCW7eyT0Z7xe4x8W er9kIGGpRQ2G3mtBRjHSXGjIztG5dVNbp5eEq7roJoQDcEcPnwox+8Au6NkA7JDS ednahUQ3qFEzS0vLXNgwvM1z77xPcgVFRRfdySfMUN0R3wBuhBp8EMTUNs2GrGSD KhxlK7WYcvwMy++o1M6Lmy7ukh40Tpgsmhg9CNr+eLTjJDs3rO/qdgteRnuQtsFd qh0PqiaX1w== =cTPK -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmFxwL0ACgkQmmx57+YA GNkfPw//ZFrn9+3SKYzXRgWKdPeGO9DfOrooEFe37DMsdCCDkTsZPxnKooidI61t g4UlgpPnnDEOas7glALgPwdFGqePzbocXgWtzgc/9kKNVNlJfE4OBVo8WykNSTOB Yqv38Z0Fz7XURfxzQCRHi9oMihkp0j9MaMmApO1/1ejYSueKnZ13OESPhmwrJBx0 0oJCpkpOdxzfBlDaoaymSa0seWl1kAcWX6bya5dgDBGKKXhl69yLrqDSafmc57dx fOSgmpHSWPT+VavuGG6+p0daEk4vY3A37A/cVgaXl+Te02/O78luNHj0Wu1kjcxk lXhsBbb0iEyCUBRHpxwBm5cszm3yaN1GFnd0kW7vjV1kscnjwcDVJ8r9B/u1jIIj RQQq32QJ548c4eqSOT/OqhpI+r/R3z4pdFnaYiz6NDW5WY3UucKwoFCmhGRYk+T6 Xp+5RreoUisDKg6rDDG6M6H/iTcYQw9W6NvLkf2HghokmcTG9cH1o/Q05COZe7dK yyZmb61fxS3SmsiWHrAtz2L8ztFibAjHjRfMV44TXX5JKp//hcMWg6HBMf+zD474 wLsKGSimeKgSsy2NjqRwh3VSueA4ETrp4Iue2NaRCZ5ROnepCa6WzuUBPNTqXv+8 HZTAHG7d7TAQ2O+hhShSGMP2tCkssuJ4OkmzbkPKDDe8xECaf9k= =NKAR -----END PGP SIGNATURE----- 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.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
bccb5d53e2
@ -1,102 +0,0 @@
|
||||
* 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>;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
# 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.
|
||||
Child nodes:
|
||||
- The lpddr3 node may have one or more child nodes of type "lpddr3-timings".
|
||||
"lpddr3-timings" provides AC timing parameters of the device for
|
||||
a given speed-bin. Please see Documentation/devicetree/
|
||||
bindings/ddr/lpddr3-timings.txt for more information on "lpddr3-timings"
|
||||
a given speed-bin. Please see
|
||||
Documentation/devicetree/bindings/memory-controllers/ddr/lpddr3-timings.txt
|
||||
for more information on "lpddr3-timings"
|
||||
|
||||
Example:
|
||||
|
@ -164,12 +164,20 @@ patternProperties:
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
lpddr2:
|
||||
$ref: "ddr/jedec,lpddr2.yaml#"
|
||||
type: object
|
||||
|
||||
patternProperties:
|
||||
"^emc-table@[0-9]+$":
|
||||
$ref: "#/$defs/emc-table"
|
||||
|
||||
required:
|
||||
- nvidia,ram-code
|
||||
oneOf:
|
||||
- required:
|
||||
- nvidia,ram-code
|
||||
|
||||
- required:
|
||||
- lpddr2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -227,4 +235,15 @@ examples:
|
||||
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:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle'
|
||||
description: |
|
||||
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
|
||||
|
||||
|
@ -353,6 +353,8 @@ patternProperties:
|
||||
description: Shenzhen Elida Technology Co., Ltd.
|
||||
"^elimo,.*":
|
||||
description: Elimo Engineering Ltd.
|
||||
"^elpida,.*":
|
||||
description: Elpida Memory, Inc.
|
||||
"^embest,.*":
|
||||
description: Shenzhen Embest Technology Co., Ltd.
|
||||
"^emlid,.*":
|
||||
|
@ -112,6 +112,26 @@
|
||||
#define NUM_DDR_ADDR_TABLE_ENTRIES 11
|
||||
#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 */
|
||||
struct lpddr2_addressing {
|
||||
u32 num_banks;
|
||||
@ -170,6 +190,33 @@ extern const struct lpddr2_timings
|
||||
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
|
||||
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.
|
||||
* 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 = {
|
||||
.tFAW = 8
|
||||
};
|
||||
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 @@ default_timings:
|
||||
return NULL;
|
||||
}
|
||||
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,
|
||||
const struct lpddr3_timings *
|
||||
of_lpddr3_get_ddr_timings(struct device_node *np_ddr,
|
||||
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
|
||||
static inline const struct lpddr2_min_tck
|
||||
*of_get_min_tck(struct device_node *np, struct device *dev)
|
||||
@ -46,6 +49,12 @@ static inline const struct lpddr3_timings
|
||||
{
|
||||
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 /* __LINUX_MEMORY_OF_REG_ */
|
||||
|
@ -16,6 +16,7 @@ config TEGRA20_EMC
|
||||
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_DEVFREQ
|
||||
select DDR
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra20 chips. The EMC controls the external DRAM on the board.
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/debugfs.h>
|
||||
@ -27,11 +28,15 @@
|
||||
#include <soc/tegra/common.h>
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "../jedec_ddr.h"
|
||||
#include "../of_memory.h"
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
#define EMC_INTSTATUS 0x000
|
||||
#define EMC_INTMASK 0x004
|
||||
#define EMC_DBG 0x008
|
||||
#define EMC_ADR_CFG_0 0x010
|
||||
#define EMC_TIMING_CONTROL 0x028
|
||||
#define EMC_RC 0x02c
|
||||
#define EMC_RFC 0x030
|
||||
@ -68,6 +73,7 @@
|
||||
#define EMC_QUSE_EXTRA 0x0ac
|
||||
#define EMC_ODT_WRITE 0x0b0
|
||||
#define EMC_ODT_READ 0x0b4
|
||||
#define EMC_MRR 0x0ec
|
||||
#define EMC_FBIO_CFG5 0x104
|
||||
#define EMC_FBIO_CFG6 0x114
|
||||
#define EMC_STAT_CONTROL 0x160
|
||||
@ -94,6 +100,7 @@
|
||||
|
||||
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
|
||||
#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_WRITE_MUX_ACTIVE BIT(1)
|
||||
@ -102,11 +109,25 @@
|
||||
#define EMC_DBG_CFG_PRIORITY BIT(24)
|
||||
|
||||
#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_DISABLE (2 << 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[] = {
|
||||
EMC_RC,
|
||||
EMC_RFC,
|
||||
@ -201,6 +222,14 @@ struct tegra_emc {
|
||||
struct mutex rate_lock;
|
||||
|
||||
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)
|
||||
@ -397,15 +426,19 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
if (!emc->timings)
|
||||
return -ENOMEM;
|
||||
|
||||
emc->num_timings = child_count;
|
||||
timing = emc->timings;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
if (of_node_name_eq(child, "lpddr2"))
|
||||
continue;
|
||||
|
||||
err = load_one_timing_from_dt(emc, timing++, child);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
|
||||
emc->num_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,
|
||||
}
|
||||
|
||||
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;
|
||||
u32 value, ram_code;
|
||||
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) {
|
||||
dev_info_once(dev, "device-tree doesn't have memory timings\n");
|
||||
return NULL;
|
||||
@ -442,8 +481,49 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
|
||||
np = of_find_node_by_name(np, "emc-tables")) {
|
||||
err = of_property_read_u32(np, "nvidia,ram-code", &value);
|
||||
if (err || value != ram_code) {
|
||||
of_node_put(np);
|
||||
continue;
|
||||
struct device_node *lpddr2_np;
|
||||
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;
|
||||
@ -455,10 +535,72 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
|
||||
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)
|
||||
{
|
||||
u32 emc_cfg, emc_dbg, emc_fbio, emc_adr_cfg;
|
||||
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);
|
||||
|
||||
@ -496,7 +638,36 @@ static int emc_setup_hw(struct tegra_emc *emc)
|
||||
else
|
||||
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;
|
||||
}
|
||||
@ -1049,14 +1220,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
emc->clk_nb.notifier_call = tegra_emc_clk_change_notify;
|
||||
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);
|
||||
if (IS_ERR(emc->regs))
|
||||
return PTR_ERR(emc->regs);
|
||||
@ -1065,6 +1228,14 @@ static int tegra_emc_probe(struct platform_device *pdev)
|
||||
if (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,
|
||||
dev_name(&pdev->dev), emc);
|
||||
if (err) {
|
||||
@ -1117,4 +1288,5 @@ module_platform_driver(tegra_emc_driver);
|
||||
|
||||
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra20 EMC driver");
|
||||
MODULE_SOFTDEP("pre: governor_simpleondemand");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
Loading…
x
Reference in New Issue
Block a user