xref: /illumos-gate/usr/src/uts/intel/io/amdzen/zen_umc.c (revision 7a7820a2)
171815ce7SRobert Mustacchi /*
271815ce7SRobert Mustacchi  * This file and its contents are supplied under the terms of the
371815ce7SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
471815ce7SRobert Mustacchi  * You may only use this file in accordance with the terms of version
571815ce7SRobert Mustacchi  * 1.0 of the CDDL.
671815ce7SRobert Mustacchi  *
771815ce7SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
871815ce7SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
971815ce7SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
1071815ce7SRobert Mustacchi  */
1171815ce7SRobert Mustacchi 
1271815ce7SRobert Mustacchi /*
13e6f89c3aSRobert Mustacchi  * Copyright 2023 Oxide Computer Company
1471815ce7SRobert Mustacchi  */
1571815ce7SRobert Mustacchi 
1671815ce7SRobert Mustacchi /*
1771815ce7SRobert Mustacchi  * AMD Zen Unified Memory Controller Driver
1871815ce7SRobert Mustacchi  *
1971815ce7SRobert Mustacchi  * This file forms the core logic around transforming a physical address that
2071815ce7SRobert Mustacchi  * we're used to using into a specific location on a DIMM. This has support for
2171815ce7SRobert Mustacchi  * a wide range of AMD CPUs and APUs ranging from Zen 1 - Zen 4.
2271815ce7SRobert Mustacchi  *
2371815ce7SRobert Mustacchi  * The goal of this driver is to implement the infrastructure and support
2471815ce7SRobert Mustacchi  * necessary to understand how DRAM requests are being routed in the system and
2571815ce7SRobert Mustacchi  * to be able to map those to particular channels and then DIMMs. This is used
2671815ce7SRobert Mustacchi  * as part of RAS (reliability, availability, and serviceability) to enable
2771815ce7SRobert Mustacchi  * aspects around understanding ECC errors, hardware topology, and more. Like
2871815ce7SRobert Mustacchi  * with any software project, there is more to do here. Please see the Future
2971815ce7SRobert Mustacchi  * Work section at the end of this big theory statement for more information.
3071815ce7SRobert Mustacchi  *
3171815ce7SRobert Mustacchi  * -------------------
3271815ce7SRobert Mustacchi  * Driver Organization
3371815ce7SRobert Mustacchi  * -------------------
3471815ce7SRobert Mustacchi  *
3571815ce7SRobert Mustacchi  * This driver is organized into two major pieces:
3671815ce7SRobert Mustacchi  *
3771815ce7SRobert Mustacchi  *   1. Logic to interface with hardware, discover the data fabric, memory
3871815ce7SRobert Mustacchi  *      controller configuration, and transform that into a normalized fashion
3971815ce7SRobert Mustacchi  *      that can be used across all different Zen family CPUs. This is
4071815ce7SRobert Mustacchi  *      implemented generally in this file, and is designed to assume it is in
4171815ce7SRobert Mustacchi  *      the kernel (as it requires access to the SMN, DF PCI registers, and the
4271815ce7SRobert Mustacchi  *      amdzen nexus driver client services).
4371815ce7SRobert Mustacchi  *
4471815ce7SRobert Mustacchi  *   2. Logic that can take the above normalized memory information and perform
4571815ce7SRobert Mustacchi  *      decoding (e.g. physical address to DIMM information). This generally
4671815ce7SRobert Mustacchi  *      lives in common/mc/zen_uc/zen_umc_decode.c. This file is in common/,
4771815ce7SRobert Mustacchi  *      meaning it is designed to be shared by userland and the kernel. Even
4871815ce7SRobert Mustacchi  *      more so, it is designed to operate on a const version of our primary
4971815ce7SRobert Mustacchi  *      data structure (zen_umc_t), not allowing it to be modified. This allows
5071815ce7SRobert Mustacchi  *      us to more easily unit test the decoding logic and utilize it in other
5171815ce7SRobert Mustacchi  *      circumstances such as with the mcdecode utility.
5271815ce7SRobert Mustacchi  *
5371815ce7SRobert Mustacchi  * There is corresponding traditional dev_ops(9S) and cb_ops(9S) logic in the
5471815ce7SRobert Mustacchi  * driver (currently this file) which take care of interfacing with the broader
5571815ce7SRobert Mustacchi  * operating system environment.
5671815ce7SRobert Mustacchi  *
5771815ce7SRobert Mustacchi  * There is only ever one instance of this driver, e.g. it is a singleton in
5871815ce7SRobert Mustacchi  * design pattern parlance. There is a single struct, the zen_umc_t found in the
5971815ce7SRobert Mustacchi  * global (albeit static) variable zen_umc. This structure itself contains a
6071815ce7SRobert Mustacchi  * hierarchical set of structures that describe the system. To make management
6171815ce7SRobert Mustacchi  * of memory simpler, all of the nested structures that we discover from
6271815ce7SRobert Mustacchi  * hardware are allocated in the same structure. The only exception to this rule
6371815ce7SRobert Mustacchi  * is when we cache serialized nvlists for dumping.
6471815ce7SRobert Mustacchi  *
6571815ce7SRobert Mustacchi  * The organization of the structures inside the zen_umc_t, generally mimics the
6671815ce7SRobert Mustacchi  * hardware organization and is structured as follows:
6771815ce7SRobert Mustacchi  *
6871815ce7SRobert Mustacchi  *   +-----------+
6971815ce7SRobert Mustacchi  *   | zen_umc_t |
7071815ce7SRobert Mustacchi  *   +-----------+
7171815ce7SRobert Mustacchi  *        |
7271815ce7SRobert Mustacchi  *        +-------------------------------+
7371815ce7SRobert Mustacchi  *        v                               v
7471815ce7SRobert Mustacchi  *   +--------------+             +--------------+        One instance of the
7571815ce7SRobert Mustacchi  *   | zen_umc_df_t |     ...     | zen_umc_df_t |        zen_umc_df_t per
7671815ce7SRobert Mustacchi  *   +--------------+             +--------------+        discovered DF.
7771815ce7SRobert Mustacchi  *     |||
7871815ce7SRobert Mustacchi  *     |||
7971815ce7SRobert Mustacchi  *     |||    +----------------+         +----------------+  Global DRAM
8071815ce7SRobert Mustacchi  *     ||+--->| df_dram_rule_t |   ...   | df_dram_rule_t |  rules for the
8171815ce7SRobert Mustacchi  *     ||     +----------------+         +----------------+  platform.
8271815ce7SRobert Mustacchi  *     ||
8371815ce7SRobert Mustacchi  *     ||    +--------------------+       +--------------------+  UMC remap
8471815ce7SRobert Mustacchi  *     |+--->| zen_umc_cs_remap_t |  ...  | zen_umc_cs_remap_t |  rule arrays.
8571815ce7SRobert Mustacchi  *     |     +--------------------+       +--------------------+
8671815ce7SRobert Mustacchi  *     |
8771815ce7SRobert Mustacchi  *     v
8871815ce7SRobert Mustacchi  *    +----------------+         +----------------+   One structure per
8971815ce7SRobert Mustacchi  *    | zen_umc_chan_t |   ...   | zen_umc_chan_t |   discovered DDR4/5
9071815ce7SRobert Mustacchi  *    +----------------+         +----------------+   memory channel.
9171815ce7SRobert Mustacchi  *     ||||
9271815ce7SRobert Mustacchi  *     ||||
9371815ce7SRobert Mustacchi  *     ||||    +----------------+       +----------------+   Channel specific
9471815ce7SRobert Mustacchi  *     |||+--->| df_dram_rule_t |  ...  | df_dram_rule_t |   copy of DRAM rules.
9571815ce7SRobert Mustacchi  *     |||     +----------------+       +----------------+   Less than global.
9671815ce7SRobert Mustacchi  *     |||
9771815ce7SRobert Mustacchi  *     |||     +---------------+       +---------------+   Per-Channel DRAM
9871815ce7SRobert Mustacchi  *     ||+---->| chan_offset_t |  ...  | chan_offset_t |   offset that is used
9971815ce7SRobert Mustacchi  *     ||      +---------------+       +---------------+   for normalization.
10071815ce7SRobert Mustacchi  *     ||
10171815ce7SRobert Mustacchi  *     ||      +-----------------+                         Channel-specific
10271815ce7SRobert Mustacchi  *     |+----->| umc_chan_hash_t |                         hashing rules.
10371815ce7SRobert Mustacchi  *     |       +-----------------+
10471815ce7SRobert Mustacchi  *     |
10571815ce7SRobert Mustacchi  *     |       +------------+         +------------+    One structure for
10671815ce7SRobert Mustacchi  *     +------>| umc_dimm_t |   ...   | umc_dimm_t |    each DIMM in the
10771815ce7SRobert Mustacchi  *             +------------+         +------------+    channel. Always two.
10871815ce7SRobert Mustacchi  *                |
10971815ce7SRobert Mustacchi  *                |     +----------+         +----------+   Per chip-select
11071815ce7SRobert Mustacchi  *                +---> | umc_cs_t |   ...   | umc_cs_t |   data. Always two.
11171815ce7SRobert Mustacchi  *                      +----------+         +----------+
11271815ce7SRobert Mustacchi  *
11371815ce7SRobert Mustacchi  * In the data structures themselves you'll often find several pieces of data
11471815ce7SRobert Mustacchi  * that have the term 'raw' in their name. The point of these is to basically
11571815ce7SRobert Mustacchi  * capture the original value that we read from the register before processing
11671815ce7SRobert Mustacchi  * it. These are generally used either for debugging or to help answer future
11771815ce7SRobert Mustacchi  * curiosity with resorting to the udf and usmn tooling, which hopefully aren't
11871815ce7SRobert Mustacchi  * actually installed on systems.
11971815ce7SRobert Mustacchi  *
12071815ce7SRobert Mustacchi  * With the exception of some of the members in the zen_umc_t that are around
12171815ce7SRobert Mustacchi  * management of state for userland ioctls, everything in the structure is
12271815ce7SRobert Mustacchi  * basically write-once and from that point on should be treated as read-only.
12371815ce7SRobert Mustacchi  *
12471815ce7SRobert Mustacchi  * ---------------
12571815ce7SRobert Mustacchi  * Memory Decoding
12671815ce7SRobert Mustacchi  * ---------------
12771815ce7SRobert Mustacchi  *
12871815ce7SRobert Mustacchi  * To understand the process of memory decoding, it's worth going through and
12971815ce7SRobert Mustacchi  * understanding a bunch of the terminology that is used in this process. As an
13071815ce7SRobert Mustacchi  * additional reference when understanding this, you may want to turn to either
13171815ce7SRobert Mustacchi  * an older generation AMD BIOS and Kernel Developer's Guide or the more current
13271815ce7SRobert Mustacchi  * Processor Programming Reference. In addition, the imc driver, which is the
13371815ce7SRobert Mustacchi  * Intel equivalent, also provides an additional bit of reference.
13471815ce7SRobert Mustacchi  *
13571815ce7SRobert Mustacchi  * SYSTEM ADDRESS
13671815ce7SRobert Mustacchi  *
13771815ce7SRobert Mustacchi  *	This is a physical address and is the way that the operating system
13871815ce7SRobert Mustacchi  *	normally thinks of memory. System addresses can refer to many different
13971815ce7SRobert Mustacchi  *	things. For example, you have traditional DRAM, memory-mapped PCIe
14071815ce7SRobert Mustacchi  *	devices, peripherals that the processor exposes such as the xAPIC, data
14171815ce7SRobert Mustacchi  *	from the FCH (Fusion Controller Hub), etc.
14271815ce7SRobert Mustacchi  *
14371815ce7SRobert Mustacchi  * TOM, TOM2, and the DRAM HOLE
14471815ce7SRobert Mustacchi  *
14571815ce7SRobert Mustacchi  *	Physical memory has a complicated layout on x86 in part because of
14671815ce7SRobert Mustacchi  *	support for traditional 16-bit and 32-bit systems. As a result, contrary
14771815ce7SRobert Mustacchi  *	to popular belief, DRAM is not at a consistent address range in the
14871815ce7SRobert Mustacchi  *	processor. AMD processors have a few different ranges. There is a 32-bit
14971815ce7SRobert Mustacchi  *	region that starts at effectively physical address zero and goes to the
15071815ce7SRobert Mustacchi  *	TOM MSR (top of memory -- Core::X86::Msr::TOP_MEM). This indicates a
15171815ce7SRobert Mustacchi  *	limit below 4 GiB, generally around 2 GiB.
15271815ce7SRobert Mustacchi  *
15371815ce7SRobert Mustacchi  *	From there, the next region of DRAM starts at 4 GiB and goes to TOM2
15471815ce7SRobert Mustacchi  *	(top of memory 2 -- Core::X86::Msr::TOM2). The region between TOM and
15571815ce7SRobert Mustacchi  *	4 GiB is called the DRAM hole. Physical addresses in this region are
15671815ce7SRobert Mustacchi  *	used for memory mapped I/O. This breaks up contiguous physical
15771815ce7SRobert Mustacchi  *	addresses being used for DRAM, creating a "hole".
15871815ce7SRobert Mustacchi  *
15971815ce7SRobert Mustacchi  * DATA FABRIC
16071815ce7SRobert Mustacchi  *
16171815ce7SRobert Mustacchi  *	The data fabric (DF) is the primary interface that different parts of
16271815ce7SRobert Mustacchi  *	the system use to communicate with one another. This includes the I/O
16371815ce7SRobert Mustacchi  *	engines (where PCIe traffic goes), CPU caches and their cores, memory
16471815ce7SRobert Mustacchi  *	channels, cross-socket communication, and a whole lot more. The first
16571815ce7SRobert Mustacchi  *	part of decoding addresses and figuring out which DRAM channel an
16671815ce7SRobert Mustacchi  *	address should be directed to all come from the data fabric.
16771815ce7SRobert Mustacchi  *
16871815ce7SRobert Mustacchi  *	The data fabric is comprised of instances. So there is one instance for
16971815ce7SRobert Mustacchi  *	each group of cores, each memory channel, etc. Each instance has its own
17071815ce7SRobert Mustacchi  *	independent set of register information. As the data fabric is a series
17171815ce7SRobert Mustacchi  *	of devices exposed over PCI, if you do a normal PCI configuration space
17271815ce7SRobert Mustacchi  *	read or write that'll end up broadcasting the I/O. Instead, to access a
17371815ce7SRobert Mustacchi  *	particular instance's register information there is an indirect access
17471815ce7SRobert Mustacchi  *	mechanism. The primary way that this driver accesses data fabric
17571815ce7SRobert Mustacchi  *	registers is via these indirect reads.
17671815ce7SRobert Mustacchi  *
17771815ce7SRobert Mustacchi  *	There is one instance of the Data Fabric per socket starting with Zen 2.
17871815ce7SRobert Mustacchi  *	In Zen 1, there was one instance of the data fabric per CCD -- core
17971815ce7SRobert Mustacchi  *	complex die (see cpuid.c's big theory statement for more information).
18071815ce7SRobert Mustacchi  *
18171815ce7SRobert Mustacchi  * DF INSTANCE ID
18271815ce7SRobert Mustacchi  *
18371815ce7SRobert Mustacchi  *	A DF instance ID is an identifier for a single entity or component in a
18471815ce7SRobert Mustacchi  *	data fabric.  The set of instance IDs is unique only with a single data
18571815ce7SRobert Mustacchi  *	fabric. So for example, each memory channel, I/O endpoint (e.g. PCIe
18671815ce7SRobert Mustacchi  *	logic), group of cores, has its own instance ID. Anything within the
18771815ce7SRobert Mustacchi  *	same data fabric (e.g. the same die) can be reached via its instance ID.
18871815ce7SRobert Mustacchi  *	The instance ID is used to indicate which instance to contact when
18971815ce7SRobert Mustacchi  *	performing indirect accesses.
19071815ce7SRobert Mustacchi  *
19171815ce7SRobert Mustacchi  *	Not everything that has an instance ID will be globally routable (e.g.
19271815ce7SRobert Mustacchi  *	between multiple sockets). For things that are, such as the memory
19371815ce7SRobert Mustacchi  *	channels and coherent core initiators, there is a second ID called a
19471815ce7SRobert Mustacchi  *	fabric ID.
19571815ce7SRobert Mustacchi  *
19671815ce7SRobert Mustacchi  * DF FABRIC ID
19771815ce7SRobert Mustacchi  *
19871815ce7SRobert Mustacchi  *	A DF fabric ID is an identifier that combines information to indicate
19971815ce7SRobert Mustacchi  *	both which instance of the data fabric a component is on and a component
20071815ce7SRobert Mustacchi  *	itself. So with this number you can distinguish between a memory channel
20171815ce7SRobert Mustacchi  *	on one of two sockets. A Fabric ID is made up of two parts. The upper
20271815ce7SRobert Mustacchi  *	part indicates which DF we are talking to and is referred to as a Node
20371815ce7SRobert Mustacchi  *	ID. The Node ID is itself broken into two parts: one that identifies a
20471815ce7SRobert Mustacchi  *	socket, and one that identifies a die. The lower part of a fabric ID is
20571815ce7SRobert Mustacchi  *	called a component ID and indicates which component in a particular data
20671815ce7SRobert Mustacchi  *	fabric that we are talking to. While only a subset of the total
20771815ce7SRobert Mustacchi  *	components in the data fabric are routable, for everything that is, its
20871815ce7SRobert Mustacchi  *	component ID matches its instance ID.
20971815ce7SRobert Mustacchi  *
21071815ce7SRobert Mustacchi  *	Put differently, the component portion of a fabric ID and a component's
21171815ce7SRobert Mustacchi  *	instance ID are always the same for routable entities. For things which
21271815ce7SRobert Mustacchi  *	cannot be routed, they only have an instance ID and no fabric ID.
21371815ce7SRobert Mustacchi  *	Because this code is always interacting with data fabric components that
21471815ce7SRobert Mustacchi  *	are routable, sometimes instance ID and the component ID portion of the
21571815ce7SRobert Mustacchi  *	data fabric ID may be used interchangeably.
21671815ce7SRobert Mustacchi  *
21771815ce7SRobert Mustacchi  *	Finally, it's worth calling out that the number of bits that are used to
21871815ce7SRobert Mustacchi  *	indicate the socket, die, and component in a fabric ID changes from
21971815ce7SRobert Mustacchi  *	hardware generation to hardware generation.
22071815ce7SRobert Mustacchi  *
22171815ce7SRobert Mustacchi  *	Inside the code here, the socket and die decomposition information is
22271815ce7SRobert Mustacchi  *	always relative to the node ID. AMD phrases the decomposition
22371815ce7SRobert Mustacchi  *	information in terms of a series of masks and shifts. This is
22471815ce7SRobert Mustacchi  *	information that can be retrieved from the data fabric itself, allowing
22571815ce7SRobert Mustacchi  *	us to avoid hardcoding too much information other than which registers
22671815ce7SRobert Mustacchi  *	actually have which fields. With both masks and shifts, it's important
22771815ce7SRobert Mustacchi  *	to establish which comes first. We follow AMD's convention and always
22871815ce7SRobert Mustacchi  *	apply masks before shifts. With that, let's look at an example of a
22971815ce7SRobert Mustacchi  *	made up bit set:
23071815ce7SRobert Mustacchi  *
23171815ce7SRobert Mustacchi  *	Assumptions (to make this example simple):
23271815ce7SRobert Mustacchi  *	  o The fabric ID is 16 bits
23371815ce7SRobert Mustacchi  *	  o The component ID is 8 bits
23471815ce7SRobert Mustacchi  *	  o The node ID is 8 bits
23571815ce7SRobert Mustacchi  *	  o The socket and die ID are both 4 bits
23671815ce7SRobert Mustacchi  *
23771815ce7SRobert Mustacchi  *	Here, let's say that we have the ID 0x2106. This decomposes into a
23871815ce7SRobert Mustacchi  *	socket 0x2, die 0x1, and component 0x6. Here is how that works in more
23971815ce7SRobert Mustacchi  *	detail:
24071815ce7SRobert Mustacchi  *
24171815ce7SRobert Mustacchi  *	          0x21      0x06
24271815ce7SRobert Mustacchi  *	        |------|  |------|
24371815ce7SRobert Mustacchi  *	        Node ID   Component ID
24471815ce7SRobert Mustacchi  *	Mask:    0xff00    0x00ff
24571815ce7SRobert Mustacchi  *	Shift:   8         0
24671815ce7SRobert Mustacchi  *
24771815ce7SRobert Mustacchi  *	Next we would decompose the Node ID as:
24871815ce7SRobert Mustacchi  *	         0x2        0x1
24971815ce7SRobert Mustacchi  *	       |------|  |------|
25071815ce7SRobert Mustacchi  *	       Sock ID    Die ID
25171815ce7SRobert Mustacchi  *	Mask:   0xf0      0x0f
25271815ce7SRobert Mustacchi  *	Shift:  4         0
25371815ce7SRobert Mustacchi  *
25471815ce7SRobert Mustacchi  *	Composing a fabric ID from its parts would work in a similar way by
25571815ce7SRobert Mustacchi  *	applying masks and shifts.
25671815ce7SRobert Mustacchi  *
25771815ce7SRobert Mustacchi  * NORMAL ADDRESS
25871815ce7SRobert Mustacchi  *
25971815ce7SRobert Mustacchi  *	A normal address is one of the primary address types that AMD uses in
26071815ce7SRobert Mustacchi  *	memory decoding. It takes into account the DRAM hole, interleave
26171815ce7SRobert Mustacchi  *	settings, and is basically the address that is dispatched to the broader
26271815ce7SRobert Mustacchi  *	data fabric towards a particular DRAM channel.
26371815ce7SRobert Mustacchi  *
26471815ce7SRobert Mustacchi  *	Often, phrases like 'normalizing the address' or normalization refer to
26571815ce7SRobert Mustacchi  *	the process of transforming a system address into the channel address.
26671815ce7SRobert Mustacchi  *
26771815ce7SRobert Mustacchi  * INTERLEAVING
26871815ce7SRobert Mustacchi  *
26971815ce7SRobert Mustacchi  *	The idea of interleaving is to take a contiguous range and weave it
27071815ce7SRobert Mustacchi  *	between multiple different actual entities. Generally certain bits in
27171815ce7SRobert Mustacchi  *	the range are used to select one of several smaller regions. For
27271815ce7SRobert Mustacchi  *	example, if you have 8 regions each that are 4 GiB in size, that creates
27371815ce7SRobert Mustacchi  *	a single 32 GiB region. You can use three bits in that 32 GiB space to
27471815ce7SRobert Mustacchi  *	select one of the 8 regions. For a more visual example, see the
27571815ce7SRobert Mustacchi  *	definition of this in uts/intel/io/imc/imc.c.
27671815ce7SRobert Mustacchi  *
27771815ce7SRobert Mustacchi  * CHANNEL
27871815ce7SRobert Mustacchi  *
27971815ce7SRobert Mustacchi  *	A channel is used to refer to a single memory channel. This is sometimes
28071815ce7SRobert Mustacchi  *	called a DRAM channel as well. A channel operates in a specific mode
28171815ce7SRobert Mustacchi  *	based on the JEDEC DRAM standards (e.g. DDR4, LPDDR5, etc.). A
28271815ce7SRobert Mustacchi  *	(LP)DDR4/5 channel may support up to two DIMMs inside the channel. The
28371815ce7SRobert Mustacchi  *	number of slots is platform dependent and from there the number of DIMMs
28471815ce7SRobert Mustacchi  *	installed can vary. Generally speaking, a DRAM channel defines a set
28571815ce7SRobert Mustacchi  *	number of signals, most of which go to all DIMMs in the channel, what
28671815ce7SRobert Mustacchi  *	varies is which "chip-select" is activated which causes a given DIMM to
28771815ce7SRobert Mustacchi  *	pay attention or not.
28871815ce7SRobert Mustacchi  *
28971815ce7SRobert Mustacchi  * DIMM
29071815ce7SRobert Mustacchi  *
29171815ce7SRobert Mustacchi  *	A DIMM refers to a physical hardware component that is installed into a
29271815ce7SRobert Mustacchi  *	computer to provide access to dynamic memory. Originally this stood for
29371815ce7SRobert Mustacchi  *	dual-inline memory module, though the DIMM itself has evolved beyond
29471815ce7SRobert Mustacchi  *	that. A DIMM is organized into various pages, which are addressed by
29571815ce7SRobert Mustacchi  *	a combination of rows, columns, banks, bank groups, and ranks. How this
29671815ce7SRobert Mustacchi  *	fits together changes from generation to generation and is standardized
29771815ce7SRobert Mustacchi  *	in something like DDR4, LPDDR4, DDR5, LPDDR5, etc. These standards
29871815ce7SRobert Mustacchi  *	define the general individual modules that are assembled into a DIMM.
29971815ce7SRobert Mustacchi  *	There are slightly different standards for combined memory modules
30071815ce7SRobert Mustacchi  *	(which is what we use the term DIMM for). Examples of those include
30171815ce7SRobert Mustacchi  *	things like registered DIMMs (RDIMMs).
30271815ce7SRobert Mustacchi  *
30371815ce7SRobert Mustacchi  *	A DDR4 DIMM contains a single channel that is 64-bits wide with 8 check
30471815ce7SRobert Mustacchi  *	bits. A DDR5 DIMM has a notable change in this scheme from earlier DDR
30571815ce7SRobert Mustacchi  *	standards. It breaks a single DDR5 DIMM into two sub-channels. Each
30671815ce7SRobert Mustacchi  *	sub-channel is independently addressed and contains 32-bits of data and
30771815ce7SRobert Mustacchi  *	8-bits of check data.
30871815ce7SRobert Mustacchi  *
30971815ce7SRobert Mustacchi  * ROW AND COLUMN
31071815ce7SRobert Mustacchi  *
31171815ce7SRobert Mustacchi  *	The most basic building block of a DIMM is a die. A DIMM consists of
31271815ce7SRobert Mustacchi  *	multiple dies that are organized together (we'll discuss the
31371815ce7SRobert Mustacchi  *	organization next). A given die is organized into a series of rows and
31471815ce7SRobert Mustacchi  *	columns. First, one selects a row. At which point one is able to select
31571815ce7SRobert Mustacchi  *	a specific column. It is more expensive to change rows than columns,
31671815ce7SRobert Mustacchi  *	leading a given row to contain approximately 1 KiB of data spread across
31771815ce7SRobert Mustacchi  *	its columns. The exact size depends on the device. Each row/column is a
31871815ce7SRobert Mustacchi  *	series of capacitors and transistors. The transistor is used to select
31971815ce7SRobert Mustacchi  *	data from the capacitor and the capacitor actually contains the logical
32071815ce7SRobert Mustacchi  *	0/1 value.
32171815ce7SRobert Mustacchi  *
32271815ce7SRobert Mustacchi  * BANKS AND BANK GROUPS
32371815ce7SRobert Mustacchi  *
32471815ce7SRobert Mustacchi  *	An individual DRAM die is organized in something called a bank. A DIMM
32571815ce7SRobert Mustacchi  *	has a number of banks that sit in series. These are then grouped into
32671815ce7SRobert Mustacchi  *	larger bank groups. Generally speaking, each bank group has the same
32771815ce7SRobert Mustacchi  *	number of banks. Let's take a look at an example of a system with 4
32871815ce7SRobert Mustacchi  *	bank groups, each with 4 banks.
32971815ce7SRobert Mustacchi  *
33071815ce7SRobert Mustacchi  *         +-----------------------+           +-----------------------+
33171815ce7SRobert Mustacchi  *         | Bank Group 0          |           | Bank Group 1          |
33271815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
33371815ce7SRobert Mustacchi  *         | | Bank 0 | | Bank 1 | |           | | Bank 0 | | Bank 1 | |
33471815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
33571815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
33671815ce7SRobert Mustacchi  *         | | Bank 2 | | Bank 3 | |           | | Bank 2 | | Bank 3 | |
33771815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
33871815ce7SRobert Mustacchi  *         +-----------------------+           +-----------------------+
33971815ce7SRobert Mustacchi  *
34071815ce7SRobert Mustacchi  *         +-----------------------+           +-----------------------+
34171815ce7SRobert Mustacchi  *         | Bank Group 2          |           | Bank Group 3          |
34271815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
34371815ce7SRobert Mustacchi  *         | | Bank 0 | | Bank 1 | |           | | Bank 0 | | Bank 1 | |
34471815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
34571815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
34671815ce7SRobert Mustacchi  *         | | Bank 2 | | Bank 3 | |           | | Bank 2 | | Bank 3 | |
34771815ce7SRobert Mustacchi  *         | +--------+ +--------+ |           | +--------+ +--------+ |
34871815ce7SRobert Mustacchi  *         +-----------------------+           +-----------------------+
34971815ce7SRobert Mustacchi  *
35071815ce7SRobert Mustacchi  *	On a DIMM, only a single bank and bank group can be active at a time for
35171815ce7SRobert Mustacchi  *	reading or writing an 8 byte chunk of data. However, these are still
35271815ce7SRobert Mustacchi  *	pretty important and useful because of the time involved to switch
35371815ce7SRobert Mustacchi  *	between them. It is much cheaper to switch between bank groups than
35471815ce7SRobert Mustacchi  *	between banks and that time can be cheaper than activating a new row.
35571815ce7SRobert Mustacchi  *	This allows memory controllers to pipeline this substantially.
35671815ce7SRobert Mustacchi  *
35771815ce7SRobert Mustacchi  * RANK AND CHIP-SELECT
35871815ce7SRobert Mustacchi  *
35971815ce7SRobert Mustacchi  *	The next level of organization is a rank. A rank is effectively an
36071815ce7SRobert Mustacchi  *	independent copy of all the bank and bank groups on a DIMM. That is,
36171815ce7SRobert Mustacchi  *	there are additional copies of the DIMM's organization, but not the data
36271815ce7SRobert Mustacchi  *	itself. Originally a
36371815ce7SRobert Mustacchi  *	single or dual rank DIMM was built such that one copy of everything was
36471815ce7SRobert Mustacchi  *	on each physical side of the DIMM. As the number of ranks has increased
36571815ce7SRobert Mustacchi  *	this has changed as well. Generally speaking, the contents of the rank
36671815ce7SRobert Mustacchi  *	are equivalent. That is, you have the same number of bank groups, banks,
36771815ce7SRobert Mustacchi  *	and each bank has the same number of rows and columns.
36871815ce7SRobert Mustacchi  *
36971815ce7SRobert Mustacchi  *	Ranks are selected by what's called a chip-select, often abbreviated as
37071815ce7SRobert Mustacchi  *	CS_L in the various DRAM standards. AMD also often abbreviates this as a
37171815ce7SRobert Mustacchi  *	CS (which is not to be confused with the DF class of device called a
37271815ce7SRobert Mustacchi  *	CS). These signals are used to select a rank to activate on a DIMM.
37371815ce7SRobert Mustacchi  *	There are some number of these for each DIMM which is how the memory
37471815ce7SRobert Mustacchi  *	controller chooses which of the DIMMs it's actually going to activate in
37571815ce7SRobert Mustacchi  *	the system.
37671815ce7SRobert Mustacchi  *
37771815ce7SRobert Mustacchi  *	One interesting gotcha here is how AMD organizes things. Each DIMM
37871815ce7SRobert Mustacchi  *	logically is broken into two chip-selects in hardware. Between DIMMs
37971815ce7SRobert Mustacchi  *	with more than 2 ranks and 3D stacked RDIMMs, there are ways to
38071815ce7SRobert Mustacchi  *	potentially activate more bits. Ultimately these are mapped to a series
38171815ce7SRobert Mustacchi  *	of rank multiplication logic internally. These ultimately then control
38271815ce7SRobert Mustacchi  *	some of these extra pins, though the exact method isn't 100% clear at
38371815ce7SRobert Mustacchi  *	this time.
38471815ce7SRobert Mustacchi  *
38571815ce7SRobert Mustacchi  * -----------------------
38671815ce7SRobert Mustacchi  * Rough Hardware Process
38771815ce7SRobert Mustacchi  * -----------------------
38871815ce7SRobert Mustacchi  *
38971815ce7SRobert Mustacchi  * To better understand how everything is implemented and structured, it's worth
39071815ce7SRobert Mustacchi  * briefly describing what happens when hardware wants to read a given physical
39171815ce7SRobert Mustacchi  * address. This is roughly summarized in the following chart. In the left hand
39271815ce7SRobert Mustacchi  * side is the type of address, which is transformed and generally shrinks along
39371815ce7SRobert Mustacchi  * the way. Next to it is the actor that is taking action and the type of
39471815ce7SRobert Mustacchi  * address that it starts with.
39571815ce7SRobert Mustacchi  *
39671815ce7SRobert Mustacchi  * +---------+   +------+
39771815ce7SRobert Mustacchi  * | Virtual |   | CPU  |
39871815ce7SRobert Mustacchi  * | Address |   | Core |
39971815ce7SRobert Mustacchi  * +---------+   +------+
40071815ce7SRobert Mustacchi  *      |           |          The CPU core receives a memory request and then
40171815ce7SRobert Mustacchi  *      |           * . . . .  determines whether this request is DRAM or MMIO
40271815ce7SRobert Mustacchi  *      |           |          (memory-mapped I/O) and then sends it to the data
40371815ce7SRobert Mustacchi  *      v           v          fabric.
40471815ce7SRobert Mustacchi  * +----------+ +--------+
40571815ce7SRobert Mustacchi  * | Physical | | Data   |
40671815ce7SRobert Mustacchi  * | Address  | | Fabric |
40771815ce7SRobert Mustacchi  * +----------+ +--------+
40871815ce7SRobert Mustacchi  *      |           |          The data fabric instance in the CCX/D uses the
40971815ce7SRobert Mustacchi  *      |           * . . . .  programmed DRAM rules to determine what DRAM
41071815ce7SRobert Mustacchi  *      |           |          channel to direct a request to and what the
41171815ce7SRobert Mustacchi  *      |           |          channel-relative address is. It then sends the
41271815ce7SRobert Mustacchi  *      |           |          request through the fabric. Note, the number of
41371815ce7SRobert Mustacchi  *      |           |          DRAM rules varies based on the processor SoC.
41471815ce7SRobert Mustacchi  *      |           |          Server parts like Milan have many more rules than
41571815ce7SRobert Mustacchi  *      |           |          an APU like Cezanne. The DRAM rules tell us both
41671815ce7SRobert Mustacchi  *      v           v          how to find and normalize the physical address.
41771815ce7SRobert Mustacchi  * +---------+  +---------+
41871815ce7SRobert Mustacchi  * | Channel |  | DRAM    |
41971815ce7SRobert Mustacchi  * | Address |  | Channel |
42071815ce7SRobert Mustacchi  * +---------+  +---------+
42171815ce7SRobert Mustacchi  *      |           |          The UMC (unified memory controller) receives the
42271815ce7SRobert Mustacchi  *      |           * . . . .  DRAM request and determines which DIMM to send
42371815ce7SRobert Mustacchi  *      |           |          the request to along with the rank, banks, row,
42471815ce7SRobert Mustacchi  *      |           |          column, etc. It initiates a DRAM transaction and
42571815ce7SRobert Mustacchi  *      |           |          then sends the results back through the data
42671815ce7SRobert Mustacchi  *      v           v          fabric to the CPU core.
42771815ce7SRobert Mustacchi  * +---------+  +--------+
42871815ce7SRobert Mustacchi  * | DIMM    |  | Target |
42971815ce7SRobert Mustacchi  * | Address |  | DIMM   |
43071815ce7SRobert Mustacchi  * +---------+  +--------+
43171815ce7SRobert Mustacchi  *
43271815ce7SRobert Mustacchi  * The above is all generally done in hardware. There are multiple steps
43371815ce7SRobert Mustacchi  * internal to this that we end up mimicking in software. This includes things
43471815ce7SRobert Mustacchi  * like, applying hashing logic, address transformations, and related.
43571815ce7SRobert Mustacchi  * Thankfully the hardware is fairly generic and programmed with enough
43671815ce7SRobert Mustacchi  * information that we can pull out to figure this out. The rest of this theory
43771815ce7SRobert Mustacchi  * statement covers the major parts of this: interleaving, the act of
43871815ce7SRobert Mustacchi  * determining which memory channel to actually go to, and normalization, the
43971815ce7SRobert Mustacchi  * act of removing some portion of the physical address bits to determine the
44071815ce7SRobert Mustacchi  * address relative to a channel.
44171815ce7SRobert Mustacchi  *
44271815ce7SRobert Mustacchi  * ------------------------
44371815ce7SRobert Mustacchi  * Data Fabric Interleaving
44471815ce7SRobert Mustacchi  * ------------------------
44571815ce7SRobert Mustacchi  *
44671815ce7SRobert Mustacchi  * One of the major parts of address decoding is to understand how the
44771815ce7SRobert Mustacchi  * interleaving features work in the data fabric. This is used to allow an
44871815ce7SRobert Mustacchi  * address range to be spread out between multiple memory channels and then,
44971815ce7SRobert Mustacchi  * later on, when normalizing the address. As mentioned above, a system address
45071815ce7SRobert Mustacchi  * matches a rule which has information on interleaving. Interleaving comes in
45171815ce7SRobert Mustacchi  * many different flavors. It can be used to just switch between channels,
45271815ce7SRobert Mustacchi  * sockets, and dies. It can also end up involving some straightforward and some
45371815ce7SRobert Mustacchi  * fairly complex hashing operations.
45471815ce7SRobert Mustacchi  *
45571815ce7SRobert Mustacchi  * Each DRAM rule has instructions on how to perform this interleaving. The way
45671815ce7SRobert Mustacchi  * this works is that the rule first says to start at a given address bit,
45771815ce7SRobert Mustacchi  * generally ranging from bit 8-12. These influence the granularity of the
45871815ce7SRobert Mustacchi  * interleaving going on. From there, the rules determine how many bits to use
45971815ce7SRobert Mustacchi  * from the address to determine the die, socket, and channel. In the simplest
46071815ce7SRobert Mustacchi  * form, these perform a log2 of the actual number of things you're interleaving
46171815ce7SRobert Mustacchi  * across (we'll come back to non-powers of two). So let's work a few common
46271815ce7SRobert Mustacchi  * examples:
46371815ce7SRobert Mustacchi  *
46471815ce7SRobert Mustacchi  *   o 8-channel interleave, 1-die interleave, 2-socket interleave
46571815ce7SRobert Mustacchi  *     Start at bit 9
46671815ce7SRobert Mustacchi  *
46771815ce7SRobert Mustacchi  *	In this case we have 3 bits that determine the channel to use, 0 bits
46871815ce7SRobert Mustacchi  *	for the die, 1 bit for the socket. Here we would then use the following
46971815ce7SRobert Mustacchi  *	bits to determine what the channel, die, and socket IDs are:
47071815ce7SRobert Mustacchi  *
47171815ce7SRobert Mustacchi  *	[12]    - Socket ID
47271815ce7SRobert Mustacchi  *	[11:9]  - Channel ID
47371815ce7SRobert Mustacchi  *
47471815ce7SRobert Mustacchi  *	You'll note that there was no die-interleave, which means the die ID is
47571815ce7SRobert Mustacchi  *	always zero. This is the general thing you expect to see in Zen 2 and 3
47671815ce7SRobert Mustacchi  *	based systems as they only have one die or a Zen 1 APU.
47771815ce7SRobert Mustacchi  *
47871815ce7SRobert Mustacchi  *   o 2-channel interleave, 4-die interleave, 2-socket interleave
47971815ce7SRobert Mustacchi  *     Start at bit 10
48071815ce7SRobert Mustacchi  *
48171815ce7SRobert Mustacchi  *	In this case we have 1 bit for the channel and socket interleave. We
48271815ce7SRobert Mustacchi  *	have 2 bits for the die. This is something you might see on a Zen 1
48371815ce7SRobert Mustacchi  *	system. This results in the following bits:
48471815ce7SRobert Mustacchi  *
48571815ce7SRobert Mustacchi  *      [13]    - Socket ID
48671815ce7SRobert Mustacchi  *      [12:11] - Die ID
48771815ce7SRobert Mustacchi  *      [10]    - Channel ID
48871815ce7SRobert Mustacchi  *
48971815ce7SRobert Mustacchi  *
49071815ce7SRobert Mustacchi  * COD and NPS HASHING
49171815ce7SRobert Mustacchi  *
49271815ce7SRobert Mustacchi  * However, this isn't the only primary extraction rule of the above values. The
49371815ce7SRobert Mustacchi  * other primary method is using a hash. While the exact hash methods vary
49471815ce7SRobert Mustacchi  * between Zen 2/3 and Zen 4 based systems, they follow a general scheme. In the
49571815ce7SRobert Mustacchi  * system there are three interleaving configurations that are either global or
49671815ce7SRobert Mustacchi  * enabled on a per-rule basis. These indicate whether one should perform the
49771815ce7SRobert Mustacchi  * XOR computation using addresses at:
49871815ce7SRobert Mustacchi  *
49971815ce7SRobert Mustacchi  *   o 64 KiB (starting at bit 16)
50071815ce7SRobert Mustacchi  *   o 2 MiB (starting at bit 21)
50171815ce7SRobert Mustacchi  *   o 1 GiB (starting at bit 30)
50271815ce7SRobert Mustacchi  *
50371815ce7SRobert Mustacchi  * In this world, you take the starting address bit defined by the rule and XOR
50471815ce7SRobert Mustacchi  * it with each enabled interleave address. If you have more than one bit to
50571815ce7SRobert Mustacchi  * select (e.g. because you are hashing across more than 2 channels), then you
50671815ce7SRobert Mustacchi  * continue taking subsequent bits from each enabled region. So the second bit
50771815ce7SRobert Mustacchi  * would use 17, 21, and 31 if all three ranges were enabled while the third bit
50871815ce7SRobert Mustacchi  * would use 18, 22, and 32. While these are straightforward, there is a catch.
50971815ce7SRobert Mustacchi  *
51071815ce7SRobert Mustacchi  * While the DRAM rule contains what the starting address bit, you don't
51171815ce7SRobert Mustacchi  * actually use subsequent bits in the same way. Instead subsequent bits are
51271815ce7SRobert Mustacchi  * deterministic and use bits 12 and 13 from the address.  This is not the same
51371815ce7SRobert Mustacchi  * consecutive thing that one might expect. Let's look at a Rome/Milan based
51471815ce7SRobert Mustacchi  * example:
51571815ce7SRobert Mustacchi  *
51671815ce7SRobert Mustacchi  *   o 8-channel "COD" hashing, starting at address 9. All three ranges enabled.
51771815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
51871815ce7SRobert Mustacchi  *
51971815ce7SRobert Mustacchi  *      In this model we are using 3 bits for the channel, 0 bits for the socket
52071815ce7SRobert Mustacchi  *      and die.
52171815ce7SRobert Mustacchi  *
52271815ce7SRobert Mustacchi  *	Channel ID[0] = addr[9]  ^ addr[16] ^ addr[21] ^ addr[30]
52371815ce7SRobert Mustacchi  *	Channel ID[1] = addr[12] ^ addr[17] ^ addr[22] ^ addr[31]
52471815ce7SRobert Mustacchi  *	Channel ID[2] = addr[13] ^ addr[18] ^ addr[23] ^ addr[32]
52571815ce7SRobert Mustacchi  *
52671815ce7SRobert Mustacchi  *	So through this scheme we'd have a socket/die of 0, and then the channel
52771815ce7SRobert Mustacchi  *	ID is computed based on that. The number of bits that we use here
52871815ce7SRobert Mustacchi  *	depends on how many channels the hash is going across.
52971815ce7SRobert Mustacchi  *
53071815ce7SRobert Mustacchi  * The Genoa and related variants, termed "NPS", has a few wrinkles. First,
53171815ce7SRobert Mustacchi  * rather than 3 bits being used for the channel, up to 4 bits are. Second,
53271815ce7SRobert Mustacchi  * while the Rome/Milan "COD" hash above does not support socket or die
53371815ce7SRobert Mustacchi  * interleaving, the "NPS" hash actually supports socket interleaving. However,
53471815ce7SRobert Mustacchi  * unlike the straightforward non-hashing scheme, the first bit is used to
53571815ce7SRobert Mustacchi  * determine the socket when enabled as opposed to the last one. In addition, if
53671815ce7SRobert Mustacchi  * we're not performing socket interleaving, then we end up throwing address bit
53771815ce7SRobert Mustacchi  * 14 into the mix here. Let's look at examples:
53871815ce7SRobert Mustacchi  *
53971815ce7SRobert Mustacchi  *   o 4-channel "NPS" hashing, starting at address 8. All three ranges enabled.
54071815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
54171815ce7SRobert Mustacchi  *
54271815ce7SRobert Mustacchi  *      In this model we are using 2 bits for the channel, 0 bits for the socket
54371815ce7SRobert Mustacchi  *      and die. Because socket interleaving is not being used, bit 14 ends up
54471815ce7SRobert Mustacchi  *      being added into the first bit of the channel selection. Presumably this
54571815ce7SRobert Mustacchi  *      is to improve the address distribution in some form.
54671815ce7SRobert Mustacchi  *
54771815ce7SRobert Mustacchi  *      Channel ID[0] = addr[8] ^ addr[16] ^ addr[21] ^ addr[30] ^ addr[14]
54871815ce7SRobert Mustacchi  *      Channel ID[1] = addr[12] ^ addr[17] ^ addr[22] ^ addr[31]
54971815ce7SRobert Mustacchi  *
55071815ce7SRobert Mustacchi  *   o 8-channel "NPS" hashing, starting at address 9. All three ranges enabled.
55171815ce7SRobert Mustacchi  *     1-die and 2-socket interleaving.
55271815ce7SRobert Mustacchi  *
55371815ce7SRobert Mustacchi  *      In this model we are using 3 bits for the channel and 1 for the socket.
55471815ce7SRobert Mustacchi  *      The die is always set to 0. Unlike the above, address bit 14 is not used
55571815ce7SRobert Mustacchi  *      because it ends up being required for the 4th address bit.
55671815ce7SRobert Mustacchi  *
55771815ce7SRobert Mustacchi  *	Socket ID[0]  = addr[9]  ^ addr[16] ^ addr[21] ^ addr[30]
55871815ce7SRobert Mustacchi  *	Channel ID[0] = addr[12] ^ addr[17] ^ addr[22] ^ addr[31]
55971815ce7SRobert Mustacchi  *	Channel ID[1] = addr[13] ^ addr[18] ^ addr[23] ^ addr[32]
56071815ce7SRobert Mustacchi  *	Channel ID[2] = addr[14] ^ addr[19] ^ addr[24] ^ addr[33]
56171815ce7SRobert Mustacchi  *
56271815ce7SRobert Mustacchi  *
56371815ce7SRobert Mustacchi  * ZEN 3 6-CHANNEL
56471815ce7SRobert Mustacchi  *
56571815ce7SRobert Mustacchi  * These were the simple cases. Things get more complex when we move to
56671815ce7SRobert Mustacchi  * non-power of 2 based hashes between channels. There are two different sets of
56771815ce7SRobert Mustacchi  * these schemes. The first of these is 6-channel hashing that was added in Zen
56871815ce7SRobert Mustacchi  * 3. The second of these is a more complex and general form that was added in
56971815ce7SRobert Mustacchi  * Zen 4. Let's start with the Zen 3 case. The Zen 3 6-channel hash requires
57071815ce7SRobert Mustacchi  * starting at address bits 11 or 12 and varies its logic somewhat from there.
57171815ce7SRobert Mustacchi  * In the 6-channel world, the socket and die interleaving must be disabled.
57271815ce7SRobert Mustacchi  * Let's walk through an example:
57371815ce7SRobert Mustacchi  *
57471815ce7SRobert Mustacchi  *   o 6-channel Zen 3, starting at address 11. 2M and 1G range enabled.
57571815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
57671815ce7SRobert Mustacchi  *
57771815ce7SRobert Mustacchi  *      Regardless of the starting address, we will always use three bits to
57871815ce7SRobert Mustacchi  *      determine a channel address. However, it's worth calling out that the
57971815ce7SRobert Mustacchi  *      64K range is not considered for this at all. Another oddity is that when
58071815ce7SRobert Mustacchi  *      calculating the hash bits the order of the extracted 2M and 1G addresses
58171815ce7SRobert Mustacchi  *      are different.
58271815ce7SRobert Mustacchi  *
58371815ce7SRobert Mustacchi  *	This flow starts by calculating the three hash bits. This is defined
58471815ce7SRobert Mustacchi  *	below. In the following, all bits marked with an '@' are ones that will
58571815ce7SRobert Mustacchi  *	change when starting at address bit 12. In those cases the value will
58671815ce7SRobert Mustacchi  *	increase by 1. Here's how we calculate the hash bits:
58771815ce7SRobert Mustacchi  *
58871815ce7SRobert Mustacchi  *      hash[0] = addr[11@] ^ addr[14@] ^ addr[23] ^ addr[32]
58971815ce7SRobert Mustacchi  *      hash[1] = addr[12@] ^ addr[21] ^ addr[30]
59071815ce7SRobert Mustacchi  *      hash[2] = addr[13@] ^ addr[22] ^ addr[31]
59171815ce7SRobert Mustacchi  *
59271815ce7SRobert Mustacchi  *      With this calculated, we always assign the first bit of the channel
59371815ce7SRobert Mustacchi  *      based on the hash. The other bits are more complicated as we have to
59471815ce7SRobert Mustacchi  *      deal with that gnarly power of two problem. We determine whether or not
59571815ce7SRobert Mustacchi  *      to use the hash bits directly in the channel based on their value. If
59671815ce7SRobert Mustacchi  *      they are not equal to 3, then we use it, otherwise if they are, then we
59771815ce7SRobert Mustacchi  *      need to go back to the physical address and we take its modulus.
59871815ce7SRobert Mustacchi  *      Basically:
59971815ce7SRobert Mustacchi  *
60071815ce7SRobert Mustacchi  *      Channel Id[0] = hash[0]
60171815ce7SRobert Mustacchi  *      if (hash[2:1] == 3)
60271815ce7SRobert Mustacchi  *		Channel ID[2:1] = (addr >> [11@+3]) % 3
60371815ce7SRobert Mustacchi  *      else
60471815ce7SRobert Mustacchi  *		Channel ID[2:1] = hash[2:1]
60571815ce7SRobert Mustacchi  *
60671815ce7SRobert Mustacchi  *
60771815ce7SRobert Mustacchi  * ZEN 4 NON-POWER OF 2
60871815ce7SRobert Mustacchi  *
60971815ce7SRobert Mustacchi  * I hope you like modulus calculations, because things get even more complex
61071815ce7SRobert Mustacchi  * here now in Zen 4 which has many more modulus variations. These function in a
61171815ce7SRobert Mustacchi  * similar way to the older 6-channel hash in Milan. They require one to start
61271815ce7SRobert Mustacchi  * at address bit 8, they require that there is no die interleaving, and they
61371815ce7SRobert Mustacchi  * support socket interleaving. The different channel arrangements end up in one
61471815ce7SRobert Mustacchi  * of two sets of modulus values: a mod % 3 and a mod % 5 based on the number
61571815ce7SRobert Mustacchi  * of channels used. Unlike the Milan form, all three address ranges (64 KiB, 2
61671815ce7SRobert Mustacchi  * MiB, 1 GiB) are allowed to be used.
61771815ce7SRobert Mustacchi  *
61871815ce7SRobert Mustacchi  *   o 6-channel Zen 4, starting at address 8. 64K, 2M, and 1G range enabled.
61971815ce7SRobert Mustacchi  *     1-die and 2-socket interleaving.
62071815ce7SRobert Mustacchi  *
62171815ce7SRobert Mustacchi  *      We start by calculating the following set of hash bits regardless of
62271815ce7SRobert Mustacchi  *      the number of channels that exist. The set of hash bits that is actually
62371815ce7SRobert Mustacchi  *      used in various computations ends up varying based upon the number of
62471815ce7SRobert Mustacchi  *      channels used. In 3-5 configs, only hash[0] is used. 6-10, both hash[0]
62571815ce7SRobert Mustacchi  *      and hash[2] (yes, not hash[1]). The 12 channel config uses all three.
62671815ce7SRobert Mustacchi  *
62771815ce7SRobert Mustacchi  *      hash[0] = addr[8]  ^ addr[16] ^ addr[21] ^ addr[30] ^ addr[14]
62871815ce7SRobert Mustacchi  *      hash[1] = addr[12] ^ addr[17] ^ addr[22] ^ addr[31]
62971815ce7SRobert Mustacchi  *      hash[2] = addr[13] ^ addr[18] ^ addr[23] ^ addr[32]
63071815ce7SRobert Mustacchi  *
63171815ce7SRobert Mustacchi  *      Unlike other schemes where bits directly map here, they instead are used
63271815ce7SRobert Mustacchi  *      to seed the overall value. Depending on whether hash[0] is a 0 or 1, the
63371815ce7SRobert Mustacchi  *      system goes through two different calculations entirely. Though all of
63471815ce7SRobert Mustacchi  *      them end up involving the remainder of the system address going through
63571815ce7SRobert Mustacchi  *      the modulus. In the following, a '3@' indicates the modulus value would
63671815ce7SRobert Mustacchi  *      be swapped to 5 in a different scenario.
63771815ce7SRobert Mustacchi  *
63871815ce7SRobert Mustacchi  *      Channel ID = addr[63:14] % 3@
63971815ce7SRobert Mustacchi  *      if (hash[0] == 1)
64071815ce7SRobert Mustacchi  *		Channel ID = (Channel ID + 1) % 3@
64171815ce7SRobert Mustacchi  *
64271815ce7SRobert Mustacchi  *      Once this base has for the channel ID has been calculated, additional
64371815ce7SRobert Mustacchi  *      portions are added in. As this is the 6-channel form, we say:
64471815ce7SRobert Mustacchi  *
64571815ce7SRobert Mustacchi  *      Channel ID = Channel ID + (hash[2] * 3@)
64671815ce7SRobert Mustacchi  *
64771815ce7SRobert Mustacchi  *      Finally the socket is deterministic and always comes from hash[0].
64871815ce7SRobert Mustacchi  *      Basically:
64971815ce7SRobert Mustacchi  *
65071815ce7SRobert Mustacchi  *      Socket ID = hash[0]
65171815ce7SRobert Mustacchi  *
65271815ce7SRobert Mustacchi  *   o 12-channel Zen 4, starting at address 8. 64K, 2M, and 1G range enabled.
65371815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
65471815ce7SRobert Mustacchi  *
65571815ce7SRobert Mustacchi  *       This is a variant of the above. The hash is calculated the same way.
65671815ce7SRobert Mustacchi  *       The base Channel ID is the same and if socket interleaving were enabled
65771815ce7SRobert Mustacchi  *       it would also be hash[0]. What instead differs is how we use hash[1]
65871815ce7SRobert Mustacchi  *       and hash[2]. The following logic is used instead of the final
65971815ce7SRobert Mustacchi  *       calculation above.
66071815ce7SRobert Mustacchi  *
66171815ce7SRobert Mustacchi  *       Channel ID = Channel ID + (hash[2:1] * 3@)
66271815ce7SRobert Mustacchi  *
66371815ce7SRobert Mustacchi  *
66471815ce7SRobert Mustacchi  * POST BIT EXTRACTION
66571815ce7SRobert Mustacchi  *
66671815ce7SRobert Mustacchi  * Now, all of this was done to concoct up a series of indexes used. However,
66771815ce7SRobert Mustacchi  * you'll note that a given DRAM rule actually already has a fabric target. So
66871815ce7SRobert Mustacchi  * what do we do here? We add them together.
66971815ce7SRobert Mustacchi  *
67071815ce7SRobert Mustacchi  * The data fabric has registers that describe which bits in a fabric ID
67171815ce7SRobert Mustacchi  * correspond to a socket, die, and channel. Taking the channel, die, and socket
67271815ce7SRobert Mustacchi  * IDs above, one can construct a fabric ID. From there, we add the two data
67371815ce7SRobert Mustacchi  * fabric IDs together and can then get to the fabric ID of the actual logical
67471815ce7SRobert Mustacchi  * target. This is why all of the socket and die interleaving examples with no
67571815ce7SRobert Mustacchi  * interleaving are OK to result in a zero. The idea here is that the base
67671815ce7SRobert Mustacchi  * fabric ID in the DRAM rule will take care of indicating those other things as
67771815ce7SRobert Mustacchi  * required.
67871815ce7SRobert Mustacchi  *
67971815ce7SRobert Mustacchi  * You'll note the use of the term "logical target" up above. That's because
68071815ce7SRobert Mustacchi  * some platforms have the ability to remap logical targets to physical targets
68171815ce7SRobert Mustacchi  * (identified by the use of the ZEN_UMC_FAM_F_TARG_REMAP flag in the family
68271815ce7SRobert Mustacchi  * data). The way that remapping works changes based on the hardware generation.
68371815ce7SRobert Mustacchi  * This was first added in Milan (Zen 3) CPUs. In that model, you would use the
68471815ce7SRobert Mustacchi  * socket and component information from the target ID to identify which
68571815ce7SRobert Mustacchi  * remapping rules to use. On Genoa (Zen 4) CPUs, you would instead use
68671815ce7SRobert Mustacchi  * information in the rule itself to determine which of the remap rule sets to
68771815ce7SRobert Mustacchi  * use and then uses the component ID to select which rewrite rule to use.
68871815ce7SRobert Mustacchi  *
68971815ce7SRobert Mustacchi  * Finally, there's one small wrinkle with this whole scheme that we haven't
69071815ce7SRobert Mustacchi  * discussed: what actually is the address that we plug into this calculation.
69171815ce7SRobert Mustacchi  * While you might think it actually is just the system address itself, that
69271815ce7SRobert Mustacchi  * isn't actually always the case. Sometimes rather than using the address
69371815ce7SRobert Mustacchi  * itself, it gets normalized based on the DRAM rule, which involves subtracting
69471815ce7SRobert Mustacchi  * out the base address and potentially subtracting out the size of the DRAM
69571815ce7SRobert Mustacchi  * hole (if the address is above the hole and hoisting is active for that
69671815ce7SRobert Mustacchi  * range). When this is performed appears to tie to the DF generation. After Zen
69771815ce7SRobert Mustacchi  * 3, it is always the default (e.g. Zen 4 and things from DF gen 3.5). At and
69871815ce7SRobert Mustacchi  * before Zen 3, it only occurs if we are doing a non-power of 2 based hashing.
69971815ce7SRobert Mustacchi  *
70071815ce7SRobert Mustacchi  * --------------------------------------------
70171815ce7SRobert Mustacchi  * Data Fabric Interleave Address Normalization
70271815ce7SRobert Mustacchi  * --------------------------------------------
70371815ce7SRobert Mustacchi  *
70471815ce7SRobert Mustacchi  * While you may have thought that we were actually done with the normalization
70571815ce7SRobert Mustacchi  * fun in the last section, there's still a bit more here that we need to
70671815ce7SRobert Mustacchi  * consider. In particular, there's a secondary transformation beyond
70771815ce7SRobert Mustacchi  * interleaving that occurs as part of constructing the channel normalized
70871815ce7SRobert Mustacchi  * address. Effectively, we need to account for all the bits that were used in
70971815ce7SRobert Mustacchi  * the interleaving and generally speaking remove them from our normalized
71071815ce7SRobert Mustacchi  * address.
71171815ce7SRobert Mustacchi  *
71271815ce7SRobert Mustacchi  * While this may sound weird on paper, the way to think about it is that
71371815ce7SRobert Mustacchi  * interleaving at some granularity means that each device is grabbing the same
71471815ce7SRobert Mustacchi  * set of addresses, the interleave just is used to direct it to its own
71571815ce7SRobert Mustacchi  * location. When working with a channel normalized address, we're effectively
71671815ce7SRobert Mustacchi  * creating a new region of addresses that have meaning within the DIMMs
71771815ce7SRobert Mustacchi  * themselves. The channel doesn't care about what got it there, mainly just
71871815ce7SRobert Mustacchi  * what it is now. So with that in mind, we need to discuss how we remove all
71971815ce7SRobert Mustacchi  * the interleaving information in our different modes.
72071815ce7SRobert Mustacchi  *
72171815ce7SRobert Mustacchi  * Just to make sure it's clear, we are _removing_ all bits that were used for
72271815ce7SRobert Mustacchi  * interleaving. This causes all bits above the removed ones to be shifted
72371815ce7SRobert Mustacchi  * right.
72471815ce7SRobert Mustacchi  *
72571815ce7SRobert Mustacchi  * First, we have the case of standard power of 2 interleaving that applies to
72671815ce7SRobert Mustacchi  * the 1, 2, 4, 8, 16, and 32 channel configurations. Here, we need to account
72771815ce7SRobert Mustacchi  * for the total number of bits that are used for the channel, die, and socket
72871815ce7SRobert Mustacchi  * interleaving and we simply remove all those bits starting from the starting
72971815ce7SRobert Mustacchi  * address.
73071815ce7SRobert Mustacchi  *
73171815ce7SRobert Mustacchi  *   o 8-channel interleave, 1-die interleave, 2-socket interleave
73271815ce7SRobert Mustacchi  *     Start at bit 9
73371815ce7SRobert Mustacchi  *
73471815ce7SRobert Mustacchi  *     If we look at this example, we are using 3 bits for the channel, 1 for
73571815ce7SRobert Mustacchi  *     the socket, for a total of 4 bits. Because this is starting at bit 9,
73671815ce7SRobert Mustacchi  *     this means that interleaving covers the bit range [12:9]. In this case
73771815ce7SRobert Mustacchi  *     our new address would be (orig[63:13] >> 4) | orig[8:0].
73871815ce7SRobert Mustacchi  *
73971815ce7SRobert Mustacchi  *
74071815ce7SRobert Mustacchi  * COD and NPS HASHING
74171815ce7SRobert Mustacchi  *
74271815ce7SRobert Mustacchi  * That was the simple case, next we have the COD/NPS hashing case that we need
74371815ce7SRobert Mustacchi  * to consider. If we look at these, the way that they work is that they split
74471815ce7SRobert Mustacchi  * which bits they use for determining the channel address and then hash others
74571815ce7SRobert Mustacchi  * in. Here, we need to extract the starting address bit, then continue at bit
74671815ce7SRobert Mustacchi  * 12 based on the number of bits in use and whether or not socket interleaving
74771815ce7SRobert Mustacchi  * is at play for the NPS variant. Let's look at an example here:
74871815ce7SRobert Mustacchi  *
74971815ce7SRobert Mustacchi  *   o 8-channel "COD" hashing, starting at address 9. All three ranges enabled.
75071815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
75171815ce7SRobert Mustacchi  *
75271815ce7SRobert Mustacchi  *     Here we have three total bits being used. Because we start at bit 9, this
75371815ce7SRobert Mustacchi  *     means we need to drop bits [13:12], [9]. So our new address would be:
75471815ce7SRobert Mustacchi  *
75571815ce7SRobert Mustacchi  *     orig[63:14] >> 3 | orig[11:10] >> 1 | orig[8:0]
75671815ce7SRobert Mustacchi  *     |                  |                  +-> stays the same
75771815ce7SRobert Mustacchi  *     |                  +-> relocated to bit 9 -- shifted by 1 because we
75871815ce7SRobert Mustacchi  *     |                      removed bit 9.
75971815ce7SRobert Mustacchi  *     +--> Relocated to bit 11 -- shifted by 3 because we removed bits, 9, 12,
76071815ce7SRobert Mustacchi  *          and 13.
76171815ce7SRobert Mustacchi  *
76271815ce7SRobert Mustacchi  *   o 8-channel "NPS" hashing, starting at address 8. All three ranges enabled.
76371815ce7SRobert Mustacchi  *     1-die and 2-socket interleaving.
76471815ce7SRobert Mustacchi  *
76571815ce7SRobert Mustacchi  *     Here we need to remove bits [14:12], [8]. We're removing an extra bit
76671815ce7SRobert Mustacchi  *     because we have 2-socket interleaving. This results in a new address of:
76771815ce7SRobert Mustacchi  *
76871815ce7SRobert Mustacchi  *     orig[63:15] >> 4 | orig[11:9] >> 1 | orig[7:0]
76971815ce7SRobert Mustacchi  *     |                  |                 +-> stays the same
77071815ce7SRobert Mustacchi  *     |                  +-> relocated to bit 8 -- shifted by 1 because we
77171815ce7SRobert Mustacchi  *     |                      removed bit 8.
77271815ce7SRobert Mustacchi  *     +--> Relocated to bit 11 -- shifted by 4 because we removed bits, 8, 12,
77371815ce7SRobert Mustacchi  *          13, and 14.
77471815ce7SRobert Mustacchi  *
77571815ce7SRobert Mustacchi  *
77671815ce7SRobert Mustacchi  * ZEN 3 6-CHANNEL
77771815ce7SRobert Mustacchi  *
77871815ce7SRobert Mustacchi  * Now, to the real fun stuff, our non-powers of two. First, let's start with
77971815ce7SRobert Mustacchi  * our friend, the Zen 3 6-channel hash. So, the first thing that we need to do
78071815ce7SRobert Mustacchi  * here is start by recomputing our hash again based on the current normalized
78171815ce7SRobert Mustacchi  * address. Regardless of the hash value, this first removes all three bits from
78271815ce7SRobert Mustacchi  * the starting address, so that's removing either [14:12] or [13:11].
78371815ce7SRobert Mustacchi  *
78471815ce7SRobert Mustacchi  * The rest of the normalization process here is quite complex and somewhat mind
78571815ce7SRobert Mustacchi  * bending. Let's start working through an example here and build this up.
78671815ce7SRobert Mustacchi  * First, let's assume that each channel has a single 16 GiB RDIMM. This would
78771815ce7SRobert Mustacchi  * mean that the channel itself has 96 GiB RDIMM. However, by removing 3 bits
78871815ce7SRobert Mustacchi  * worth, that technically corresponds to an 8-channel configuration that
78971815ce7SRobert Mustacchi  * normally suggest a 128 GiB configuration. The processor requires us to record
79071815ce7SRobert Mustacchi  * this fact in the DF::Np2ChannelConfig register. The value that it wants us a
79171815ce7SRobert Mustacchi  * bit weird. We believe it's calculated by the following:
79271815ce7SRobert Mustacchi  *
79371815ce7SRobert Mustacchi  *   1. Round the channel size up to the next power of 2.
79471815ce7SRobert Mustacchi  *   2. Divide this total size by 64 KiB.
79571815ce7SRobert Mustacchi  *   3. Determine the log base 2 that satisfies this value.
79671815ce7SRobert Mustacchi  *
79771815ce7SRobert Mustacchi  * In our particular example above. We have a 96 GiB channel, so for (1) we end
79871815ce7SRobert Mustacchi  * up with 128 GiB (2^37). We now divide that by 64 KiB (2^16), so this becomes
79971815ce7SRobert Mustacchi  * 2^(37 - 16) or 2^21. Because we want the log base 2 of 2^21 from (2), this
80071815ce7SRobert Mustacchi  * simply becomes 21. The DF::Np2ChannelConfig has two members, a 'space 0' and
80171815ce7SRobert Mustacchi  * 'space 1'. Near as we can tell, in this mode only 'space 0' is used.
80271815ce7SRobert Mustacchi  *
80371815ce7SRobert Mustacchi  * Before we get into the actual normalization scheme, we have to ask ourselves
80471815ce7SRobert Mustacchi  * how do we actually interleave data 6 ways. The scheme here is involved.
80571815ce7SRobert Mustacchi  * First, it's important to remember like with other normalization schemes, we
80671815ce7SRobert Mustacchi  * do adjust for the address for the base address in the DRAM rule and then also
80771815ce7SRobert Mustacchi  * take into account the DRAM hole if present.
80871815ce7SRobert Mustacchi  *
80971815ce7SRobert Mustacchi  * If we delete 3 bits, let's take a sample address and see where it would end
81071815ce7SRobert Mustacchi  * up in the above scheme. We're going to take our 3 address bits and say that
81171815ce7SRobert Mustacchi  * they start at bit 12, so this means that the bits removed are [14:12]. So the
81271815ce7SRobert Mustacchi  * following are the 8 addresses that we have here and where they end up
81371815ce7SRobert Mustacchi  * starting with 1ff:
81471815ce7SRobert Mustacchi  *
81571815ce7SRobert Mustacchi  *   o 0x01ff  -> 0x1ff, Channel 0 (hash 0b000)
81671815ce7SRobert Mustacchi  *   o 0x11ff  -> 0x1ff, Channel 1 (hash 0b001)
81771815ce7SRobert Mustacchi  *   o 0x21ff  -> 0x1ff, Channel 2 (hash 0b010)
81871815ce7SRobert Mustacchi  *   o 0x31ff  -> 0x1ff, Channel 3 (hash 0b011)
81971815ce7SRobert Mustacchi  *   o 0x41ff  -> 0x1ff, Channel 4 (hash 0b100)
82071815ce7SRobert Mustacchi  *   o 0x51ff  -> 0x1ff, Channel 5 (hash 0b101)
82171815ce7SRobert Mustacchi  *   o 0x61ff  -> 0x3000001ff, Channel 0 (hash 0b110)
82271815ce7SRobert Mustacchi  *   o 0x71ff  -> 0x3000001ff, Channel 1 (hash 0b111)
82371815ce7SRobert Mustacchi  *
82471815ce7SRobert Mustacchi  * Yes, we did just jump to near the top of what is a 16 GiB DIMM's range for
82571815ce7SRobert Mustacchi  * those last two. The way we determine when to do this jump is based on our
82671815ce7SRobert Mustacchi  * hash. Effectively we ask what is hash[2:1]. If it is 0b11, then we need to
82771815ce7SRobert Mustacchi  * do something different and enter this special case, basically jumping to the
82871815ce7SRobert Mustacchi  * top of the range. If we think about a 6-channel configuration for a moment,
82971815ce7SRobert Mustacchi  * the thing that doesn't exist are the traditional 8-channel hash DIMMs 0b110
83071815ce7SRobert Mustacchi  * and 0b111.
83171815ce7SRobert Mustacchi  *
83271815ce7SRobert Mustacchi  * If you go back to the interleave this kind of meshes, that tried to handle
83371815ce7SRobert Mustacchi  * the case of the hash being 0, 1, and 2, normally, and then did special things
83471815ce7SRobert Mustacchi  * with the case of the hash being in this upper quadrant. The hash then
83571815ce7SRobert Mustacchi  * determined where it went by shifting over the upper address and doing a mod
83671815ce7SRobert Mustacchi  * 3 and using that to determine the upper two bits. With that weird address at
83771815ce7SRobert Mustacchi  * the top of the range, let's go through and see what else actually goes to
83871815ce7SRobert Mustacchi  * those weird addresses:
83971815ce7SRobert Mustacchi  *
84071815ce7SRobert Mustacchi  *   o 0x08000061ff -> 0x3000001ff, Channel 2 (hash 0b110)
84171815ce7SRobert Mustacchi  *   o 0x08000071ff -> 0x3000001ff, Channel 3 (hash 0b111)
84271815ce7SRobert Mustacchi  *   o 0x10000061ff -> 0x3000001ff, Channel 4 (hash 0b110)
84371815ce7SRobert Mustacchi  *   o 0x10000071ff -> 0x3000001ff, Channel 5 (hash 0b111)
84471815ce7SRobert Mustacchi  *
84571815ce7SRobert Mustacchi  * Based on the above you can see that we've split the 16 GiB DIMM into a 12 GiB
84671815ce7SRobert Mustacchi  * region (e.g. [ 0x0, 0x300000000 ), and a 4 GiB region [ 0x300000000,
84771815ce7SRobert Mustacchi  * 0x400000000 ). What seems to happen is that the CPU algorithmically is going
84871815ce7SRobert Mustacchi  * to put things in this upper range. To perform that action it goes back to the
84971815ce7SRobert Mustacchi  * register information that we stored in DF::Np2ChannelConfig. The way this
85071815ce7SRobert Mustacchi  * seems to be thought of is it wants to set the upper two bits of a 64 KiB
85171815ce7SRobert Mustacchi  * chunk (e.g. bits [15:14]) to 0b11 and then shift that over based on the DIMM
85271815ce7SRobert Mustacchi  * size.
85371815ce7SRobert Mustacchi  *
85471815ce7SRobert Mustacchi  * Our 16 GiB DIMM has 34 bits, so effectively we want to set bits [33:32] in
85571815ce7SRobert Mustacchi  * this case. The channel is 37 bits wide, which the CPU again knows as 2^21 *
85671815ce7SRobert Mustacchi  * 2^16. So it constructs the 64 KiB value of [15:14] = 0b11 and fills the rest
85771815ce7SRobert Mustacchi  * with zeros. It then multiplies it by 2^(21 - 3), or 2^18. The - 3 comes from
85871815ce7SRobert Mustacchi  * the fact that we removed 3 address bits. This when added to the above gets
85971815ce7SRobert Mustacchi  * us bits [33,32] = 0b11.
86071815ce7SRobert Mustacchi  *
86171815ce7SRobert Mustacchi  * While this appears to be the logic, I don't have a proof that this scheme
86271815ce7SRobert Mustacchi  * actually evenly covers the entire range, but a few examples appear to work
86371815ce7SRobert Mustacchi  * out.
86471815ce7SRobert Mustacchi  *
86571815ce7SRobert Mustacchi  * With this, the standard example flow that we give, results in something like:
86671815ce7SRobert Mustacchi  *
86771815ce7SRobert Mustacchi  *   o 6-channel Zen 3, starting at address 11. 2M and 1G range enabled. Here,
86871815ce7SRobert Mustacchi  *     we assume that the value of the NP2 space0 is 21 bits. This example
86971815ce7SRobert Mustacchi  *     assumes we have 96 GiB total memory, which means rounding up to 128 GiB.
87071815ce7SRobert Mustacchi  *
87171815ce7SRobert Mustacchi  *     Step 1 here is to adjust our address to remove the three bits indicated.
87271815ce7SRobert Mustacchi  *     So we simply always set our new address to:
87371815ce7SRobert Mustacchi  *
87471815ce7SRobert Mustacchi  *     orig[63:14] >> 3 | orig[10:0]
87571815ce7SRobert Mustacchi  *     |                  +-> stays the same
87671815ce7SRobert Mustacchi  *     +--> Relocated to bit 11 because a 6-channel config always uses 3 bits to
87771815ce7SRobert Mustacchi  *          perform interleaving.
87871815ce7SRobert Mustacchi  *
87971815ce7SRobert Mustacchi  *     At this step, one would need to consult the hash of the normalized
88071815ce7SRobert Mustacchi  *     address before removing bits (but after adjusting for the base / DRAM
88171815ce7SRobert Mustacchi  *     hole). If hash[2:1] == 3, then we would say that the address is actually:
88271815ce7SRobert Mustacchi  *
88371815ce7SRobert Mustacchi  *     0b11 << 32 | orig[63:14] >> 3 | orig[10:0]
88471815ce7SRobert Mustacchi  *
88571815ce7SRobert Mustacchi  *
88671815ce7SRobert Mustacchi  * ZEN 4 NON-POWER OF 2
88771815ce7SRobert Mustacchi  *
88871815ce7SRobert Mustacchi  * Next, we have the DFv4 versions of the 3, 5, 6, 10, and 12 channel hashing.
88971815ce7SRobert Mustacchi  * An important part of this is whether or not there is any socket hashing going
89071815ce7SRobert Mustacchi  * on. Recall there, that if socket hashing was going on, then it is part of the
89171815ce7SRobert Mustacchi  * interleave logic; however, if it is not, then its hash actually becomes
89271815ce7SRobert Mustacchi  * part of the normalized address, but not in the same spot!
89371815ce7SRobert Mustacchi  *
89471815ce7SRobert Mustacchi  * In this mode, we always remove the bits that are actually used by the hash.
89571815ce7SRobert Mustacchi  * Recall that some modes use hash[0], others hash[0] and hash[2], and then only
89671815ce7SRobert Mustacchi  * the 12-channel config uses hash[2:0]. This means we need to be careful in how
89771815ce7SRobert Mustacchi  * we actually remove address bits. All other bits in this lower range we end up
89871815ce7SRobert Mustacchi  * keeping and using. The top bits, e.g. addr[63:14] are kept and divided by the
89971815ce7SRobert Mustacchi  * actual channel-modulus. If we're not performing socket interleaving and
90071815ce7SRobert Mustacchi  * therefore need to keep the value of hash[0], then it is appended as the least
90171815ce7SRobert Mustacchi  * significant bit of that calculation.
90271815ce7SRobert Mustacchi  *
90371815ce7SRobert Mustacchi  * Let's look at an example of this to try to make sense of it all.
90471815ce7SRobert Mustacchi  *
90571815ce7SRobert Mustacchi  *   o 6-channel Zen 4, starting at address 8. 64K, 2M, and 1G range enabled.
90671815ce7SRobert Mustacchi  *     1-die and 2-socket interleaving.
90771815ce7SRobert Mustacchi  *
90871815ce7SRobert Mustacchi  *     Here we'd start by calculating hash[2:0] as described in the earlier
90971815ce7SRobert Mustacchi  *     interleaving situation. Because we're using a socket interleave, we will
91071815ce7SRobert Mustacchi  *     not opt to include hash[0] in the higher-level address calculation.
91171815ce7SRobert Mustacchi  *     Because this is a 6-channel calculation, our modulus is 3. Here, we will
91271815ce7SRobert Mustacchi  *     strip out bits 8 and 13 (recall in the interleaving 6-channel example we
91371815ce7SRobert Mustacchi  *     ignored hash[1], thus no bit 12 here). Our new address will be:
91471815ce7SRobert Mustacchi  *
91571815ce7SRobert Mustacchi  *     (orig[63:14] / 3) >> 2 | orig[12:9] >> 1 | orig[7:0]
91671815ce7SRobert Mustacchi  *      |                       |                 +-> stays the same
91771815ce7SRobert Mustacchi  *      |                       +-> relocated to bit 8 -- shifted by 1 because
91871815ce7SRobert Mustacchi  *      |                           we removed bit 8.
91971815ce7SRobert Mustacchi  *      +--> Relocated to bit 12 -- shifted by 2 because we removed bits 8 and
92071815ce7SRobert Mustacchi  *           13.
92171815ce7SRobert Mustacchi  *
92271815ce7SRobert Mustacchi  *   o 12-channel Zen 4, starting at address 8. 64K, 2M, and 1G range enabled.
92371815ce7SRobert Mustacchi  *     1-die and 1-socket interleaving.
92471815ce7SRobert Mustacchi  *
92571815ce7SRobert Mustacchi  *     This is a slightly different case from the above in two ways. First, we
92671815ce7SRobert Mustacchi  *     will end up removing bits 8, 12, and 13, but then we'll also reuse
92771815ce7SRobert Mustacchi  *     hash[0]. Our new address will be:
92871815ce7SRobert Mustacchi  *
92971815ce7SRobert Mustacchi  *     ((orig[63:14] / 3) << 1 | hash[0]) >> 3 | orig[11:9] >> 1 | orig[7:0]
93071815ce7SRobert Mustacchi  *      |                                   |                      +-> stays the
93171815ce7SRobert Mustacchi  *      |                                   |                          same
93271815ce7SRobert Mustacchi  *      |                                   +-> relocated to bit 8 -- shifted by
93371815ce7SRobert Mustacchi  *      |                                       1 because we removed bit 8.
93471815ce7SRobert Mustacchi  *      +--> Relocated to bit 11 -- shifted by 3 because we removed bits 8, 12,
93571815ce7SRobert Mustacchi  *           and 13.
93671815ce7SRobert Mustacchi  *
93771815ce7SRobert Mustacchi  * That's most of the normalization process for the time being. We will have to
93871815ce7SRobert Mustacchi  * revisit this when we have to transform a normal address into a system address
93971815ce7SRobert Mustacchi  * and undo all this.
94071815ce7SRobert Mustacchi  *
94171815ce7SRobert Mustacchi  * -------------------------------------
94271815ce7SRobert Mustacchi  * Selecting a DIMM and UMC Organization
94371815ce7SRobert Mustacchi  * -------------------------------------
94471815ce7SRobert Mustacchi  *
94571815ce7SRobert Mustacchi  * One of the more nuanced things in decoding and encoding is the question of
94671815ce7SRobert Mustacchi  * where do we send a channel normalized address. That is, now that we've gotten
94771815ce7SRobert Mustacchi  * to a given channel, we need to transform the address into something
94871815ce7SRobert Mustacchi  * meaningful for a DIMM, and select a DIMM as well. The UMC SMN space contains
94971815ce7SRobert Mustacchi  * a number of Base Address and Mask registers which they describe as activating
95071815ce7SRobert Mustacchi  * a chip-select. A given UMC has up to four primary chip-selects (we'll come
95171815ce7SRobert Mustacchi  * back to DDR5 sub-channels later). The first two always go to the first DIMM
95271815ce7SRobert Mustacchi  * in the channel and the latter two always go to the second DIMM in the
95371815ce7SRobert Mustacchi  * channel. Put another way, you can always determine which DIMM you are
95471815ce7SRobert Mustacchi  * referring to by taking the chip-select and shifting it by 1.
95571815ce7SRobert Mustacchi  *
95671815ce7SRobert Mustacchi  * The UMC Channel registers are organized a bit differently in different
95771815ce7SRobert Mustacchi  * hardware generations. In a DDR5 based UMC, almost all of our settings are on
95871815ce7SRobert Mustacchi  * a per-chip-select basis while as in a DDR4 based system only the bases and
95971815ce7SRobert Mustacchi  * masks are. While gathering data we normalize this such that each logical
96071815ce7SRobert Mustacchi  * chip-select (umc_cs_t) that we have in the system has the same data so that
96171815ce7SRobert Mustacchi  * way DDR4 and DDR5 based systems are the same to the decoding logic. There is
96271815ce7SRobert Mustacchi  * also channel-wide data such as hash configurations and related.
96371815ce7SRobert Mustacchi  *
96471815ce7SRobert Mustacchi  * Each channel has a set of base and mask registers (and secondary ones as
96571815ce7SRobert Mustacchi  * well). To determine if we activate a given one, we first check if the
96671815ce7SRobert Mustacchi  * enabled bit is set. The enabled bit is set on a per-base basis, so both the
96771815ce7SRobert Mustacchi  * primary and secondary registers have separate enables. As there are four of
96871815ce7SRobert Mustacchi  * each base, mask, secondary base, and secondary mask, we say that if a
96971815ce7SRobert Mustacchi  * normalized address matches either a given indexes primary or secondary index,
97071815ce7SRobert Mustacchi  * then it activates that given UMC index. The basic formula for an enabled
97171815ce7SRobert Mustacchi  * selection is:
97271815ce7SRobert Mustacchi  *
97371815ce7SRobert Mustacchi  *	NormAddr & ~Mask[i] == Base[i] & ~Mask[i]
97471815ce7SRobert Mustacchi  *
97571815ce7SRobert Mustacchi  * Once this is selected, this index in the UMC is what it always used to derive
97671815ce7SRobert Mustacchi  * the rest of the information that is specific to a given chip-select or DIMM.
97771815ce7SRobert Mustacchi  * An important thing to remember is that from this point onwards, while there
97871815ce7SRobert Mustacchi  * is a bunch of hashing and interleaving logic it doesn't change which UMC
97971815ce7SRobert Mustacchi  * channel we read the data from. Though the particular DIMM, rank, and address
98071815ce7SRobert Mustacchi  * we access will change as we go through hashing and interleaving.
98171815ce7SRobert Mustacchi  *
98271815ce7SRobert Mustacchi  * ------------------------
98371815ce7SRobert Mustacchi  * Row and Column Selection
98471815ce7SRobert Mustacchi  * ------------------------
98571815ce7SRobert Mustacchi  *
98671815ce7SRobert Mustacchi  * The number of bits that are used for the row and column address of a DIMM
98771815ce7SRobert Mustacchi  * varies based on the type of module itself. These depend on the density of a
98871815ce7SRobert Mustacchi  * DIMM module, e.g. how large an individual DRAM block is, a value such as 16
98971815ce7SRobert Mustacchi  * Gbit, and the number of these wide it is, which is generally phrased as X4,
99071815ce7SRobert Mustacchi  * X8, and X16. The memory controller encodes the number of bits (derived from
99171815ce7SRobert Mustacchi  * the DIMM's SPD data) and then determines which bits are used for addresses.
99271815ce7SRobert Mustacchi  *
99371815ce7SRobert Mustacchi  * Based on this information we can initially construct a row and a column
99471815ce7SRobert Mustacchi  * address by leveraging the information about the number of bits and then
99571815ce7SRobert Mustacchi  * extracting the correct bits out of the normalized channel address.
99671815ce7SRobert Mustacchi  *
99771815ce7SRobert Mustacchi  * If you've made it this far, you know nothing is quite this simple, despite it
99871815ce7SRobert Mustacchi  * seeming so. Importantly, not all DIMMs actually have storage that is a power
99971815ce7SRobert Mustacchi  * of 2. As such, there's another bit that we have to consult to transform the
100071815ce7SRobert Mustacchi  * actual value that we have for a row, remarkably the column somehow has no
100171815ce7SRobert Mustacchi  * transformations applied to it.
100271815ce7SRobert Mustacchi  *
100371815ce7SRobert Mustacchi  * The hardware gives us information on inverting the two 'most significant
100471815ce7SRobert Mustacchi  * bits' of the row address which we store in 'ucs_inv_msbs'. First, we have the
100571815ce7SRobert Mustacchi  * question of what are our most significant bits here. This is basically
100671815ce7SRobert Mustacchi  * determined by the number of low and high row bits. In this case higher
100771815ce7SRobert Mustacchi  * actually is what we want. Note, the high row bits only exist in DDR4. Next,
100871815ce7SRobert Mustacchi  * we need to know whether we used the primary or secondary base/mask pair for
100971815ce7SRobert Mustacchi  * this as there is a primary and secondary inversion bits. The higher bit of
101071815ce7SRobert Mustacchi  * the inversion register (e.g ucs_inv_msbs[1]) corresponds to the highest row
101171815ce7SRobert Mustacchi  * bit. A zero in the bit position indicates that we should not perform an
101271815ce7SRobert Mustacchi  * inversion where as a one says that we should invert this.
101371815ce7SRobert Mustacchi  *
101471815ce7SRobert Mustacchi  * To actually make this happen we can take advantage of the fact that the
101571815ce7SRobert Mustacchi  * meaning of a 0/1 above means that this can be implemented with a binary
101671815ce7SRobert Mustacchi  * exclusive-OR (XOR). Logically speaking if we have a don't invert setting
101771815ce7SRobert Mustacchi  * present, a 0, then x ^ 0 is always x. However, if we have a 1 present, then
101871815ce7SRobert Mustacchi  * we know that (for a single bit) x ^ 1 = ~x. We take advantage of this fact in
101971815ce7SRobert Mustacchi  * the row logic.
102071815ce7SRobert Mustacchi  *
102171815ce7SRobert Mustacchi  * ---------------------
102271815ce7SRobert Mustacchi  * Banks and Bank Groups
102371815ce7SRobert Mustacchi  * ---------------------
102471815ce7SRobert Mustacchi  *
102571815ce7SRobert Mustacchi  * While addressing within a given module is done by the use of a row and column
102671815ce7SRobert Mustacchi  * address, to increase storage density a module generally has a number of
102771815ce7SRobert Mustacchi  * banks, which may be organized into one or more bank groups. While a given
102871815ce7SRobert Mustacchi  * DDR4/5 access happens in some prefetched chunk of say 64 bytes (what do you
102971815ce7SRobert Mustacchi  * know, that's a cacheline), that all occurs within a single bank. The addition
103071815ce7SRobert Mustacchi  * of bank groups makes it easier to access data in parallel -- it is often
103171815ce7SRobert Mustacchi  * faster to read from another bank group than to read another region inside a
103271815ce7SRobert Mustacchi  * bank group.
103371815ce7SRobert Mustacchi  *
103471815ce7SRobert Mustacchi  * Based on the DIMMs internal configuration, there will be a specified number
103571815ce7SRobert Mustacchi  * of bits used for the overall bank address (including bank group bits)
103671815ce7SRobert Mustacchi  * followed by a number of bits actually used for bank groups. There are
103771815ce7SRobert Mustacchi  * separately an array of bits used to concoct the actual address. It appears,
103871815ce7SRobert Mustacchi  * mostly through experimental evidence, that the bank group bits occur first
103971815ce7SRobert Mustacchi  * and then are followed by the bank selection itself.  This makes some sense if
104071815ce7SRobert Mustacchi  * you assume that switching bank groups is faster than switching banks.
104171815ce7SRobert Mustacchi  *
104271815ce7SRobert Mustacchi  * So if we see the UMC noting 4 bank bits and 2 bank groups bits, that means
104371815ce7SRobert Mustacchi  * that the umc_cs_t's ucs_bank_bits[1:0] correspond to bank_group[1:0] and
104471815ce7SRobert Mustacchi  * ucs_bank_bits[3:2] correspond to bank_address[1:0]. However, if there were no
104571815ce7SRobert Mustacchi  * bank bits indicated, then all of the address bits would correspond to the
104671815ce7SRobert Mustacchi  * bank address.
104771815ce7SRobert Mustacchi  *
104871815ce7SRobert Mustacchi  * Now, this would all be straightforward if not for hashing, our favorite.
104971815ce7SRobert Mustacchi  * There are five bank hashing registers per channel (UMC_BANK_HASH_DDR4,
105071815ce7SRobert Mustacchi  * UMC_BANK_HASH_DDR5), one that corresponds to the five possible bank bits. To
105171815ce7SRobert Mustacchi  * do this we need to use the calculated row and column that we previously
105271815ce7SRobert Mustacchi  * determined. This calculation happens in a few steps:
105371815ce7SRobert Mustacchi  *
105471815ce7SRobert Mustacchi  *   1) First check if the enable bit is set in the rule. If not, just use the
105571815ce7SRobert Mustacchi  *      normal bank address bit and we're done.
105671815ce7SRobert Mustacchi  *   2) Take a bitwise-AND of the calculated row and hash register's row value.
105771815ce7SRobert Mustacchi  *      Next do the same thing for the column.
105871815ce7SRobert Mustacchi  *   3) For each bit in the row, progressively XOR it, e.g. row[0] ^ row[1] ^
105971815ce7SRobert Mustacchi  *      row[2] ^ ... to calculate a net bit value for the row. This then
106071815ce7SRobert Mustacchi  *      repeats itself for the column. What basically has happened is that we're
106171815ce7SRobert Mustacchi  *      using the hash register to select which bits to impact our decision.
106271815ce7SRobert Mustacchi  *      Think of this as a traditional bitwise functional reduce.
106371815ce7SRobert Mustacchi  *   4) XOR the combined rank bit with the column bit and the actual bank
106471815ce7SRobert Mustacchi  *      address bit from the normalized address. So if this were bank bit 0,
106571815ce7SRobert Mustacchi  *      which indicated we should use bit 15 for bank[0], then we would
106671815ce7SRobert Mustacchi  *      ultimately say our new bit is norm_addr[15] ^ row_xor ^ col_xor
106771815ce7SRobert Mustacchi  *
106871815ce7SRobert Mustacchi  * An important caveat is that we would only consult all this if we actually
106971815ce7SRobert Mustacchi  * were told that the bank bit was being used. For example if we had 3 bank
107071815ce7SRobert Mustacchi  * bits, then we'd only check the first 3 hash registers. The latter two would
107171815ce7SRobert Mustacchi  * be ignored.
107271815ce7SRobert Mustacchi  *
107371815ce7SRobert Mustacchi  * Once this process is done, then we can go back and split the activated bank
107471815ce7SRobert Mustacchi  * into the actual bank used and the bank group used based on the first bits
107571815ce7SRobert Mustacchi  * going to the bank group.
107671815ce7SRobert Mustacchi  *
107771815ce7SRobert Mustacchi  * ---------------
107871815ce7SRobert Mustacchi  * DDR5 Sub-channel
107971815ce7SRobert Mustacchi  * ---------------
108071815ce7SRobert Mustacchi  *
108171815ce7SRobert Mustacchi  * As described in the definitions section, DDR5 has the notion of a
108271815ce7SRobert Mustacchi  * sub-channel. Here, a single bit is used to determine which of the
108371815ce7SRobert Mustacchi  * sub-channels to actually operate and utilize. Importantly the same
108471815ce7SRobert Mustacchi  * chip-select seems to apply to both halves of a given sub-channel.
108571815ce7SRobert Mustacchi  *
108671815ce7SRobert Mustacchi  * There is also a hash that is used here. The hash here utilizes the calculated
108771815ce7SRobert Mustacchi  * bank, column, and row and follows the same pattern used in the bank
108871815ce7SRobert Mustacchi  * calculation where we do a bunch of running exclusive-ORs and then do that
108971815ce7SRobert Mustacchi  * with the original value we found to get the new value. Because there's only
109071815ce7SRobert Mustacchi  * one bit for the sub-channel, we only have a single hash to consider.
109171815ce7SRobert Mustacchi  *
109271815ce7SRobert Mustacchi  * -------------------------------------------
109371815ce7SRobert Mustacchi  * Ranks, Chip-Select, and Rank Multiplication
109471815ce7SRobert Mustacchi  * -------------------------------------------
109571815ce7SRobert Mustacchi  *
109671815ce7SRobert Mustacchi  * The notion of ranks and the chip-select are interwoven. From a strict DDR4
109771815ce7SRobert Mustacchi  * RDIMM perspective, there are two lines that are dedicated for chip-selects
109871815ce7SRobert Mustacchi  * and then another two that are shared with three 'chip-id' bits that are used
109971815ce7SRobert Mustacchi  * in 3DS RDIMMs. In all cases the controller starts with two logical chip
110071815ce7SRobert Mustacchi  * selects and then uses something called rank multiplication to figure out how
110171815ce7SRobert Mustacchi  * to multiplex that and map to the broader set of things. Basically, in
110271815ce7SRobert Mustacchi  * reality, DDR4 RDIMMs allow for 4 bits to determine a rank and then 3DS RDIMMs
110371815ce7SRobert Mustacchi  * use 2 bits for a rank and 3 bits to select a stacked chip. In DDR5 this is
110471815ce7SRobert Mustacchi  * different and you just have 2 bits for a rank.
110571815ce7SRobert Mustacchi  *
110671815ce7SRobert Mustacchi  * It's not entirely clear from what we know from AMD, but it seems that we use
110771815ce7SRobert Mustacchi  * the RM bits as a way to basically go beyond the basic 2 bits of chip-select
110871815ce7SRobert Mustacchi  * which is determined based on which channel we logically activate. Initially
110971815ce7SRobert Mustacchi  * we treat this as two distinct things, here as that's what we get from the
111071815ce7SRobert Mustacchi  * hardware. There are two hashes here a chip-select and rank-multiplication
111171815ce7SRobert Mustacchi  * hash. Unlike the others, which rely on the bank, row, and column addresses,
111271815ce7SRobert Mustacchi  * this hash relies on the normalized address. So we calculate that mask and do
111371815ce7SRobert Mustacchi  * our same xor dance.
111471815ce7SRobert Mustacchi  *
111571815ce7SRobert Mustacchi  * There is one hash for each rank multiplication bit and chip-select bit. The
111671815ce7SRobert Mustacchi  * number of rank multiplication bits is given to us. The number of chip-select
111771815ce7SRobert Mustacchi  * bits is fixed, it's simply two because there are four base/mask registers and
111871815ce7SRobert Mustacchi  * logical chip-selects in a given UMC channel. The chip-select on some DDR5
111971815ce7SRobert Mustacchi  * platforms has a secondary exclusive-OR hash that can be applied. As this only
112071815ce7SRobert Mustacchi  * exists in some families, for any where it does exist, we seed it to be zero
112171815ce7SRobert Mustacchi  * so that it becomes a no-op.
112271815ce7SRobert Mustacchi  *
112371815ce7SRobert Mustacchi  * -----------
112471815ce7SRobert Mustacchi  * Future Work
112571815ce7SRobert Mustacchi  * -----------
112671815ce7SRobert Mustacchi  *
112771815ce7SRobert Mustacchi  * As the road goes ever on and on, down from the door where it began, there are
112871815ce7SRobert Mustacchi  * still some stops on the journey for this driver. In particular, here are the
112971815ce7SRobert Mustacchi  * major open areas that could be implemented to extend what this can do:
113071815ce7SRobert Mustacchi  *
113171815ce7SRobert Mustacchi  *   o The ability to transform a normalized channel address back to a system
113271815ce7SRobert Mustacchi  *     address. This is required for MCA/MCA-X error handling as those generally
113371815ce7SRobert Mustacchi  *     work in terms of channel addresses.
113471815ce7SRobert Mustacchi  *   o Integrating with the MCA/MCA-X error handling paths so that way we can
113571815ce7SRobert Mustacchi  *     take correct action in the face of ECC errors and allowing recovery from
113671815ce7SRobert Mustacchi  *     uncorrectable errors.
113771815ce7SRobert Mustacchi  *   o Providing memory controller information to FMA so that way it can opt to
113871815ce7SRobert Mustacchi  *     do predictive failure or give us more information about what is fault
113971815ce7SRobert Mustacchi  *     with ECC errors.
114071815ce7SRobert Mustacchi  *   o Figuring out if we will get MCEs for privilged address decoding and if so
114171815ce7SRobert Mustacchi  *     mapping those back to system addresses and related.
114271815ce7SRobert Mustacchi  *   o 3DS RDIMMs likely will need a little bit of work to ensure we're handling
114371815ce7SRobert Mustacchi  *     the resulting combination of the RM bits and CS and reporting it
114471815ce7SRobert Mustacchi  *     intelligently.
114571815ce7SRobert Mustacchi  */
114671815ce7SRobert Mustacchi 
114771815ce7SRobert Mustacchi #include <sys/types.h>
114871815ce7SRobert Mustacchi #include <sys/file.h>
114971815ce7SRobert Mustacchi #include <sys/errno.h>
115071815ce7SRobert Mustacchi #include <sys/open.h>
115171815ce7SRobert Mustacchi #include <sys/cred.h>
115271815ce7SRobert Mustacchi #include <sys/ddi.h>
115371815ce7SRobert Mustacchi #include <sys/sunddi.h>
115471815ce7SRobert Mustacchi #include <sys/stat.h>
115571815ce7SRobert Mustacchi #include <sys/conf.h>
115671815ce7SRobert Mustacchi #include <sys/devops.h>
115771815ce7SRobert Mustacchi #include <sys/cmn_err.h>
115871815ce7SRobert Mustacchi #include <sys/x86_archext.h>
115971815ce7SRobert Mustacchi #include <sys/sysmacros.h>
116071815ce7SRobert Mustacchi #include <sys/mc.h>
116171815ce7SRobert Mustacchi 
116271815ce7SRobert Mustacchi #include <zen_umc.h>
116371815ce7SRobert Mustacchi #include <sys/amdzen/df.h>
116471815ce7SRobert Mustacchi #include <sys/amdzen/umc.h>
116571815ce7SRobert Mustacchi 
116671815ce7SRobert Mustacchi static zen_umc_t *zen_umc;
116771815ce7SRobert Mustacchi 
116871815ce7SRobert Mustacchi /*
116971815ce7SRobert Mustacchi  * Per-CPU family information that describes the set of capabilities that they
117071815ce7SRobert Mustacchi  * implement. When adding support for new CPU generations, you must go through
117171815ce7SRobert Mustacchi  * what documentation you have and validate these. The best bet is to find a
117271815ce7SRobert Mustacchi  * similar processor and see what has changed. Unfortunately, there really isn't
117371815ce7SRobert Mustacchi  * a substitute for just basically checking every register. The family name
117471815ce7SRobert Mustacchi  * comes from the amdzen_c_family(). One additional note for new CPUs, if our
117571815ce7SRobert Mustacchi  * parent amdzen nexus driver does not attach (because the DF has changed PCI
117671815ce7SRobert Mustacchi  * IDs or more), then just adding something here will not be sufficient to make
117771815ce7SRobert Mustacchi  * it work.
117871815ce7SRobert Mustacchi  */
117971815ce7SRobert Mustacchi static const zen_umc_fam_data_t zen_umc_fam_data[] = {
118071815ce7SRobert Mustacchi 	{
118122e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_NAPLES,
118271815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
118371815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
118471815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
118571815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
118671815ce7SRobert Mustacchi 	}, {
118722e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_HYGON_DHYANA,
118871815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
118971815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
119071815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
119171815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
119271815ce7SRobert Mustacchi 	}, {
119322e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_DALI,
119471815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
119571815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
119671815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4_APU,
119771815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
119871815ce7SRobert Mustacchi 	}, {
119922e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_ROME,
120071815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NP2 | ZEN_UMC_FAM_F_NORM_HASH |
120171815ce7SRobert Mustacchi 		    ZEN_UMC_FAM_F_UMC_HASH,
120271815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
120371815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
120471815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
120571815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
120671815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
120771815ce7SRobert Mustacchi 	}, {
120822e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_RENOIR,
120971815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH,
121071815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
121171815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
121271815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4_APU,
121371815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_PC |
121471815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
121571815ce7SRobert Mustacchi 	}, {
121622e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_MATISSE,
121771815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH | ZEN_UMC_FAM_F_UMC_HASH,
121871815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
121971815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
122071815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
122171815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
122271815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
122371815ce7SRobert Mustacchi 	}, {
122422e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_VAN_GOGH,
122571815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH,
122671815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
122771815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
1228*0dd92943SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_HYBRID_LPDDR5,
122971815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
123071815ce7SRobert Mustacchi 	}, {
123122e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_MENDOCINO,
123271815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH,
123371815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
123471815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
1235*0dd92943SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_HYBRID_LPDDR5,
123671815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
123771815ce7SRobert Mustacchi 	}, {
123822e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_MILAN,
123971815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_TARG_REMAP | ZEN_UMC_FAM_F_NP2 |
124071815ce7SRobert Mustacchi 		    ZEN_UMC_FAM_F_NORM_HASH | ZEN_UMC_FAM_F_UMC_HASH,
124171815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
124271815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
124371815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
124471815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
124571815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
124671815ce7SRobert Mustacchi 	}, {
124722e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_GENOA,
124871815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_TARG_REMAP |
124971815ce7SRobert Mustacchi 		    ZEN_UMC_FAM_F_UMC_HASH | ZEN_UMC_FAM_F_UMC_EADDR |
125071815ce7SRobert Mustacchi 		    ZEN_UMC_FAM_F_CS_XOR,
125171815ce7SRobert Mustacchi 		.zufd_dram_nrules = 20,
125271815ce7SRobert Mustacchi 		.zufd_cs_nrules = 4,
125371815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR5,
125471815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
125571815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_PC | UMC_CHAN_HASH_F_CS
125671815ce7SRobert Mustacchi 	}, {
125722e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_VERMEER,
125871815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH | ZEN_UMC_FAM_F_UMC_HASH,
125971815ce7SRobert Mustacchi 		.zufd_dram_nrules = 16,
126071815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
126171815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4,
126271815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
126371815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS,
126471815ce7SRobert Mustacchi 	}, {
126522e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_REMBRANDT,
126671815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH,
126771815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
126871815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
126971815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR5_APU,
127071815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_CS
127171815ce7SRobert Mustacchi 	}, {
127222e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_CEZANNE,
127371815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_NORM_HASH,
127471815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
127571815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
127671815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR4_APU,
127771815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_PC |
127871815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
127971815ce7SRobert Mustacchi 	}, {
128022e4c3acSKeith M Wesolowski 		.zufd_family = X86_PF_AMD_RAPHAEL,
128171815ce7SRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_TARG_REMAP | ZEN_UMC_FAM_F_CS_XOR,
128271815ce7SRobert Mustacchi 		.zufd_dram_nrules = 2,
128371815ce7SRobert Mustacchi 		.zufd_cs_nrules = 2,
128471815ce7SRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR5,
128571815ce7SRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_PC |
128671815ce7SRobert Mustacchi 		    UMC_CHAN_HASH_F_CS
1287e6f89c3aSRobert Mustacchi 	}, {
1288e6f89c3aSRobert Mustacchi 		.zufd_family = X86_PF_AMD_BERGAMO,
1289e6f89c3aSRobert Mustacchi 		.zufd_flags = ZEN_UMC_FAM_F_TARG_REMAP |
1290e6f89c3aSRobert Mustacchi 		    ZEN_UMC_FAM_F_UMC_HASH | ZEN_UMC_FAM_F_UMC_EADDR |
1291e6f89c3aSRobert Mustacchi 		    ZEN_UMC_FAM_F_CS_XOR,
1292e6f89c3aSRobert Mustacchi 		.zufd_dram_nrules = 20,
1293e6f89c3aSRobert Mustacchi 		.zufd_cs_nrules = 4,
1294e6f89c3aSRobert Mustacchi 		.zufd_umc_style = ZEN_UMC_UMC_S_DDR5,
1295e6f89c3aSRobert Mustacchi 		.zufd_chan_hash = UMC_CHAN_HASH_F_BANK | UMC_CHAN_HASH_F_RM |
1296e6f89c3aSRobert Mustacchi 		    UMC_CHAN_HASH_F_PC | UMC_CHAN_HASH_F_CS
129771815ce7SRobert Mustacchi 	}
129871815ce7SRobert Mustacchi };
129971815ce7SRobert Mustacchi 
1300*0dd92943SRobert Mustacchi /*
1301*0dd92943SRobert Mustacchi  * We use this for the DDR4 and Hybrid DDR4 + LPDDR5 tables to map between the
1302*0dd92943SRobert Mustacchi  * specific enumerated speeds which are encoded values and the corresponding
1303*0dd92943SRobert Mustacchi  * memory clock and speed. For all DDR4 and LPDDR5 items we assume a a 1:2 ratio
1304*0dd92943SRobert Mustacchi  * between them. This is not used for the pure DDR5 / LPDDR5 entries because of
1305*0dd92943SRobert Mustacchi  * how the register just encodes the raw value in MHz.
1306*0dd92943SRobert Mustacchi  */
1307*0dd92943SRobert Mustacchi typedef struct zen_umc_freq_map {
1308*0dd92943SRobert Mustacchi 	uint32_t zufm_reg;
1309*0dd92943SRobert Mustacchi 	uint32_t zufm_mhz;
1310*0dd92943SRobert Mustacchi 	uint32_t zufm_mts2;
1311*0dd92943SRobert Mustacchi 	uint32_t zufm_mts4;
1312*0dd92943SRobert Mustacchi } zen_umc_freq_map_t;
1313*0dd92943SRobert Mustacchi 
1314*0dd92943SRobert Mustacchi static const zen_umc_freq_map_t zen_umc_ddr4_map[] = {
1315*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_667, 667, 1333, 0 },
1316*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_800, 800, 1600, 0 },
1317*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_933, 933, 1866, 0 },
1318*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_1067, 1067, 2133, 0 },
1319*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_1200, 1200, 2400, 0 },
1320*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_1333, 1333, 2666, 0 },
1321*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_1467, 1467, 2933, 0 },
1322*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_DDR4_MEMCLK_1600, 1600, 3200, 0 }
1323*0dd92943SRobert Mustacchi };
1324*0dd92943SRobert Mustacchi 
1325*0dd92943SRobert Mustacchi static const zen_umc_freq_map_t zen_umc_lpddr5_map[] = {
1326*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_333, 333, 667, 1333 },
1327*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_400, 400, 800, 1600 },
1328*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_533, 533, 1066, 2133 },
1329*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_687, 687, 1375, 2750 },
1330*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_750, 750, 1500, 3000 },
1331*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_800, 800, 1600, 3200 },
1332*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_933, 933, 1866, 3733 },
1333*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_1066, 1066, 2133, 4267 },
1334*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_1200, 1200, 2400, 4800 },
1335*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_1375, 1375, 2750, 5500 },
1336*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_1500, 1500, 3000, 6000 },
1337*0dd92943SRobert Mustacchi 	{ UMC_DRAMCFG_HYB_MEMCLK_1600, 1600, 3200, 6400 }
1338*0dd92943SRobert Mustacchi 
1339*0dd92943SRobert Mustacchi };
1340*0dd92943SRobert Mustacchi 
134171815ce7SRobert Mustacchi static boolean_t
zen_umc_identify(zen_umc_t * umc)134271815ce7SRobert Mustacchi zen_umc_identify(zen_umc_t *umc)
134371815ce7SRobert Mustacchi {
134471815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ARRAY_SIZE(zen_umc_fam_data); i++) {
134571815ce7SRobert Mustacchi 		if (zen_umc_fam_data[i].zufd_family == umc->umc_family) {
134671815ce7SRobert Mustacchi 			umc->umc_fdata = &zen_umc_fam_data[i];
134771815ce7SRobert Mustacchi 			return (B_TRUE);
134871815ce7SRobert Mustacchi 		}
134971815ce7SRobert Mustacchi 	}
135071815ce7SRobert Mustacchi 
135171815ce7SRobert Mustacchi 	return (B_FALSE);
135271815ce7SRobert Mustacchi }
135371815ce7SRobert Mustacchi 
135471815ce7SRobert Mustacchi /*
135571815ce7SRobert Mustacchi  * This operates on DFv2, DFv3, and DFv3.5 DRAM rules, which generally speaking
135671815ce7SRobert Mustacchi  * are in similar register locations and meanings, but the size of bits in
135771815ce7SRobert Mustacchi  * memory is not consistent.
135871815ce7SRobert Mustacchi  */
135971815ce7SRobert Mustacchi static int
zen_umc_read_dram_rule_df_23(zen_umc_t * umc,const uint_t dfno,const uint_t inst,const uint_t ruleno,df_dram_rule_t * rule)136071815ce7SRobert Mustacchi zen_umc_read_dram_rule_df_23(zen_umc_t *umc, const uint_t dfno,
136171815ce7SRobert Mustacchi     const uint_t inst, const uint_t ruleno, df_dram_rule_t *rule)
136271815ce7SRobert Mustacchi {
136371815ce7SRobert Mustacchi 	int ret;
136471815ce7SRobert Mustacchi 	uint32_t base, limit;
136571815ce7SRobert Mustacchi 	uint64_t dbase, dlimit;
136671815ce7SRobert Mustacchi 	uint16_t addr_ileave, chan_ileave, sock_ileave, die_ileave, dest;
136771815ce7SRobert Mustacchi 	boolean_t hash = B_FALSE;
136871815ce7SRobert Mustacchi 	zen_umc_df_t *df = &umc->umc_dfs[dfno];
136971815ce7SRobert Mustacchi 
137071815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_BASE_V2(ruleno),
137171815ce7SRobert Mustacchi 	    &base)) != 0) {
137271815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM base "
137371815ce7SRobert Mustacchi 		    "register %u on 0x%x/0x%x: %d", ruleno, dfno, inst, ret);
137471815ce7SRobert Mustacchi 		return (ret);
137571815ce7SRobert Mustacchi 	}
137671815ce7SRobert Mustacchi 
137771815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_LIMIT_V2(ruleno),
137871815ce7SRobert Mustacchi 	    &limit)) != 0) {
137971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM limit "
138071815ce7SRobert Mustacchi 		    "register %u on 0x%x/0x%x: %d", ruleno, dfno, inst, ret);
138171815ce7SRobert Mustacchi 		return (ret);
138271815ce7SRobert Mustacchi 	}
138371815ce7SRobert Mustacchi 
138471815ce7SRobert Mustacchi 
138571815ce7SRobert Mustacchi 	rule->ddr_raw_base = base;
138671815ce7SRobert Mustacchi 	rule->ddr_raw_limit = limit;
138771815ce7SRobert Mustacchi 	rule->ddr_raw_ileave = rule->ddr_raw_ctrl = 0;
138871815ce7SRobert Mustacchi 
138971815ce7SRobert Mustacchi 	if (!DF_DRAM_BASE_V2_GET_VALID(base)) {
139071815ce7SRobert Mustacchi 		return (0);
139171815ce7SRobert Mustacchi 	}
139271815ce7SRobert Mustacchi 
139371815ce7SRobert Mustacchi 	/*
139471815ce7SRobert Mustacchi 	 * Extract all values from the registers and then normalize. While there
139571815ce7SRobert Mustacchi 	 * are often different bit patterns for the values, the interpretation
139671815ce7SRobert Mustacchi 	 * is the same across all the Zen 1-3 parts. That is while which bits
139771815ce7SRobert Mustacchi 	 * may be used for say channel interleave vary, the values of them are
139871815ce7SRobert Mustacchi 	 * consistent.
139971815ce7SRobert Mustacchi 	 */
140071815ce7SRobert Mustacchi 	rule->ddr_flags |= DF_DRAM_F_VALID;
140171815ce7SRobert Mustacchi 	if (DF_DRAM_BASE_V2_GET_HOLE_EN(base)) {
140271815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_HOLE;
140371815ce7SRobert Mustacchi 	}
140471815ce7SRobert Mustacchi 
140571815ce7SRobert Mustacchi 	dbase = DF_DRAM_BASE_V2_GET_BASE(base);
140671815ce7SRobert Mustacchi 	dlimit = DF_DRAM_LIMIT_V2_GET_LIMIT(limit);
140771815ce7SRobert Mustacchi 	switch (umc->umc_df_rev) {
140871815ce7SRobert Mustacchi 	case DF_REV_2:
140971815ce7SRobert Mustacchi 		addr_ileave = DF_DRAM_BASE_V2_GET_ILV_ADDR(base);
141071815ce7SRobert Mustacchi 		chan_ileave = DF_DRAM_BASE_V2_GET_ILV_CHAN(base);
141171815ce7SRobert Mustacchi 		die_ileave = DF_DRAM_LIMIT_V2_GET_ILV_DIE(limit);
141271815ce7SRobert Mustacchi 		sock_ileave = DF_DRAM_LIMIT_V2_GET_ILV_SOCK(limit);
141371815ce7SRobert Mustacchi 		dest = DF_DRAM_LIMIT_V2_GET_DEST_ID(limit);
141471815ce7SRobert Mustacchi 		break;
141571815ce7SRobert Mustacchi 	case DF_REV_3:
141671815ce7SRobert Mustacchi 		addr_ileave = DF_DRAM_BASE_V3_GET_ILV_ADDR(base);
141771815ce7SRobert Mustacchi 		sock_ileave = DF_DRAM_BASE_V3_GET_ILV_SOCK(base);
141871815ce7SRobert Mustacchi 		die_ileave = DF_DRAM_BASE_V3_GET_ILV_DIE(base);
141971815ce7SRobert Mustacchi 		chan_ileave = DF_DRAM_BASE_V3_GET_ILV_CHAN(base);
142071815ce7SRobert Mustacchi 		dest = DF_DRAM_LIMIT_V3_GET_DEST_ID(limit);
142171815ce7SRobert Mustacchi 		break;
142271815ce7SRobert Mustacchi 	case DF_REV_3P5:
142371815ce7SRobert Mustacchi 		addr_ileave = DF_DRAM_BASE_V3P5_GET_ILV_ADDR(base);
142471815ce7SRobert Mustacchi 		sock_ileave = DF_DRAM_BASE_V3P5_GET_ILV_SOCK(base);
142571815ce7SRobert Mustacchi 		die_ileave = DF_DRAM_BASE_V3P5_GET_ILV_DIE(base);
142671815ce7SRobert Mustacchi 		chan_ileave = DF_DRAM_BASE_V3P5_GET_ILV_CHAN(base);
142771815ce7SRobert Mustacchi 		dest = DF_DRAM_LIMIT_V3P5_GET_DEST_ID(limit);
142871815ce7SRobert Mustacchi 		break;
142971815ce7SRobert Mustacchi 	default:
143071815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered unsupported "
143171815ce7SRobert Mustacchi 		    "DF revision processing DRAM rules: 0x%x", umc->umc_df_rev);
143271815ce7SRobert Mustacchi 		return (-1);
143371815ce7SRobert Mustacchi 	}
143471815ce7SRobert Mustacchi 
143571815ce7SRobert Mustacchi 	rule->ddr_base = dbase << DF_DRAM_BASE_V2_BASE_SHIFT;
143671815ce7SRobert Mustacchi 	rule->ddr_sock_ileave_bits = sock_ileave;
143771815ce7SRobert Mustacchi 	rule->ddr_die_ileave_bits = die_ileave;
143871815ce7SRobert Mustacchi 	switch (addr_ileave) {
143971815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_8:
144071815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_9:
144171815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_10:
144271815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_11:
144371815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_12:
144471815ce7SRobert Mustacchi 		break;
144571815ce7SRobert Mustacchi 	default:
144671815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered invalid address "
144771815ce7SRobert Mustacchi 		    "interleave on rule %u, df/inst 0x%x/0x%x: 0x%x", ruleno,
144871815ce7SRobert Mustacchi 		    dfno, inst, addr_ileave);
144971815ce7SRobert Mustacchi 		return (EINVAL);
145071815ce7SRobert Mustacchi 	}
145171815ce7SRobert Mustacchi 	rule->ddr_addr_start = DF_DRAM_ILV_ADDR_BASE + addr_ileave;
145271815ce7SRobert Mustacchi 
145371815ce7SRobert Mustacchi 	switch (chan_ileave) {
145471815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_1:
145571815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_1CH;
145671815ce7SRobert Mustacchi 		break;
145771815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_2:
145871815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_2CH;
145971815ce7SRobert Mustacchi 		break;
146071815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_4:
146171815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_4CH;
146271815ce7SRobert Mustacchi 		break;
146371815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_8:
146471815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_8CH;
146571815ce7SRobert Mustacchi 		break;
146671815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_6:
146771815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_6CH;
146871815ce7SRobert Mustacchi 		break;
146971815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_COD4_2:
147071815ce7SRobert Mustacchi 		hash = B_TRUE;
147171815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_COD4_2CH;
147271815ce7SRobert Mustacchi 		break;
147371815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_COD2_4:
147471815ce7SRobert Mustacchi 		hash = B_TRUE;
147571815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_COD2_4CH;
147671815ce7SRobert Mustacchi 		break;
147771815ce7SRobert Mustacchi 	case DF_DRAM_BASE_V2_ILV_CHAN_COD1_8:
147871815ce7SRobert Mustacchi 		hash = B_TRUE;
147971815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_COD1_8CH;
148071815ce7SRobert Mustacchi 		break;
148171815ce7SRobert Mustacchi 	default:
148271815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered invalid channel "
148371815ce7SRobert Mustacchi 		    "interleave on rule %u, df/inst 0x%x/0x%x: 0x%x", ruleno,
148471815ce7SRobert Mustacchi 		    dfno, inst, chan_ileave);
148571815ce7SRobert Mustacchi 		return (EINVAL);
148671815ce7SRobert Mustacchi 	}
148771815ce7SRobert Mustacchi 
148871815ce7SRobert Mustacchi 	/*
148971815ce7SRobert Mustacchi 	 * If hashing is enabled, note which hashing rules apply to this
149071815ce7SRobert Mustacchi 	 * address. This is done to smooth over the differences between DFv3 and
149171815ce7SRobert Mustacchi 	 * DFv4, where the flags are in the rules themselves in the latter, but
149271815ce7SRobert Mustacchi 	 * global today.
149371815ce7SRobert Mustacchi 	 */
149471815ce7SRobert Mustacchi 	if (hash) {
149571815ce7SRobert Mustacchi 		if ((df->zud_flags & ZEN_UMC_DF_F_HASH_16_18) != 0) {
149671815ce7SRobert Mustacchi 			rule->ddr_flags |= DF_DRAM_F_HASH_16_18;
149771815ce7SRobert Mustacchi 		}
149871815ce7SRobert Mustacchi 
149971815ce7SRobert Mustacchi 		if ((df->zud_flags & ZEN_UMC_DF_F_HASH_21_23) != 0) {
150071815ce7SRobert Mustacchi 			rule->ddr_flags |= DF_DRAM_F_HASH_21_23;
150171815ce7SRobert Mustacchi 		}
150271815ce7SRobert Mustacchi 
150371815ce7SRobert Mustacchi 		if ((df->zud_flags & ZEN_UMC_DF_F_HASH_30_32) != 0) {
150471815ce7SRobert Mustacchi 			rule->ddr_flags |= DF_DRAM_F_HASH_30_32;
150571815ce7SRobert Mustacchi 		}
150671815ce7SRobert Mustacchi 	}
150771815ce7SRobert Mustacchi 
150871815ce7SRobert Mustacchi 	/*
150971815ce7SRobert Mustacchi 	 * While DFv4 makes remapping explicit, it is basically always enabled
151071815ce7SRobert Mustacchi 	 * and used on supported platforms prior to that point. So flag such
151171815ce7SRobert Mustacchi 	 * supported platforms as ones that need to do this. On those systems
151271815ce7SRobert Mustacchi 	 * there is only one set of remap rules for an entire DF that are
151371815ce7SRobert Mustacchi 	 * determined based on the target socket. To indicate that we use the
151471815ce7SRobert Mustacchi 	 * DF_DRAM_F_REMAP_SOCK flag below and skip setting a remap target.
151571815ce7SRobert Mustacchi 	 */
151671815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_TARG_REMAP) != 0) {
151771815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_REMAP_EN | DF_DRAM_F_REMAP_SOCK;
151871815ce7SRobert Mustacchi 	}
151971815ce7SRobert Mustacchi 
152071815ce7SRobert Mustacchi 	rule->ddr_limit = (dlimit << DF_DRAM_LIMIT_V2_LIMIT_SHIFT) +
152171815ce7SRobert Mustacchi 	    DF_DRAM_LIMIT_V2_LIMIT_EXCL;
152271815ce7SRobert Mustacchi 	rule->ddr_dest_fabid = dest;
152371815ce7SRobert Mustacchi 
152471815ce7SRobert Mustacchi 	return (0);
152571815ce7SRobert Mustacchi }
152671815ce7SRobert Mustacchi 
152771815ce7SRobert Mustacchi static int
zen_umc_read_dram_rule_df_4(zen_umc_t * umc,const uint_t dfno,const uint_t inst,const uint_t ruleno,df_dram_rule_t * rule)152871815ce7SRobert Mustacchi zen_umc_read_dram_rule_df_4(zen_umc_t *umc, const uint_t dfno,
152971815ce7SRobert Mustacchi     const uint_t inst, const uint_t ruleno, df_dram_rule_t *rule)
153071815ce7SRobert Mustacchi {
153171815ce7SRobert Mustacchi 	int ret;
153271815ce7SRobert Mustacchi 	uint16_t addr_ileave;
153371815ce7SRobert Mustacchi 	uint32_t base, limit, ilv, ctl;
153471815ce7SRobert Mustacchi 
153571815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_BASE_V4(ruleno),
153671815ce7SRobert Mustacchi 	    &base)) != 0) {
153771815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM base "
153871815ce7SRobert Mustacchi 		    "register %u on 0x%x/0x%x: %d", ruleno, dfno, inst, ret);
153971815ce7SRobert Mustacchi 		return (ret);
154071815ce7SRobert Mustacchi 	}
154171815ce7SRobert Mustacchi 
154271815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_LIMIT_V4(ruleno),
154371815ce7SRobert Mustacchi 	    &limit)) != 0) {
154471815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM limit "
154571815ce7SRobert Mustacchi 		    "register %u on 0x%x/0x%x: %d", ruleno, dfno, inst, ret);
154671815ce7SRobert Mustacchi 		return (ret);
154771815ce7SRobert Mustacchi 	}
154871815ce7SRobert Mustacchi 
154971815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_ILV_V4(ruleno),
155071815ce7SRobert Mustacchi 	    &ilv)) != 0) {
155171815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM "
155271815ce7SRobert Mustacchi 		    "interleave register %u on 0x%x/0x%x: %d", ruleno, dfno,
155371815ce7SRobert Mustacchi 		    inst, ret);
155471815ce7SRobert Mustacchi 		return (ret);
155571815ce7SRobert Mustacchi 	}
155671815ce7SRobert Mustacchi 
155771815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, inst, DF_DRAM_CTL_V4(ruleno),
155871815ce7SRobert Mustacchi 	    &ctl)) != 0) {
155971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM control "
156071815ce7SRobert Mustacchi 		    "register %u on 0x%x/0x%x: %d", ruleno, dfno, inst, ret);
156171815ce7SRobert Mustacchi 		return (ret);
156271815ce7SRobert Mustacchi 	}
156371815ce7SRobert Mustacchi 
156471815ce7SRobert Mustacchi 	rule->ddr_raw_base = base;
156571815ce7SRobert Mustacchi 	rule->ddr_raw_limit = limit;
156671815ce7SRobert Mustacchi 	rule->ddr_raw_ileave = ilv;
156771815ce7SRobert Mustacchi 	rule->ddr_raw_ctrl = ctl;
156871815ce7SRobert Mustacchi 
156971815ce7SRobert Mustacchi 	if (!DF_DRAM_CTL_V4_GET_VALID(ctl)) {
157071815ce7SRobert Mustacchi 		return (0);
157171815ce7SRobert Mustacchi 	}
157271815ce7SRobert Mustacchi 
157371815ce7SRobert Mustacchi 	rule->ddr_flags |= DF_DRAM_F_VALID;
157471815ce7SRobert Mustacchi 	rule->ddr_base = DF_DRAM_BASE_V4_GET_ADDR(base);
157571815ce7SRobert Mustacchi 	rule->ddr_base = rule->ddr_base << DF_DRAM_BASE_V4_BASE_SHIFT;
157671815ce7SRobert Mustacchi 	rule->ddr_limit = DF_DRAM_LIMIT_V4_GET_ADDR(limit);
157771815ce7SRobert Mustacchi 	rule->ddr_limit = (rule->ddr_limit << DF_DRAM_LIMIT_V4_LIMIT_SHIFT) +
157871815ce7SRobert Mustacchi 	    DF_DRAM_LIMIT_V4_LIMIT_EXCL;
157971815ce7SRobert Mustacchi 	rule->ddr_dest_fabid = DF_DRAM_CTL_V4_GET_DEST_ID(ctl);
158071815ce7SRobert Mustacchi 
158171815ce7SRobert Mustacchi 	if (DF_DRAM_CTL_V4_GET_HASH_1G(ctl) != 0) {
158271815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_HASH_30_32;
158371815ce7SRobert Mustacchi 	}
158471815ce7SRobert Mustacchi 
158571815ce7SRobert Mustacchi 	if (DF_DRAM_CTL_V4_GET_HASH_2M(ctl) != 0) {
158671815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_HASH_21_23;
158771815ce7SRobert Mustacchi 	}
158871815ce7SRobert Mustacchi 
158971815ce7SRobert Mustacchi 	if (DF_DRAM_CTL_V4_GET_HASH_64K(ctl) != 0) {
159071815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_HASH_16_18;
159171815ce7SRobert Mustacchi 	}
159271815ce7SRobert Mustacchi 
159371815ce7SRobert Mustacchi 	if (DF_DRAM_CTL_V4_GET_REMAP_EN(ctl) != 0) {
159471815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_REMAP_EN;
159571815ce7SRobert Mustacchi 		rule->ddr_remap_ent = DF_DRAM_CTL_V4_GET_REMAP_SEL(ctl);
159671815ce7SRobert Mustacchi 	}
159771815ce7SRobert Mustacchi 
159871815ce7SRobert Mustacchi 	if (DF_DRAM_CTL_V4_GET_HOLE_EN(ctl) != 0) {
159971815ce7SRobert Mustacchi 		rule->ddr_flags |= DF_DRAM_F_HOLE;
160071815ce7SRobert Mustacchi 	}
160171815ce7SRobert Mustacchi 
160271815ce7SRobert Mustacchi 	rule->ddr_sock_ileave_bits = DF_DRAM_ILV_V4_GET_SOCK(ilv);
160371815ce7SRobert Mustacchi 	rule->ddr_die_ileave_bits = DF_DRAM_ILV_V4_GET_DIE(ilv);
160471815ce7SRobert Mustacchi 	switch (DF_DRAM_ILV_V4_GET_CHAN(ilv)) {
160571815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_1:
160671815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_1CH;
160771815ce7SRobert Mustacchi 		break;
160871815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_2:
160971815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_2CH;
161071815ce7SRobert Mustacchi 		break;
161171815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_4:
161271815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_4CH;
161371815ce7SRobert Mustacchi 		break;
161471815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_8:
161571815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_8CH;
161671815ce7SRobert Mustacchi 		break;
161771815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_16:
161871815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_16CH;
161971815ce7SRobert Mustacchi 		break;
162071815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_32:
162171815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_32CH;
162271815ce7SRobert Mustacchi 		break;
162371815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS4_2CH:
162471815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS4_2CH;
162571815ce7SRobert Mustacchi 		break;
162671815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS2_4CH:
162771815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_COD2_4CH;
162871815ce7SRobert Mustacchi 		break;
162971815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS1_8CH:
163071815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS1_8CH;
163171815ce7SRobert Mustacchi 		break;
163271815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS4_3CH:
163371815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS4_3CH;
163471815ce7SRobert Mustacchi 		break;
163571815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS2_6CH:
163671815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS2_6CH;
163771815ce7SRobert Mustacchi 		break;
163871815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS1_12CH:
163971815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS1_12CH;
164071815ce7SRobert Mustacchi 		break;
164171815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS2_5CH:
164271815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS2_5CH;
164371815ce7SRobert Mustacchi 		break;
164471815ce7SRobert Mustacchi 	case DF_DRAM_ILV_V4_CHAN_NPS1_10CH:
164571815ce7SRobert Mustacchi 		rule->ddr_chan_ileave = DF_CHAN_ILEAVE_NPS1_10CH;
164671815ce7SRobert Mustacchi 		break;
164771815ce7SRobert Mustacchi 	default:
164871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered invalid channel "
164971815ce7SRobert Mustacchi 		    "interleave on rule %u, df/inst 0x%x/0x%x: 0x%x", ruleno,
165071815ce7SRobert Mustacchi 		    dfno, inst, DF_DRAM_ILV_V4_GET_CHAN(ilv));
165171815ce7SRobert Mustacchi 
165271815ce7SRobert Mustacchi 		break;
165371815ce7SRobert Mustacchi 	}
165471815ce7SRobert Mustacchi 
165571815ce7SRobert Mustacchi 	addr_ileave = DF_DRAM_ILV_V4_GET_ADDR(ilv);
165671815ce7SRobert Mustacchi 	switch (addr_ileave) {
165771815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_8:
165871815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_9:
165971815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_10:
166071815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_11:
166171815ce7SRobert Mustacchi 	case DF_DRAM_ILV_ADDR_12:
166271815ce7SRobert Mustacchi 		break;
166371815ce7SRobert Mustacchi 	default:
166471815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered invalid address "
166571815ce7SRobert Mustacchi 		    "interleave on rule %u, df/inst 0x%x/0x%x: 0x%x", ruleno,
166671815ce7SRobert Mustacchi 		    dfno, inst, addr_ileave);
166771815ce7SRobert Mustacchi 		return (EINVAL);
166871815ce7SRobert Mustacchi 	}
166971815ce7SRobert Mustacchi 	rule->ddr_addr_start = DF_DRAM_ILV_ADDR_BASE + addr_ileave;
167071815ce7SRobert Mustacchi 
167171815ce7SRobert Mustacchi 	return (0);
167271815ce7SRobert Mustacchi }
167371815ce7SRobert Mustacchi 
167471815ce7SRobert Mustacchi static int
zen_umc_read_dram_rule(zen_umc_t * umc,const uint_t dfno,const uint_t instid,const uint_t ruleno,df_dram_rule_t * rule)167571815ce7SRobert Mustacchi zen_umc_read_dram_rule(zen_umc_t *umc, const uint_t dfno, const uint_t instid,
167671815ce7SRobert Mustacchi     const uint_t ruleno, df_dram_rule_t *rule)
167771815ce7SRobert Mustacchi {
167871815ce7SRobert Mustacchi 	int ret;
167971815ce7SRobert Mustacchi 
168071815ce7SRobert Mustacchi 	switch (umc->umc_df_rev) {
168171815ce7SRobert Mustacchi 	case DF_REV_2:
168271815ce7SRobert Mustacchi 	case DF_REV_3:
168371815ce7SRobert Mustacchi 	case DF_REV_3P5:
168471815ce7SRobert Mustacchi 		ret = zen_umc_read_dram_rule_df_23(umc, dfno, instid, ruleno,
168571815ce7SRobert Mustacchi 		    rule);
168671815ce7SRobert Mustacchi 		break;
168771815ce7SRobert Mustacchi 	case DF_REV_4:
168871815ce7SRobert Mustacchi 		ret = zen_umc_read_dram_rule_df_4(umc, dfno, instid, ruleno,
168971815ce7SRobert Mustacchi 		    rule);
169071815ce7SRobert Mustacchi 		break;
169171815ce7SRobert Mustacchi 	default:
169271815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered unsupported "
169371815ce7SRobert Mustacchi 		    "DF revision processing DRAM rules: 0x%x", umc->umc_df_rev);
169471815ce7SRobert Mustacchi 		return (-1);
169571815ce7SRobert Mustacchi 	}
169671815ce7SRobert Mustacchi 
169771815ce7SRobert Mustacchi 	if (ret != 0) {
169871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM "
169971815ce7SRobert Mustacchi 		    "rule %u on df/inst 0x%x/0x%x: %d", ruleno,
170071815ce7SRobert Mustacchi 		    dfno, instid, ret);
170171815ce7SRobert Mustacchi 		return (-1);
170271815ce7SRobert Mustacchi 	}
170371815ce7SRobert Mustacchi 
170471815ce7SRobert Mustacchi 	return (0);
170571815ce7SRobert Mustacchi }
170671815ce7SRobert Mustacchi 
170771815ce7SRobert Mustacchi static int
zen_umc_read_remap(zen_umc_t * umc,zen_umc_df_t * df,const uint_t instid)170871815ce7SRobert Mustacchi zen_umc_read_remap(zen_umc_t *umc, zen_umc_df_t *df, const uint_t instid)
170971815ce7SRobert Mustacchi {
171071815ce7SRobert Mustacchi 	uint_t nremaps, nents;
171171815ce7SRobert Mustacchi 	uint_t dfno = df->zud_dfno;
171271815ce7SRobert Mustacchi 	const df_reg_def_t milan_remap0[ZEN_UMC_MILAN_CS_NREMAPS] = {
171371815ce7SRobert Mustacchi 	    DF_SKT0_CS_REMAP0_V3, DF_SKT1_CS_REMAP0_V3 };
171471815ce7SRobert Mustacchi 	const df_reg_def_t milan_remap1[ZEN_UMC_MILAN_CS_NREMAPS] = {
171571815ce7SRobert Mustacchi 	    DF_SKT0_CS_REMAP1_V3, DF_SKT1_CS_REMAP1_V3 };
171671815ce7SRobert Mustacchi 	const df_reg_def_t dfv4_remapA[ZEN_UMC_MAX_CS_REMAPS] = {
171771815ce7SRobert Mustacchi 	    DF_CS_REMAP0A_V4, DF_CS_REMAP1A_V4, DF_CS_REMAP2A_V4,
171871815ce7SRobert Mustacchi 	    DF_CS_REMAP3A_V4 };
171971815ce7SRobert Mustacchi 	const df_reg_def_t dfv4_remapB[ZEN_UMC_MAX_CS_REMAPS] = {
172071815ce7SRobert Mustacchi 	    DF_CS_REMAP0B_V4, DF_CS_REMAP1B_V4, DF_CS_REMAP2B_V4,
172171815ce7SRobert Mustacchi 	    DF_CS_REMAP3B_V4 };
172271815ce7SRobert Mustacchi 	const df_reg_def_t *remapA, *remapB;
172371815ce7SRobert Mustacchi 
172471815ce7SRobert Mustacchi 
172571815ce7SRobert Mustacchi 	switch (umc->umc_df_rev) {
172671815ce7SRobert Mustacchi 	case DF_REV_3:
172771815ce7SRobert Mustacchi 		nremaps = ZEN_UMC_MILAN_CS_NREMAPS;
172871815ce7SRobert Mustacchi 		nents = ZEN_UMC_MILAN_REMAP_ENTS;
172971815ce7SRobert Mustacchi 		remapA = milan_remap0;
173071815ce7SRobert Mustacchi 		remapB = milan_remap1;
173171815ce7SRobert Mustacchi 		break;
173271815ce7SRobert Mustacchi 	case DF_REV_4:
173371815ce7SRobert Mustacchi 		nremaps = ZEN_UMC_MAX_CS_REMAPS;
173471815ce7SRobert Mustacchi 		nents = ZEN_UMC_MAX_REMAP_ENTS;
173571815ce7SRobert Mustacchi 		remapA = dfv4_remapA;
173671815ce7SRobert Mustacchi 		remapB = dfv4_remapB;
173771815ce7SRobert Mustacchi 		break;
173871815ce7SRobert Mustacchi 	default:
173971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered unsupported DF "
174071815ce7SRobert Mustacchi 		    "revision processing remap rules: 0x%x", umc->umc_df_rev);
174171815ce7SRobert Mustacchi 		return (-1);
174271815ce7SRobert Mustacchi 	}
174371815ce7SRobert Mustacchi 
174471815ce7SRobert Mustacchi 	df->zud_cs_nremap = nremaps;
174571815ce7SRobert Mustacchi 	for (uint_t i = 0; i < nremaps; i++) {
174671815ce7SRobert Mustacchi 		int ret;
174771815ce7SRobert Mustacchi 		uint32_t rmA, rmB;
174871815ce7SRobert Mustacchi 		zen_umc_cs_remap_t *remap = &df->zud_remap[i];
174971815ce7SRobert Mustacchi 
175071815ce7SRobert Mustacchi 		if ((ret = amdzen_c_df_read32(dfno, instid, remapA[i],
175171815ce7SRobert Mustacchi 		    &rmA)) != 0) {
175271815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!failed to read "
175371815ce7SRobert Mustacchi 			    "df/inst 0x%x/0x%x remap socket %u-0/A: %d", dfno,
175471815ce7SRobert Mustacchi 			    instid, i, ret);
175571815ce7SRobert Mustacchi 			return (-1);
175671815ce7SRobert Mustacchi 		}
175771815ce7SRobert Mustacchi 
175871815ce7SRobert Mustacchi 		if ((ret = amdzen_c_df_read32(dfno, instid, remapB[i],
175971815ce7SRobert Mustacchi 		    &rmB)) != 0) {
176071815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!failed to read "
176171815ce7SRobert Mustacchi 			    "df/inst 0x%x/0x%x remap socket %u-1/B: %d", dfno,
176271815ce7SRobert Mustacchi 			    instid, i, ret);
176371815ce7SRobert Mustacchi 			return (-1);
176471815ce7SRobert Mustacchi 		}
176571815ce7SRobert Mustacchi 
176671815ce7SRobert Mustacchi 		remap->csr_nremaps = nents;
176771815ce7SRobert Mustacchi 		for (uint_t ent = 0; ent < ZEN_UMC_REMAP_PER_REG; ent++) {
176871815ce7SRobert Mustacchi 			uint_t alt = ent + ZEN_UMC_REMAP_PER_REG;
176971815ce7SRobert Mustacchi 			boolean_t do_alt = alt < nents;
177071815ce7SRobert Mustacchi 			remap->csr_remaps[ent] = DF_CS_REMAP_GET_CSX(rmA,
177171815ce7SRobert Mustacchi 			    ent);
177271815ce7SRobert Mustacchi 			if (do_alt) {
177371815ce7SRobert Mustacchi 				remap->csr_remaps[alt] =
177471815ce7SRobert Mustacchi 				    DF_CS_REMAP_GET_CSX(rmB, ent);
177571815ce7SRobert Mustacchi 			}
177671815ce7SRobert Mustacchi 		}
177771815ce7SRobert Mustacchi 	}
177871815ce7SRobert Mustacchi 
177971815ce7SRobert Mustacchi 	return (0);
178071815ce7SRobert Mustacchi }
178171815ce7SRobert Mustacchi 
178271815ce7SRobert Mustacchi /*
178371815ce7SRobert Mustacchi  * Now that we have a CCM, we have several different tasks ahead of us:
178471815ce7SRobert Mustacchi  *
178571815ce7SRobert Mustacchi  *   o Determine whether or not the DRAM hole is valid.
178671815ce7SRobert Mustacchi  *   o Snapshot all of the system address rules and translate them into our
178771815ce7SRobert Mustacchi  *     generic format.
178871815ce7SRobert Mustacchi  *   o Determine if there are any rules to retarget things (currently
178971815ce7SRobert Mustacchi  *     Milan/Genoa).
179071815ce7SRobert Mustacchi  *   o Determine if there are any other hashing rules enabled.
179171815ce7SRobert Mustacchi  *
179271815ce7SRobert Mustacchi  * We only require this from a single CCM as these are currently required to be
179371815ce7SRobert Mustacchi  * the same across all of them.
179471815ce7SRobert Mustacchi  */
179571815ce7SRobert Mustacchi static int
zen_umc_fill_ccm_cb(const uint_t dfno,const uint32_t fabid,const uint32_t instid,void * arg)179671815ce7SRobert Mustacchi zen_umc_fill_ccm_cb(const uint_t dfno, const uint32_t fabid,
179771815ce7SRobert Mustacchi     const uint32_t instid, void *arg)
179871815ce7SRobert Mustacchi {
179971815ce7SRobert Mustacchi 	zen_umc_t *umc = arg;
180071815ce7SRobert Mustacchi 	zen_umc_df_t *df = &umc->umc_dfs[dfno];
180171815ce7SRobert Mustacchi 	df_reg_def_t hole;
180271815ce7SRobert Mustacchi 	int ret;
180371815ce7SRobert Mustacchi 	uint32_t val;
180471815ce7SRobert Mustacchi 
180571815ce7SRobert Mustacchi 	df->zud_dfno = dfno;
180671815ce7SRobert Mustacchi 	df->zud_ccm_inst = instid;
180771815ce7SRobert Mustacchi 
180871815ce7SRobert Mustacchi 	/*
180971815ce7SRobert Mustacchi 	 * First get the DRAM hole. This has the same layout, albeit different
181071815ce7SRobert Mustacchi 	 * registers across our different platforms.
181171815ce7SRobert Mustacchi 	 */
181271815ce7SRobert Mustacchi 	switch (umc->umc_df_rev) {
181371815ce7SRobert Mustacchi 	case DF_REV_2:
181471815ce7SRobert Mustacchi 	case DF_REV_3:
181571815ce7SRobert Mustacchi 	case DF_REV_3P5:
181671815ce7SRobert Mustacchi 		hole = DF_DRAM_HOLE_V2;
181771815ce7SRobert Mustacchi 		break;
181871815ce7SRobert Mustacchi 	case DF_REV_4:
181971815ce7SRobert Mustacchi 		hole = DF_DRAM_HOLE_V4;
182071815ce7SRobert Mustacchi 		break;
182171815ce7SRobert Mustacchi 	default:
182271815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered unsupported "
182371815ce7SRobert Mustacchi 		    "DF version: 0x%x", umc->umc_df_rev);
182471815ce7SRobert Mustacchi 		return (-1);
182571815ce7SRobert Mustacchi 	}
182671815ce7SRobert Mustacchi 
182771815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_read32(dfno, instid, hole, &val)) != 0) {
182871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM Hole: %d",
182971815ce7SRobert Mustacchi 		    ret);
183071815ce7SRobert Mustacchi 		return (-1);
183171815ce7SRobert Mustacchi 	}
183271815ce7SRobert Mustacchi 
183371815ce7SRobert Mustacchi 	df->zud_hole_raw = val;
183471815ce7SRobert Mustacchi 	if (DF_DRAM_HOLE_GET_VALID(val)) {
183571815ce7SRobert Mustacchi 		uint64_t t;
183671815ce7SRobert Mustacchi 
183771815ce7SRobert Mustacchi 		df->zud_flags |= ZEN_UMC_DF_F_HOLE_VALID;
183871815ce7SRobert Mustacchi 		t = DF_DRAM_HOLE_GET_BASE(val);
183971815ce7SRobert Mustacchi 		df->zud_hole_base = t << DF_DRAM_HOLE_BASE_SHIFT;
184071815ce7SRobert Mustacchi 	}
184171815ce7SRobert Mustacchi 
184271815ce7SRobert Mustacchi 	/*
184371815ce7SRobert Mustacchi 	 * Prior to Zen 4, the hash information was global and applied to all
184471815ce7SRobert Mustacchi 	 * COD rules globally. Check if we're on such a system and snapshot this
184571815ce7SRobert Mustacchi 	 * so we can use it during the rule application. Note, this was added in
184671815ce7SRobert Mustacchi 	 * DFv3.
184771815ce7SRobert Mustacchi 	 */
184871815ce7SRobert Mustacchi 	if (umc->umc_df_rev == DF_REV_3 || umc->umc_df_rev == DF_REV_3P5) {
184971815ce7SRobert Mustacchi 		uint32_t globctl;
185071815ce7SRobert Mustacchi 
185171815ce7SRobert Mustacchi 		if ((ret = amdzen_c_df_read32(dfno, instid, DF_GLOB_CTL_V3,
185271815ce7SRobert Mustacchi 		    &globctl)) != 0) {
185371815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!failed to read global "
185471815ce7SRobert Mustacchi 			    "control: %d", ret);
185571815ce7SRobert Mustacchi 			return (-1);
185671815ce7SRobert Mustacchi 		}
185771815ce7SRobert Mustacchi 
185871815ce7SRobert Mustacchi 		df->zud_glob_ctl_raw = globctl;
185971815ce7SRobert Mustacchi 		if (DF_GLOB_CTL_V3_GET_HASH_1G(globctl) != 0) {
186071815ce7SRobert Mustacchi 			df->zud_flags |= ZEN_UMC_DF_F_HASH_30_32;
186171815ce7SRobert Mustacchi 		}
186271815ce7SRobert Mustacchi 
186371815ce7SRobert Mustacchi 		if (DF_GLOB_CTL_V3_GET_HASH_2M(globctl) != 0) {
186471815ce7SRobert Mustacchi 			df->zud_flags |= ZEN_UMC_DF_F_HASH_21_23;
186571815ce7SRobert Mustacchi 		}
186671815ce7SRobert Mustacchi 
186771815ce7SRobert Mustacchi 		if (DF_GLOB_CTL_V3_GET_HASH_64K(globctl) != 0) {
186871815ce7SRobert Mustacchi 			df->zud_flags |= ZEN_UMC_DF_F_HASH_16_18;
186971815ce7SRobert Mustacchi 		}
187071815ce7SRobert Mustacchi 	}
187171815ce7SRobert Mustacchi 
187271815ce7SRobert Mustacchi 	df->zud_dram_nrules = umc->umc_fdata->zufd_dram_nrules;
187371815ce7SRobert Mustacchi 	for (uint_t i = 0; i < umc->umc_fdata->zufd_dram_nrules; i++) {
187471815ce7SRobert Mustacchi 		if (zen_umc_read_dram_rule(umc, dfno, instid, i,
187571815ce7SRobert Mustacchi 		    &df->zud_rules[i]) != 0) {
187671815ce7SRobert Mustacchi 			return (-1);
187771815ce7SRobert Mustacchi 		}
187871815ce7SRobert Mustacchi 	}
187971815ce7SRobert Mustacchi 
188071815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_TARG_REMAP) != 0) {
188171815ce7SRobert Mustacchi 		if (zen_umc_read_remap(umc, df, instid) != 0) {
188271815ce7SRobert Mustacchi 			return (-1);
188371815ce7SRobert Mustacchi 		}
188471815ce7SRobert Mustacchi 	}
188571815ce7SRobert Mustacchi 
188671815ce7SRobert Mustacchi 	/*
188771815ce7SRobert Mustacchi 	 * We only want a single entry, so always return 1 to terminate us
188871815ce7SRobert Mustacchi 	 * early.
188971815ce7SRobert Mustacchi 	 */
189071815ce7SRobert Mustacchi 	return (1);
189171815ce7SRobert Mustacchi }
189271815ce7SRobert Mustacchi 
1893*0dd92943SRobert Mustacchi /*
1894*0dd92943SRobert Mustacchi  * At this point we can go through and calculate the size of the DIMM that we've
1895*0dd92943SRobert Mustacchi  * found. While it would be nice to determine this from the SPD data, we can
1896*0dd92943SRobert Mustacchi  * figure this out entirely based upon the information in the memory controller.
1897*0dd92943SRobert Mustacchi  *
1898*0dd92943SRobert Mustacchi  * This works by first noting that DDR4, LPDDR4, DDR5, and LPDDR5 are all built
1899*0dd92943SRobert Mustacchi  * around 64-bit data channels. This means that each row and column provides up
1900*0dd92943SRobert Mustacchi  * 64-bits (ignoring ECC) of data. There are a number of banks and bank groups.
1901*0dd92943SRobert Mustacchi  * The memory controller tracks the total number of bits that are used for each.
1902*0dd92943SRobert Mustacchi  * While DDR5 introduces sub-channels, we don't need to worry about those here,
1903*0dd92943SRobert Mustacchi  * because ultimately the sub-channel just splits the 64-bit bus we're assuming
1904*0dd92943SRobert Mustacchi  * into 2x 32-bit buses. While they can be independently selected, they should
1905*0dd92943SRobert Mustacchi  * have equivalent capacities.
1906*0dd92943SRobert Mustacchi  *
1907*0dd92943SRobert Mustacchi  * The most confusing part of this is that there is one of these related to each
1908*0dd92943SRobert Mustacchi  * rank on the device. The UMC natively has two 'chip-selects', each of which is
1909*0dd92943SRobert Mustacchi  * used to correspond to a rank. There are then separately multiple rm bits in
1910*0dd92943SRobert Mustacchi  * each chip-select. As far as we can tell the PSP or SMU programs the number of
1911*0dd92943SRobert Mustacchi  * rm bits to be zero when you have a dual-rank device.
1912*0dd92943SRobert Mustacchi  *
1913*0dd92943SRobert Mustacchi  * We end up summing each chip-select rather than assuming that the chip-selects
1914*0dd92943SRobert Mustacchi  * are identical. In theory some amount of asymmetric DIMMs exist in the wild,
1915*0dd92943SRobert Mustacchi  * but we don't know of many systems using them.
1916*0dd92943SRobert Mustacchi  */
1917*0dd92943SRobert Mustacchi static void
zen_umc_calc_dimm_size(umc_dimm_t * dimm)1918*0dd92943SRobert Mustacchi zen_umc_calc_dimm_size(umc_dimm_t *dimm)
1919*0dd92943SRobert Mustacchi {
1920*0dd92943SRobert Mustacchi 	dimm->ud_dimm_size = 0;
1921*0dd92943SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_BASE; i++) {
1922*0dd92943SRobert Mustacchi 		uint64_t nrc;
1923*0dd92943SRobert Mustacchi 		const umc_cs_t *cs = &dimm->ud_cs[i];
1924*0dd92943SRobert Mustacchi 
1925*0dd92943SRobert Mustacchi 		if (!cs->ucs_base.udb_valid && !cs->ucs_sec.udb_valid) {
1926*0dd92943SRobert Mustacchi 			continue;
1927*0dd92943SRobert Mustacchi 		}
1928*0dd92943SRobert Mustacchi 
1929*0dd92943SRobert Mustacchi 		nrc = cs->ucs_nrow_lo + cs->ucs_nrow_hi + cs->ucs_ncol;
1930*0dd92943SRobert Mustacchi 		dimm->ud_dimm_size += (8ULL << nrc) * (1 << cs->ucs_nbanks) *
1931*0dd92943SRobert Mustacchi 		    (1 << cs->ucs_nrm);
1932*0dd92943SRobert Mustacchi 	}
1933*0dd92943SRobert Mustacchi }
1934*0dd92943SRobert Mustacchi 
1935f8e9c7b3SRobert Mustacchi /*
1936f8e9c7b3SRobert Mustacchi  * This is used to fill in the common properties about a DIMM. This should occur
1937f8e9c7b3SRobert Mustacchi  * after the rank information has been filled out. The information used is the
1938f8e9c7b3SRobert Mustacchi  * same between DDR4 and DDR5 DIMMs. The only major difference is the register
1939f8e9c7b3SRobert Mustacchi  * offset.
1940f8e9c7b3SRobert Mustacchi  */
1941f8e9c7b3SRobert Mustacchi static boolean_t
zen_umc_fill_dimm_common(zen_umc_t * umc,zen_umc_df_t * df,zen_umc_chan_t * chan,const uint_t dimmno,boolean_t ddr4_style)1942f8e9c7b3SRobert Mustacchi zen_umc_fill_dimm_common(zen_umc_t *umc, zen_umc_df_t *df, zen_umc_chan_t *chan,
1943*0dd92943SRobert Mustacchi     const uint_t dimmno, boolean_t ddr4_style)
1944f8e9c7b3SRobert Mustacchi {
1945f8e9c7b3SRobert Mustacchi 	umc_dimm_t *dimm;
1946f8e9c7b3SRobert Mustacchi 	int ret;
1947f8e9c7b3SRobert Mustacchi 	smn_reg_t reg;
1948f8e9c7b3SRobert Mustacchi 	uint32_t val;
1949f8e9c7b3SRobert Mustacchi 	const uint32_t id = chan->chan_logid;
1950f8e9c7b3SRobert Mustacchi 
1951f8e9c7b3SRobert Mustacchi 	dimm = &chan->chan_dimms[dimmno];
1952f8e9c7b3SRobert Mustacchi 	dimm->ud_dimmno = dimmno;
1953f8e9c7b3SRobert Mustacchi 
1954*0dd92943SRobert Mustacchi 	if (ddr4_style) {
1955f8e9c7b3SRobert Mustacchi 		reg = UMC_DIMMCFG_DDR4(id, dimmno);
1956f8e9c7b3SRobert Mustacchi 	} else {
1957f8e9c7b3SRobert Mustacchi 		reg = UMC_DIMMCFG_DDR5(id, dimmno);
1958f8e9c7b3SRobert Mustacchi 	}
19594adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
1960f8e9c7b3SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read DIMM "
1961f8e9c7b3SRobert Mustacchi 		    "configuration register %x: %d", SMN_REG_ADDR(reg), ret);
1962f8e9c7b3SRobert Mustacchi 		return (B_FALSE);
1963f8e9c7b3SRobert Mustacchi 	}
1964f8e9c7b3SRobert Mustacchi 	dimm->ud_dimmcfg_raw = val;
1965f8e9c7b3SRobert Mustacchi 
1966f8e9c7b3SRobert Mustacchi 	if (UMC_DIMMCFG_GET_X16(val) != 0) {
1967f8e9c7b3SRobert Mustacchi 		dimm->ud_width = UMC_DIMM_W_X16;
1968f8e9c7b3SRobert Mustacchi 	} else if (UMC_DIMMCFG_GET_X4(val) != 0) {
1969f8e9c7b3SRobert Mustacchi 		dimm->ud_width = UMC_DIMM_W_X4;
1970f8e9c7b3SRobert Mustacchi 	} else {
1971f8e9c7b3SRobert Mustacchi 		dimm->ud_width = UMC_DIMM_W_X8;
1972f8e9c7b3SRobert Mustacchi 	}
1973f8e9c7b3SRobert Mustacchi 
1974f8e9c7b3SRobert Mustacchi 	if (UMC_DIMMCFG_GET_3DS(val) != 0) {
1975f8e9c7b3SRobert Mustacchi 		dimm->ud_kind = UMC_DIMM_K_3DS_RDIMM;
1976f8e9c7b3SRobert Mustacchi 	} else if (UMC_DIMMCFG_GET_LRDIMM(val) != 0) {
1977f8e9c7b3SRobert Mustacchi 		dimm->ud_kind = UMC_DIMM_K_LRDIMM;
1978f8e9c7b3SRobert Mustacchi 	} else if (UMC_DIMMCFG_GET_RDIMM(val) != 0) {
1979f8e9c7b3SRobert Mustacchi 		dimm->ud_kind = UMC_DIMM_K_RDIMM;
1980f8e9c7b3SRobert Mustacchi 	} else {
1981f8e9c7b3SRobert Mustacchi 		dimm->ud_kind = UMC_DIMM_K_UDIMM;
1982f8e9c7b3SRobert Mustacchi 	}
1983f8e9c7b3SRobert Mustacchi 
1984f8e9c7b3SRobert Mustacchi 	/*
1985f8e9c7b3SRobert Mustacchi 	 * DIMM information in a UMC can be somewhat confusing. There are quite
1986f8e9c7b3SRobert Mustacchi 	 * a number of non-zero reset values that are here. Flag whether or not
1987f8e9c7b3SRobert Mustacchi 	 * we think this entry should be usable based on enabled chip-selects.
1988f8e9c7b3SRobert Mustacchi 	 */
1989f8e9c7b3SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_BASE; i++) {
1990f8e9c7b3SRobert Mustacchi 		if (dimm->ud_cs[i].ucs_base.udb_valid ||
1991f8e9c7b3SRobert Mustacchi 		    dimm->ud_cs[i].ucs_sec.udb_valid) {
1992f8e9c7b3SRobert Mustacchi 			dimm->ud_flags |= UMC_DIMM_F_VALID;
1993f8e9c7b3SRobert Mustacchi 			break;
1994f8e9c7b3SRobert Mustacchi 		}
1995f8e9c7b3SRobert Mustacchi 	}
1996f8e9c7b3SRobert Mustacchi 
1997*0dd92943SRobert Mustacchi 	/*
1998*0dd92943SRobert Mustacchi 	 * The remaining calculations we only want to perform if we have actual
1999*0dd92943SRobert Mustacchi 	 * data for a DIMM.
2000*0dd92943SRobert Mustacchi 	 */
2001*0dd92943SRobert Mustacchi 	if ((dimm->ud_flags & UMC_DIMM_F_VALID) == 0) {
2002*0dd92943SRobert Mustacchi 		return (B_TRUE);
2003*0dd92943SRobert Mustacchi 	}
2004*0dd92943SRobert Mustacchi 
2005*0dd92943SRobert Mustacchi 	zen_umc_calc_dimm_size(dimm);
2006*0dd92943SRobert Mustacchi 
2007f8e9c7b3SRobert Mustacchi 	return (B_TRUE);
2008f8e9c7b3SRobert Mustacchi }
2009f8e9c7b3SRobert Mustacchi 
201071815ce7SRobert Mustacchi /*
201171815ce7SRobert Mustacchi  * Fill all the information about a DDR4 DIMM. In the DDR4 UMC, some of this
201271815ce7SRobert Mustacchi  * information is on a per-chip select basis while at other times it is on a
201371815ce7SRobert Mustacchi  * per-DIMM basis.  In general, chip-selects 0/1 correspond to DIMM 0, and
201471815ce7SRobert Mustacchi  * chip-selects 2/3 correspond to DIMM 1. To normalize things with the DDR5 UMC
201571815ce7SRobert Mustacchi  * which generally has things stored on a per-rank/chips-select basis, we
201671815ce7SRobert Mustacchi  * duplicate information that is DIMM-wide into the chip-select data structure
201771815ce7SRobert Mustacchi  * (umc_cs_t).
201871815ce7SRobert Mustacchi  */
201971815ce7SRobert Mustacchi static boolean_t
zen_umc_fill_chan_dimm_ddr4(zen_umc_t * umc,zen_umc_df_t * df,zen_umc_chan_t * chan,const uint_t dimmno)202071815ce7SRobert Mustacchi zen_umc_fill_chan_dimm_ddr4(zen_umc_t *umc, zen_umc_df_t *df,
202171815ce7SRobert Mustacchi     zen_umc_chan_t *chan, const uint_t dimmno)
202271815ce7SRobert Mustacchi {
202371815ce7SRobert Mustacchi 	umc_dimm_t *dimm;
202471815ce7SRobert Mustacchi 	umc_cs_t *cs0, *cs1;
202571815ce7SRobert Mustacchi 	const uint32_t id = chan->chan_logid;
202671815ce7SRobert Mustacchi 	int ret;
2027ba215efeSKeith M Wesolowski 	uint32_t val;
2028ba215efeSKeith M Wesolowski 	smn_reg_t reg;
202971815ce7SRobert Mustacchi 
203071815ce7SRobert Mustacchi 	ASSERT3U(dimmno, <, ZEN_UMC_MAX_DIMMS);
203171815ce7SRobert Mustacchi 	dimm = &chan->chan_dimms[dimmno];
203271815ce7SRobert Mustacchi 	cs0 = &dimm->ud_cs[0];
203371815ce7SRobert Mustacchi 	cs1 = &dimm->ud_cs[1];
203471815ce7SRobert Mustacchi 
203571815ce7SRobert Mustacchi 	/*
203671815ce7SRobert Mustacchi 	 * DDR4 organization has initial data that exists on a per-chip select
203771815ce7SRobert Mustacchi 	 * basis. The rest of it is on a per-DIMM basis. First we grab the
203871815ce7SRobert Mustacchi 	 * per-chip-select data. After this for loop, we will always duplicate
203971815ce7SRobert Mustacchi 	 * all data that we gather into both chip-selects.
204071815ce7SRobert Mustacchi 	 */
204171815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_CS_PER_DIMM; i++) {
204271815ce7SRobert Mustacchi 		uint64_t addr;
2043ba215efeSKeith M Wesolowski 		const uint16_t reginst = i + dimmno * 2;
204471815ce7SRobert Mustacchi 		reg = UMC_BASE(id, reginst);
20454adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
204671815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read base "
2047ba215efeSKeith M Wesolowski 			    "register %x: %d", SMN_REG_ADDR(reg), ret);
204871815ce7SRobert Mustacchi 			return (B_FALSE);
204971815ce7SRobert Mustacchi 		}
205071815ce7SRobert Mustacchi 
205171815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_BASE_GET_ADDR(val) << UMC_BASE_ADDR_SHIFT;
205271815ce7SRobert Mustacchi 		dimm->ud_cs[i].ucs_base.udb_base = addr;
205371815ce7SRobert Mustacchi 		dimm->ud_cs[i].ucs_base.udb_valid = UMC_BASE_GET_EN(val);
205471815ce7SRobert Mustacchi 
205571815ce7SRobert Mustacchi 		reg = UMC_BASE_SEC(id, reginst);
20564adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
205771815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read "
2058ba215efeSKeith M Wesolowski 			    "secondary base register %x: %d", SMN_REG_ADDR(reg),
2059ba215efeSKeith M Wesolowski 			    ret);
206071815ce7SRobert Mustacchi 			return (B_FALSE);
206171815ce7SRobert Mustacchi 		}
206271815ce7SRobert Mustacchi 
206371815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_BASE_GET_ADDR(val) << UMC_BASE_ADDR_SHIFT;
206471815ce7SRobert Mustacchi 		dimm->ud_cs[i].ucs_sec.udb_base = addr;
206571815ce7SRobert Mustacchi 		dimm->ud_cs[i].ucs_sec.udb_valid = UMC_BASE_GET_EN(val);
206671815ce7SRobert Mustacchi 	}
206771815ce7SRobert Mustacchi 
206871815ce7SRobert Mustacchi 	reg = UMC_MASK_DDR4(id, dimmno);
20694adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
207071815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read mask register "
2071ba215efeSKeith M Wesolowski 		    "%x: %d", SMN_REG_ADDR(reg), ret);
207271815ce7SRobert Mustacchi 		return (B_FALSE);
207371815ce7SRobert Mustacchi 	}
207471815ce7SRobert Mustacchi 
207571815ce7SRobert Mustacchi 	/*
207671815ce7SRobert Mustacchi 	 * When we extract the masks, hardware only checks a limited range of
207771815ce7SRobert Mustacchi 	 * bits. Therefore we need to always OR in those lower order bits.
207871815ce7SRobert Mustacchi 	 */
207971815ce7SRobert Mustacchi 	cs0->ucs_base_mask = (uint64_t)UMC_MASK_GET_ADDR(val) <<
208071815ce7SRobert Mustacchi 	    UMC_MASK_ADDR_SHIFT;
208171815ce7SRobert Mustacchi 	cs0->ucs_base_mask |= (1 << UMC_MASK_ADDR_SHIFT) - 1;
208271815ce7SRobert Mustacchi 	cs1->ucs_base_mask = cs0->ucs_base_mask;
208371815ce7SRobert Mustacchi 
208471815ce7SRobert Mustacchi 	reg = UMC_MASK_SEC_DDR4(id, dimmno);
20854adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
208671815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read secondary mask "
2087ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
208871815ce7SRobert Mustacchi 		return (B_FALSE);
208971815ce7SRobert Mustacchi 	}
209071815ce7SRobert Mustacchi 	cs0->ucs_sec_mask = (uint64_t)UMC_MASK_GET_ADDR(val) <<
209171815ce7SRobert Mustacchi 	    UMC_MASK_ADDR_SHIFT;
209271815ce7SRobert Mustacchi 	cs0->ucs_sec_mask |= (1 << UMC_MASK_ADDR_SHIFT) - 1;
209371815ce7SRobert Mustacchi 	cs1->ucs_sec_mask = cs0->ucs_sec_mask;
209471815ce7SRobert Mustacchi 
209571815ce7SRobert Mustacchi 	reg = UMC_ADDRCFG_DDR4(id, dimmno);
20964adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
209771815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read address config "
2098ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
209971815ce7SRobert Mustacchi 		return (B_FALSE);
210071815ce7SRobert Mustacchi 	}
210171815ce7SRobert Mustacchi 
210271815ce7SRobert Mustacchi 	cs0->ucs_nbanks = UMC_ADDRCFG_GET_NBANK_BITS(val) +
210371815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NBANK_BITS_BASE;
210471815ce7SRobert Mustacchi 	cs1->ucs_nbanks = cs0->ucs_nbanks;
210571815ce7SRobert Mustacchi 	cs0->ucs_ncol = UMC_ADDRCFG_GET_NCOL_BITS(val) +
210671815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NCOL_BITS_BASE;
210771815ce7SRobert Mustacchi 	cs1->ucs_ncol = cs0->ucs_ncol;
210871815ce7SRobert Mustacchi 	cs0->ucs_nrow_hi = UMC_ADDRCFG_DDR4_GET_NROW_BITS_HI(val);
210971815ce7SRobert Mustacchi 	cs1->ucs_nrow_hi = cs0->ucs_nrow_hi;
211071815ce7SRobert Mustacchi 	cs0->ucs_nrow_lo = UMC_ADDRCFG_GET_NROW_BITS_LO(val) +
211171815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NROW_BITS_LO_BASE;
211271815ce7SRobert Mustacchi 	cs1->ucs_nrow_lo = cs0->ucs_nrow_lo;
211371815ce7SRobert Mustacchi 	cs0->ucs_nbank_groups = UMC_ADDRCFG_GET_NBANKGRP_BITS(val);
211471815ce7SRobert Mustacchi 	cs1->ucs_nbank_groups = cs0->ucs_nbank_groups;
211571815ce7SRobert Mustacchi 	/*
211671815ce7SRobert Mustacchi 	 * As the chip-select XORs don't always show up, use a dummy value
211771815ce7SRobert Mustacchi 	 * that'll result in no change occurring here.
211871815ce7SRobert Mustacchi 	 */
211971815ce7SRobert Mustacchi 	cs0->ucs_cs_xor = cs1->ucs_cs_xor = 0;
212071815ce7SRobert Mustacchi 
212171815ce7SRobert Mustacchi 	/*
212271815ce7SRobert Mustacchi 	 * APUs don't seem to support various rank select bits.
212371815ce7SRobert Mustacchi 	 */
212471815ce7SRobert Mustacchi 	if (umc->umc_fdata->zufd_umc_style == ZEN_UMC_UMC_S_DDR4) {
212571815ce7SRobert Mustacchi 		cs0->ucs_nrm = UMC_ADDRCFG_DDR4_GET_NRM_BITS(val);
212671815ce7SRobert Mustacchi 		cs1->ucs_nrm = cs0->ucs_nrm;
212771815ce7SRobert Mustacchi 	} else {
212871815ce7SRobert Mustacchi 		cs0->ucs_nrm = cs1->ucs_nrm = 0;
212971815ce7SRobert Mustacchi 	}
213071815ce7SRobert Mustacchi 
213171815ce7SRobert Mustacchi 	reg = UMC_ADDRSEL_DDR4(id, dimmno);
21324adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
213371815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read bank address "
2134ba215efeSKeith M Wesolowski 		    "select register %x: %d", SMN_REG_ADDR(reg), ret);
213571815ce7SRobert Mustacchi 		return (B_FALSE);
213671815ce7SRobert Mustacchi 	}
213771815ce7SRobert Mustacchi 	cs0->ucs_row_hi_bit = UMC_ADDRSEL_DDR4_GET_ROW_HI(val) +
213871815ce7SRobert Mustacchi 	    UMC_ADDRSEL_DDR4_ROW_HI_BASE;
213971815ce7SRobert Mustacchi 	cs1->ucs_row_hi_bit = cs0->ucs_row_hi_bit;
214071815ce7SRobert Mustacchi 	cs0->ucs_row_low_bit = UMC_ADDRSEL_GET_ROW_LO(val) +
214171815ce7SRobert Mustacchi 	    UMC_ADDRSEL_ROW_LO_BASE;
214271815ce7SRobert Mustacchi 	cs1->ucs_row_low_bit = cs0->ucs_row_low_bit;
214371815ce7SRobert Mustacchi 	cs0->ucs_bank_bits[0] = UMC_ADDRSEL_GET_BANK0(val) +
214471815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
214571815ce7SRobert Mustacchi 	cs0->ucs_bank_bits[1] = UMC_ADDRSEL_GET_BANK1(val) +
214671815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
214771815ce7SRobert Mustacchi 	cs0->ucs_bank_bits[2] = UMC_ADDRSEL_GET_BANK2(val) +
214871815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
214971815ce7SRobert Mustacchi 	cs0->ucs_bank_bits[3] = UMC_ADDRSEL_GET_BANK3(val) +
215071815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
215171815ce7SRobert Mustacchi 	cs0->ucs_bank_bits[4] = UMC_ADDRSEL_GET_BANK4(val) +
215271815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
215371815ce7SRobert Mustacchi 	bcopy(cs0->ucs_bank_bits, cs1->ucs_bank_bits,
215471815ce7SRobert Mustacchi 	    sizeof (cs0->ucs_bank_bits));
215571815ce7SRobert Mustacchi 
215671815ce7SRobert Mustacchi 	reg = UMC_COLSEL_LO_DDR4(id, dimmno);
21574adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
215871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read column address "
2159ba215efeSKeith M Wesolowski 		    "select low register %x: %d", SMN_REG_ADDR(reg), ret);
216071815ce7SRobert Mustacchi 		return (B_FALSE);
216171815ce7SRobert Mustacchi 	}
216271815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_COLSEL_PER_REG; i++) {
216371815ce7SRobert Mustacchi 		cs0->ucs_col_bits[i] = UMC_COLSEL_REMAP_GET_COL(val, i) +
216471815ce7SRobert Mustacchi 		    UMC_COLSEL_LO_BASE;
216571815ce7SRobert Mustacchi 	}
216671815ce7SRobert Mustacchi 
216771815ce7SRobert Mustacchi 	reg = UMC_COLSEL_HI_DDR4(id, dimmno);
21684adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
216971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read column address "
2170ba215efeSKeith M Wesolowski 		    "select high register %x: %d", SMN_REG_ADDR(reg), ret);
217171815ce7SRobert Mustacchi 		return (B_FALSE);
217271815ce7SRobert Mustacchi 	}
217371815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_COLSEL_PER_REG; i++) {
217471815ce7SRobert Mustacchi 		cs0->ucs_col_bits[i + ZEN_UMC_MAX_COLSEL_PER_REG] =
217571815ce7SRobert Mustacchi 		    UMC_COLSEL_REMAP_GET_COL(val, i) + UMC_COLSEL_HI_BASE;
217671815ce7SRobert Mustacchi 	}
217771815ce7SRobert Mustacchi 	bcopy(cs0->ucs_col_bits, cs1->ucs_col_bits, sizeof (cs0->ucs_col_bits));
217871815ce7SRobert Mustacchi 
217971815ce7SRobert Mustacchi 	/*
218071815ce7SRobert Mustacchi 	 * The next two registers give us information about a given rank select.
218171815ce7SRobert Mustacchi 	 * In the APUs, the inversion bits are there; however, the actual bit
218271815ce7SRobert Mustacchi 	 * selects are not. In this case we read the reserved bits regardless.
218371815ce7SRobert Mustacchi 	 * They should be ignored due to the fact that the number of banks is
218471815ce7SRobert Mustacchi 	 * zero.
218571815ce7SRobert Mustacchi 	 */
218671815ce7SRobert Mustacchi 	reg = UMC_RMSEL_DDR4(id, dimmno);
21874adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
218871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read rank address "
2189ba215efeSKeith M Wesolowski 		    "select register %x: %d", SMN_REG_ADDR(reg), ret);
219071815ce7SRobert Mustacchi 		return (B_FALSE);
219171815ce7SRobert Mustacchi 	}
219271815ce7SRobert Mustacchi 	cs0->ucs_inv_msbs = UMC_RMSEL_DDR4_GET_INV_MSBE(val);
219371815ce7SRobert Mustacchi 	cs1->ucs_inv_msbs = UMC_RMSEL_DDR4_GET_INV_MSBO(val);
219471815ce7SRobert Mustacchi 	cs0->ucs_rm_bits[0] = UMC_RMSEL_DDR4_GET_RM0(val) +
219571815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
219671815ce7SRobert Mustacchi 	cs0->ucs_rm_bits[1] = UMC_RMSEL_DDR4_GET_RM1(val) +
219771815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
219871815ce7SRobert Mustacchi 	cs0->ucs_rm_bits[2] = UMC_RMSEL_DDR4_GET_RM2(val) +
219971815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
220071815ce7SRobert Mustacchi 	bcopy(cs0->ucs_rm_bits, cs1->ucs_rm_bits, sizeof (cs0->ucs_rm_bits));
220171815ce7SRobert Mustacchi 
220271815ce7SRobert Mustacchi 	reg = UMC_RMSEL_SEC_DDR4(id, dimmno);
22034adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
220471815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read secondary rank "
2205ba215efeSKeith M Wesolowski 		    "address select register %x: %d", SMN_REG_ADDR(reg), ret);
220671815ce7SRobert Mustacchi 		return (B_FALSE);
220771815ce7SRobert Mustacchi 	}
220871815ce7SRobert Mustacchi 	cs0->ucs_inv_msbs_sec = UMC_RMSEL_DDR4_GET_INV_MSBE(val);
220971815ce7SRobert Mustacchi 	cs1->ucs_inv_msbs_sec = UMC_RMSEL_DDR4_GET_INV_MSBO(val);
221071815ce7SRobert Mustacchi 	cs0->ucs_rm_bits_sec[0] = UMC_RMSEL_DDR4_GET_RM0(val) +
221171815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
221271815ce7SRobert Mustacchi 	cs0->ucs_rm_bits_sec[1] = UMC_RMSEL_DDR4_GET_RM1(val) +
221371815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
221471815ce7SRobert Mustacchi 	cs0->ucs_rm_bits_sec[2] = UMC_RMSEL_DDR4_GET_RM2(val) +
221571815ce7SRobert Mustacchi 	    UMC_RMSEL_BASE;
221671815ce7SRobert Mustacchi 	bcopy(cs0->ucs_rm_bits_sec, cs1->ucs_rm_bits_sec,
221771815ce7SRobert Mustacchi 	    sizeof (cs0->ucs_rm_bits_sec));
221871815ce7SRobert Mustacchi 
2219f8e9c7b3SRobert Mustacchi 	return (zen_umc_fill_dimm_common(umc, df, chan, dimmno, B_TRUE));
222071815ce7SRobert Mustacchi }
222171815ce7SRobert Mustacchi 
222271815ce7SRobert Mustacchi /*
222371815ce7SRobert Mustacchi  * The DDR5 based systems are organized such that almost all the information we
222471815ce7SRobert Mustacchi  * care about is split between two different chip-select structures in the UMC
222571815ce7SRobert Mustacchi  * hardware SMN space.
222671815ce7SRobert Mustacchi  */
222771815ce7SRobert Mustacchi static boolean_t
zen_umc_fill_chan_rank_ddr5(zen_umc_t * umc,zen_umc_df_t * df,zen_umc_chan_t * chan,const uint_t dimmno,const uint_t rankno)222871815ce7SRobert Mustacchi zen_umc_fill_chan_rank_ddr5(zen_umc_t *umc, zen_umc_df_t *df,
222971815ce7SRobert Mustacchi     zen_umc_chan_t *chan, const uint_t dimmno, const uint_t rankno)
223071815ce7SRobert Mustacchi {
223171815ce7SRobert Mustacchi 	int ret;
223271815ce7SRobert Mustacchi 	umc_cs_t *cs;
2233ba215efeSKeith M Wesolowski 	uint32_t val;
2234ba215efeSKeith M Wesolowski 	smn_reg_t reg;
223571815ce7SRobert Mustacchi 	const uint32_t id = chan->chan_logid;
223671815ce7SRobert Mustacchi 	const uint32_t regno = dimmno * 2 + rankno;
223771815ce7SRobert Mustacchi 
223871815ce7SRobert Mustacchi 	ASSERT3U(dimmno, <, ZEN_UMC_MAX_DIMMS);
223971815ce7SRobert Mustacchi 	ASSERT3U(rankno, <, ZEN_UMC_MAX_CS_PER_DIMM);
224071815ce7SRobert Mustacchi 	cs = &chan->chan_dimms[dimmno].ud_cs[rankno];
224171815ce7SRobert Mustacchi 
224271815ce7SRobert Mustacchi 	reg = UMC_BASE(id, regno);
22434adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
224471815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read base "
2245ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
224671815ce7SRobert Mustacchi 		return (B_FALSE);
224771815ce7SRobert Mustacchi 	}
224871815ce7SRobert Mustacchi 	cs->ucs_base.udb_base = (uint64_t)UMC_BASE_GET_ADDR(val) <<
224971815ce7SRobert Mustacchi 	    UMC_BASE_ADDR_SHIFT;
225071815ce7SRobert Mustacchi 	cs->ucs_base.udb_valid = UMC_BASE_GET_EN(val);
225171815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_UMC_EADDR) != 0) {
225271815ce7SRobert Mustacchi 		uint64_t addr;
225371815ce7SRobert Mustacchi 
225471815ce7SRobert Mustacchi 		reg = UMC_BASE_EXT_DDR5(id, regno);
22554adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) !=
225671815ce7SRobert Mustacchi 		    0) {
225771815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read "
2258ba215efeSKeith M Wesolowski 			    "extended base register %x: %d", SMN_REG_ADDR(reg),
2259ba215efeSKeith M Wesolowski 			    ret);
226071815ce7SRobert Mustacchi 			return (B_FALSE);
226171815ce7SRobert Mustacchi 		}
226271815ce7SRobert Mustacchi 
226371815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_BASE_EXT_GET_ADDR(val) <<
226471815ce7SRobert Mustacchi 		    UMC_BASE_EXT_ADDR_SHIFT;
226571815ce7SRobert Mustacchi 		cs->ucs_base.udb_base |= addr;
226671815ce7SRobert Mustacchi 	}
226771815ce7SRobert Mustacchi 
226871815ce7SRobert Mustacchi 	reg = UMC_BASE_SEC(id, regno);
22694adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
227071815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read secondary base "
2271ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
227271815ce7SRobert Mustacchi 		return (B_FALSE);
227371815ce7SRobert Mustacchi 	}
227471815ce7SRobert Mustacchi 	cs->ucs_sec.udb_base = (uint64_t)UMC_BASE_GET_ADDR(val) <<
227571815ce7SRobert Mustacchi 	    UMC_BASE_ADDR_SHIFT;
227671815ce7SRobert Mustacchi 	cs->ucs_sec.udb_valid = UMC_BASE_GET_EN(val);
227771815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_UMC_EADDR) != 0) {
227871815ce7SRobert Mustacchi 		uint64_t addr;
227971815ce7SRobert Mustacchi 
228071815ce7SRobert Mustacchi 		reg = UMC_BASE_EXT_SEC_DDR5(id, regno);
22814adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) !=
228271815ce7SRobert Mustacchi 		    0) {
228371815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read "
2284ba215efeSKeith M Wesolowski 			    "extended secondary base register %x: %d",
2285ba215efeSKeith M Wesolowski 			    SMN_REG_ADDR(reg), ret);
228671815ce7SRobert Mustacchi 			return (B_FALSE);
228771815ce7SRobert Mustacchi 		}
228871815ce7SRobert Mustacchi 
228971815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_BASE_EXT_GET_ADDR(val) <<
229071815ce7SRobert Mustacchi 		    UMC_BASE_EXT_ADDR_SHIFT;
229171815ce7SRobert Mustacchi 		cs->ucs_sec.udb_base |= addr;
229271815ce7SRobert Mustacchi 	}
229371815ce7SRobert Mustacchi 
229471815ce7SRobert Mustacchi 	reg = UMC_MASK_DDR5(id, regno);
22954adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
229671815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read mask "
2297ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
229871815ce7SRobert Mustacchi 		return (B_FALSE);
229971815ce7SRobert Mustacchi 	}
230071815ce7SRobert Mustacchi 	cs->ucs_base_mask = (uint64_t)UMC_MASK_GET_ADDR(val) <<
230171815ce7SRobert Mustacchi 	    UMC_MASK_ADDR_SHIFT;
230271815ce7SRobert Mustacchi 	cs->ucs_base_mask |= (1 << UMC_MASK_ADDR_SHIFT) - 1;
230371815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_UMC_EADDR) != 0) {
230471815ce7SRobert Mustacchi 		uint64_t addr;
230571815ce7SRobert Mustacchi 
230671815ce7SRobert Mustacchi 		reg = UMC_MASK_EXT_DDR5(id, regno);
23074adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) !=
230871815ce7SRobert Mustacchi 		    0) {
230971815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read "
2310ba215efeSKeith M Wesolowski 			    "extended mask register %x: %d", SMN_REG_ADDR(reg),
2311ba215efeSKeith M Wesolowski 			    ret);
231271815ce7SRobert Mustacchi 			return (B_FALSE);
231371815ce7SRobert Mustacchi 		}
231471815ce7SRobert Mustacchi 
231571815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_MASK_EXT_GET_ADDR(val) <<
231671815ce7SRobert Mustacchi 		    UMC_MASK_EXT_ADDR_SHIFT;
231771815ce7SRobert Mustacchi 		cs->ucs_base_mask |= addr;
231871815ce7SRobert Mustacchi 	}
231971815ce7SRobert Mustacchi 
232071815ce7SRobert Mustacchi 
232171815ce7SRobert Mustacchi 	reg = UMC_MASK_SEC_DDR5(id, regno);
23224adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
232371815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read secondary mask "
2324ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
232571815ce7SRobert Mustacchi 		return (B_FALSE);
232671815ce7SRobert Mustacchi 	}
232771815ce7SRobert Mustacchi 	cs->ucs_sec_mask = (uint64_t)UMC_MASK_GET_ADDR(val) <<
232871815ce7SRobert Mustacchi 	    UMC_MASK_ADDR_SHIFT;
232971815ce7SRobert Mustacchi 	cs->ucs_sec_mask |= (1 << UMC_MASK_ADDR_SHIFT) - 1;
233071815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_UMC_EADDR) != 0) {
233171815ce7SRobert Mustacchi 		uint64_t addr;
233271815ce7SRobert Mustacchi 
233371815ce7SRobert Mustacchi 		reg = UMC_MASK_EXT_SEC_DDR5(id, regno);
23344adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) !=
233571815ce7SRobert Mustacchi 		    0) {
233671815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read "
2337ba215efeSKeith M Wesolowski 			    "extended mask register %x: %d", SMN_REG_ADDR(reg),
2338ba215efeSKeith M Wesolowski 			    ret);
233971815ce7SRobert Mustacchi 			return (B_FALSE);
234071815ce7SRobert Mustacchi 		}
234171815ce7SRobert Mustacchi 
234271815ce7SRobert Mustacchi 		addr = (uint64_t)UMC_MASK_EXT_GET_ADDR(val) <<
234371815ce7SRobert Mustacchi 		    UMC_MASK_EXT_ADDR_SHIFT;
234471815ce7SRobert Mustacchi 		cs->ucs_sec_mask |= addr;
234571815ce7SRobert Mustacchi 	}
234671815ce7SRobert Mustacchi 
234771815ce7SRobert Mustacchi 	reg = UMC_ADDRCFG_DDR5(id, regno);
23484adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
234971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read address config "
2350ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
235171815ce7SRobert Mustacchi 		return (B_FALSE);
235271815ce7SRobert Mustacchi 	}
235371815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_CS_XOR) != 0) {
235471815ce7SRobert Mustacchi 		cs->ucs_cs_xor = UMC_ADDRCFG_DDR5_GET_CSXOR(val);
235571815ce7SRobert Mustacchi 	} else {
235671815ce7SRobert Mustacchi 		cs->ucs_cs_xor = 0;
235771815ce7SRobert Mustacchi 	}
235871815ce7SRobert Mustacchi 	cs->ucs_nbanks = UMC_ADDRCFG_GET_NBANK_BITS(val) +
235971815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NBANK_BITS_BASE;
236071815ce7SRobert Mustacchi 	cs->ucs_ncol = UMC_ADDRCFG_GET_NCOL_BITS(val) +
236171815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NCOL_BITS_BASE;
236271815ce7SRobert Mustacchi 	cs->ucs_nrow_lo = UMC_ADDRCFG_GET_NROW_BITS_LO(val) +
236371815ce7SRobert Mustacchi 	    UMC_ADDRCFG_NROW_BITS_LO_BASE;
236471815ce7SRobert Mustacchi 	cs->ucs_nrow_hi = 0;
236571815ce7SRobert Mustacchi 	cs->ucs_nrm = UMC_ADDRCFG_DDR5_GET_NRM_BITS(val);
236671815ce7SRobert Mustacchi 	cs->ucs_nbank_groups = UMC_ADDRCFG_GET_NBANKGRP_BITS(val);
236771815ce7SRobert Mustacchi 
236871815ce7SRobert Mustacchi 	reg = UMC_ADDRSEL_DDR5(id, regno);
23694adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
237071815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read address select "
2371ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
237271815ce7SRobert Mustacchi 		return (B_FALSE);
237371815ce7SRobert Mustacchi 	}
237471815ce7SRobert Mustacchi 	cs->ucs_row_hi_bit = 0;
237571815ce7SRobert Mustacchi 	cs->ucs_row_low_bit = UMC_ADDRSEL_GET_ROW_LO(val) +
237671815ce7SRobert Mustacchi 	    UMC_ADDRSEL_ROW_LO_BASE;
237771815ce7SRobert Mustacchi 	cs->ucs_bank_bits[4] = UMC_ADDRSEL_GET_BANK4(val) +
237871815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
237971815ce7SRobert Mustacchi 	cs->ucs_bank_bits[3] = UMC_ADDRSEL_GET_BANK3(val) +
238071815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
238171815ce7SRobert Mustacchi 	cs->ucs_bank_bits[2] = UMC_ADDRSEL_GET_BANK2(val) +
238271815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
238371815ce7SRobert Mustacchi 	cs->ucs_bank_bits[1] = UMC_ADDRSEL_GET_BANK1(val) +
238471815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
238571815ce7SRobert Mustacchi 	cs->ucs_bank_bits[0] = UMC_ADDRSEL_GET_BANK0(val) +
238671815ce7SRobert Mustacchi 	    UMC_ADDRSEL_BANK_BASE;
238771815ce7SRobert Mustacchi 
238871815ce7SRobert Mustacchi 	reg = UMC_COLSEL_LO_DDR5(id, regno);
23894adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
239071815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read column address "
2391ba215efeSKeith M Wesolowski 		    "select low register %x: %d", SMN_REG_ADDR(reg), ret);
239271815ce7SRobert Mustacchi 		return (B_FALSE);
239371815ce7SRobert Mustacchi 	}
239471815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_COLSEL_PER_REG; i++) {
239571815ce7SRobert Mustacchi 		cs->ucs_col_bits[i] = UMC_COLSEL_REMAP_GET_COL(val, i) +
239671815ce7SRobert Mustacchi 		    UMC_COLSEL_LO_BASE;
239771815ce7SRobert Mustacchi 	}
239871815ce7SRobert Mustacchi 
239971815ce7SRobert Mustacchi 	reg = UMC_COLSEL_HI_DDR5(id, regno);
24004adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
240171815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read column address "
2402ba215efeSKeith M Wesolowski 		    "select high register %x: %d", SMN_REG_ADDR(reg), ret);
240371815ce7SRobert Mustacchi 		return (B_FALSE);
240471815ce7SRobert Mustacchi 	}
240571815ce7SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_MAX_COLSEL_PER_REG; i++) {
240671815ce7SRobert Mustacchi 		cs->ucs_col_bits[i + ZEN_UMC_MAX_COLSEL_PER_REG] =
240771815ce7SRobert Mustacchi 		    UMC_COLSEL_REMAP_GET_COL(val, i) + UMC_COLSEL_HI_BASE;
240871815ce7SRobert Mustacchi 	}
240971815ce7SRobert Mustacchi 
241071815ce7SRobert Mustacchi 	/*
241171815ce7SRobert Mustacchi 	 * Time for our friend, the RM Selection register. Like in DDR4 we end
241271815ce7SRobert Mustacchi 	 * up reading everything here, even though most others have reserved
241371815ce7SRobert Mustacchi 	 * bits here. The intent is that we won't look at the reserved bits
241471815ce7SRobert Mustacchi 	 * unless something actually points us there.
241571815ce7SRobert Mustacchi 	 */
241671815ce7SRobert Mustacchi 	reg = UMC_RMSEL_DDR5(id, regno);
24174adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
241871815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read rank multiply "
2419ba215efeSKeith M Wesolowski 		    "select register %x: %d", SMN_REG_ADDR(reg), ret);
242071815ce7SRobert Mustacchi 		return (B_FALSE);
242171815ce7SRobert Mustacchi 	}
242271815ce7SRobert Mustacchi 
242371815ce7SRobert Mustacchi 	/*
242471815ce7SRobert Mustacchi 	 * DDR5 based devices have a primary and secondary msbs; however, they
242571815ce7SRobert Mustacchi 	 * only have a single set of rm bits. To normalize things with the DDR4
242671815ce7SRobert Mustacchi 	 * subsystem, we copy the primary bits to the secondary so we can use
242771815ce7SRobert Mustacchi 	 * these the same way in the decoder/encoder.
242871815ce7SRobert Mustacchi 	 */
242971815ce7SRobert Mustacchi 	cs->ucs_inv_msbs = UMC_RMSEL_DDR5_GET_INV_MSBS(val);
243071815ce7SRobert Mustacchi 	cs->ucs_inv_msbs_sec = UMC_RMSEL_DDR5_GET_INV_MSBS_SEC(val);
243171815ce7SRobert Mustacchi 	cs->ucs_subchan = UMC_RMSEL_DDR5_GET_SUBCHAN(val) +
243271815ce7SRobert Mustacchi 	    UMC_RMSEL_DDR5_SUBCHAN_BASE;
243371815ce7SRobert Mustacchi 	cs->ucs_rm_bits[3] = UMC_RMSEL_DDR5_GET_RM3(val) + UMC_RMSEL_BASE;
243471815ce7SRobert Mustacchi 	cs->ucs_rm_bits[2] = UMC_RMSEL_DDR5_GET_RM2(val) + UMC_RMSEL_BASE;
243571815ce7SRobert Mustacchi 	cs->ucs_rm_bits[1] = UMC_RMSEL_DDR5_GET_RM1(val) + UMC_RMSEL_BASE;
243671815ce7SRobert Mustacchi 	cs->ucs_rm_bits[0] = UMC_RMSEL_DDR5_GET_RM0(val) + UMC_RMSEL_BASE;
243771815ce7SRobert Mustacchi 	bcopy(cs->ucs_rm_bits, cs->ucs_rm_bits_sec,
243871815ce7SRobert Mustacchi 	    sizeof (cs->ucs_rm_bits));
243971815ce7SRobert Mustacchi 
2440f8e9c7b3SRobert Mustacchi 	return (zen_umc_fill_dimm_common(umc, df, chan, dimmno, B_FALSE));
244171815ce7SRobert Mustacchi }
244271815ce7SRobert Mustacchi 
244371815ce7SRobert Mustacchi static void
zen_umc_fill_ddr_type(zen_umc_t * umc,zen_umc_chan_t * chan)2444*0dd92943SRobert Mustacchi zen_umc_fill_ddr_type(zen_umc_t *umc, zen_umc_chan_t *chan)
244571815ce7SRobert Mustacchi {
244671815ce7SRobert Mustacchi 	umc_dimm_type_t dimm = UMC_DIMM_T_UNKNOWN;
244771815ce7SRobert Mustacchi 	uint8_t val;
244871815ce7SRobert Mustacchi 
244971815ce7SRobert Mustacchi 	/*
2450*0dd92943SRobert Mustacchi 	 * The different UMC styles split into two groups. Those that support
2451*0dd92943SRobert Mustacchi 	 * DDR4 and those that support DDR5 (with the hybrid group being in the
2452*0dd92943SRobert Mustacchi 	 * DDR5 style camp). While all the values are consistent between
2453*0dd92943SRobert Mustacchi 	 * different ones (e.g. reserved values correspond to unsupported
2454*0dd92943SRobert Mustacchi 	 * items), we still check types based on the UMC's design type so if we
2455*0dd92943SRobert Mustacchi 	 * see something weird, we don't accidentally use an older value.
245671815ce7SRobert Mustacchi 	 */
245771815ce7SRobert Mustacchi 	val = UMC_UMCCFG_GET_DDR_TYPE(chan->chan_umccfg_raw);
2458*0dd92943SRobert Mustacchi 	switch (umc->umc_fdata->zufd_umc_style) {
2459*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4:
2460*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4_APU:
246171815ce7SRobert Mustacchi 		switch (val) {
246271815ce7SRobert Mustacchi 		case UMC_UMCCFG_DDR4_T_DDR4:
246371815ce7SRobert Mustacchi 			dimm = UMC_DIMM_T_DDR4;
246471815ce7SRobert Mustacchi 			break;
246571815ce7SRobert Mustacchi 		case UMC_UMCCFG_DDR4_T_LPDDR4:
246671815ce7SRobert Mustacchi 			dimm = UMC_DIMM_T_LPDDR4;
246771815ce7SRobert Mustacchi 			break;
246871815ce7SRobert Mustacchi 		default:
246971815ce7SRobert Mustacchi 			break;
247071815ce7SRobert Mustacchi 		}
2471*0dd92943SRobert Mustacchi 		break;
2472*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_HYBRID_LPDDR5:
2473*0dd92943SRobert Mustacchi 		switch (val) {
2474*0dd92943SRobert Mustacchi 		case UMC_UMCCFG_DDR5_T_LPDDR5:
2475*0dd92943SRobert Mustacchi 			dimm = UMC_DIMM_T_LPDDR5;
2476*0dd92943SRobert Mustacchi 			break;
2477*0dd92943SRobert Mustacchi 		case UMC_UMCCFG_DDR5_T_LPDDR4:
2478*0dd92943SRobert Mustacchi 			dimm = UMC_DIMM_T_LPDDR4;
2479*0dd92943SRobert Mustacchi 			break;
2480*0dd92943SRobert Mustacchi 		default:
2481*0dd92943SRobert Mustacchi 			break;
2482*0dd92943SRobert Mustacchi 		}
2483*0dd92943SRobert Mustacchi 		break;
2484*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5:
2485*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5_APU:
248671815ce7SRobert Mustacchi 		switch (val) {
248771815ce7SRobert Mustacchi 		case UMC_UMCCFG_DDR5_T_DDR5:
248871815ce7SRobert Mustacchi 			dimm = UMC_DIMM_T_DDR5;
248971815ce7SRobert Mustacchi 			break;
249071815ce7SRobert Mustacchi 		case UMC_UMCCFG_DDR5_T_LPDDR5:
249171815ce7SRobert Mustacchi 			dimm = UMC_DIMM_T_LPDDR5;
249271815ce7SRobert Mustacchi 			break;
249371815ce7SRobert Mustacchi 		default:
249471815ce7SRobert Mustacchi 			break;
249571815ce7SRobert Mustacchi 		}
2496*0dd92943SRobert Mustacchi 		break;
2497*0dd92943SRobert Mustacchi 	}
2498*0dd92943SRobert Mustacchi 
2499*0dd92943SRobert Mustacchi 	chan->chan_type = dimm;
2500*0dd92943SRobert Mustacchi }
2501*0dd92943SRobert Mustacchi 
2502*0dd92943SRobert Mustacchi /*
2503*0dd92943SRobert Mustacchi  * Use the DDR4 frequency table to determine the speed of this. Note that our
2504*0dd92943SRobert Mustacchi  * hybrid based UMCs use 8 bits for the clock, while the traditional DDR4 ones
2505*0dd92943SRobert Mustacchi  * only use 7. The caller is responsible for using the right mask for the UMC.
2506*0dd92943SRobert Mustacchi  */
2507*0dd92943SRobert Mustacchi static void
zen_umc_fill_chan_ddr4(zen_umc_chan_t * chan,uint_t mstate,const uint32_t clock)2508*0dd92943SRobert Mustacchi zen_umc_fill_chan_ddr4(zen_umc_chan_t *chan, uint_t mstate,
2509*0dd92943SRobert Mustacchi     const uint32_t clock)
2510*0dd92943SRobert Mustacchi {
2511*0dd92943SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(zen_umc_ddr4_map); i++) {
2512*0dd92943SRobert Mustacchi 		if (clock == zen_umc_ddr4_map[i].zufm_reg) {
2513*0dd92943SRobert Mustacchi 			chan->chan_clock[mstate] = zen_umc_ddr4_map[i].zufm_mhz;
2514*0dd92943SRobert Mustacchi 			chan->chan_speed[mstate] =
2515*0dd92943SRobert Mustacchi 			    zen_umc_ddr4_map[i].zufm_mts2;
2516*0dd92943SRobert Mustacchi 			break;
2517*0dd92943SRobert Mustacchi 		}
2518*0dd92943SRobert Mustacchi 	}
2519*0dd92943SRobert Mustacchi }
2520*0dd92943SRobert Mustacchi 
2521*0dd92943SRobert Mustacchi static void
zen_umc_fill_chan_hyb_lpddr5(zen_umc_chan_t * chan,uint_t mstate)2522*0dd92943SRobert Mustacchi zen_umc_fill_chan_hyb_lpddr5(zen_umc_chan_t *chan, uint_t mstate)
2523*0dd92943SRobert Mustacchi {
2524*0dd92943SRobert Mustacchi 	const uint32_t reg = chan->chan_dramcfg_raw[mstate];
2525*0dd92943SRobert Mustacchi 	const uint32_t wck = UMC_DRAMCFG_HYB_GET_WCLKRATIO(reg);
2526*0dd92943SRobert Mustacchi 	const uint32_t clock = UMC_DRAMCFG_HYB_GET_MEMCLK(reg);
2527*0dd92943SRobert Mustacchi 	boolean_t twox;
2528*0dd92943SRobert Mustacchi 
2529*0dd92943SRobert Mustacchi 	switch (wck) {
2530*0dd92943SRobert Mustacchi 	case UMC_DRAMCFG_WCLKRATIO_1TO2:
2531*0dd92943SRobert Mustacchi 		twox = B_TRUE;
2532*0dd92943SRobert Mustacchi 		break;
2533*0dd92943SRobert Mustacchi 	case UMC_DRAMCFG_WCLKRATIO_1TO4:
2534*0dd92943SRobert Mustacchi 		twox = B_FALSE;
2535*0dd92943SRobert Mustacchi 		break;
2536*0dd92943SRobert Mustacchi 	default:
2537*0dd92943SRobert Mustacchi 		return;
2538*0dd92943SRobert Mustacchi 	}
2539*0dd92943SRobert Mustacchi 
2540*0dd92943SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(zen_umc_lpddr5_map); i++) {
2541*0dd92943SRobert Mustacchi 		if (clock == zen_umc_lpddr5_map[i].zufm_reg) {
2542*0dd92943SRobert Mustacchi 			chan->chan_clock[mstate] =
2543*0dd92943SRobert Mustacchi 			    zen_umc_lpddr5_map[i].zufm_mhz;
2544*0dd92943SRobert Mustacchi 
2545*0dd92943SRobert Mustacchi 			if (twox) {
2546*0dd92943SRobert Mustacchi 				chan->chan_speed[mstate] =
2547*0dd92943SRobert Mustacchi 				    zen_umc_lpddr5_map[i].zufm_mts2;
2548*0dd92943SRobert Mustacchi 			} else {
2549*0dd92943SRobert Mustacchi 				chan->chan_speed[mstate] =
2550*0dd92943SRobert Mustacchi 				    zen_umc_lpddr5_map[i].zufm_mts4;
2551*0dd92943SRobert Mustacchi 			}
2552*0dd92943SRobert Mustacchi 			break;
2553*0dd92943SRobert Mustacchi 		}
255471815ce7SRobert Mustacchi 	}
2555*0dd92943SRobert Mustacchi }
2556*0dd92943SRobert Mustacchi 
2557*0dd92943SRobert Mustacchi /*
2558*0dd92943SRobert Mustacchi  * Determine the current operating frequency of the channel. This varies based
2559*0dd92943SRobert Mustacchi  * upon the type of UMC that we're operating on as there are multiple ways to
2560*0dd92943SRobert Mustacchi  * determine this. There are up to four memory P-states that exist in the UMC.
2561*0dd92943SRobert Mustacchi  * This grabs it for a single P-state at a time.
2562*0dd92943SRobert Mustacchi  *
2563*0dd92943SRobert Mustacchi  * Unlike other things, if we cannot determine the frequency of the clock or
2564*0dd92943SRobert Mustacchi  * transfer speed, we do not consider this fatal because that does not stop
2565*0dd92943SRobert Mustacchi  * decoding. It only means that we cannot give a bit of useful information to
2566*0dd92943SRobert Mustacchi  * topo.
2567*0dd92943SRobert Mustacchi  */
2568*0dd92943SRobert Mustacchi static void
zen_umc_fill_chan_freq(zen_umc_t * umc,zen_umc_chan_t * chan,uint_t mstate)2569*0dd92943SRobert Mustacchi zen_umc_fill_chan_freq(zen_umc_t *umc, zen_umc_chan_t *chan, uint_t mstate)
2570*0dd92943SRobert Mustacchi {
2571*0dd92943SRobert Mustacchi 	const uint32_t cfg = chan->chan_dramcfg_raw[mstate];
2572*0dd92943SRobert Mustacchi 	const umc_dimm_type_t dimm_type = chan->chan_type;
257371815ce7SRobert Mustacchi 
2574*0dd92943SRobert Mustacchi 	switch (umc->umc_fdata->zufd_umc_style) {
2575*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_HYBRID_LPDDR5:
2576*0dd92943SRobert Mustacchi 		if (dimm_type == UMC_DIMM_T_LPDDR5) {
2577*0dd92943SRobert Mustacchi 			zen_umc_fill_chan_hyb_lpddr5(chan, mstate);
2578*0dd92943SRobert Mustacchi 		} else if (dimm_type != UMC_DIMM_T_LPDDR4) {
2579*0dd92943SRobert Mustacchi 			zen_umc_fill_chan_ddr4(chan, mstate,
2580*0dd92943SRobert Mustacchi 			    UMC_DRAMCFG_HYB_GET_MEMCLK(cfg));
2581*0dd92943SRobert Mustacchi 		}
2582*0dd92943SRobert Mustacchi 		break;
2583*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4:
2584*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4_APU:
2585*0dd92943SRobert Mustacchi 		zen_umc_fill_chan_ddr4(chan, mstate,
2586*0dd92943SRobert Mustacchi 		    UMC_DRAMCFG_DDR4_GET_MEMCLK(cfg));
2587*0dd92943SRobert Mustacchi 		break;
2588*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5:
2589*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5_APU:
2590*0dd92943SRobert Mustacchi 		chan->chan_clock[mstate] = UMC_DRAMCFG_DDR5_GET_MEMCLK(cfg);
2591*0dd92943SRobert Mustacchi 		if (dimm_type == UMC_DIMM_T_DDR5) {
2592*0dd92943SRobert Mustacchi 			chan->chan_speed[mstate] = 2 * chan->chan_clock[mstate];
2593*0dd92943SRobert Mustacchi 		} else if (dimm_type == UMC_DIMM_T_LPDDR5) {
2594*0dd92943SRobert Mustacchi 			switch (UMC_DRAMCFG_LPDDR5_GET_WCKRATIO(cfg)) {
2595*0dd92943SRobert Mustacchi 			case UMC_DRAMCFG_WCLKRATIO_1TO2:
2596*0dd92943SRobert Mustacchi 				chan->chan_speed[mstate] = 2 *
2597*0dd92943SRobert Mustacchi 				    chan->chan_clock[mstate];
2598*0dd92943SRobert Mustacchi 				break;
2599*0dd92943SRobert Mustacchi 			case UMC_DRAMCFG_WCLKRATIO_1TO4:
2600*0dd92943SRobert Mustacchi 				chan->chan_speed[mstate] = 4 *
2601*0dd92943SRobert Mustacchi 				    chan->chan_clock[mstate];
2602*0dd92943SRobert Mustacchi 				break;
2603*0dd92943SRobert Mustacchi 			default:
2604*0dd92943SRobert Mustacchi 				break;
2605*0dd92943SRobert Mustacchi 			}
2606*0dd92943SRobert Mustacchi 		}
2607*0dd92943SRobert Mustacchi 		break;
260871815ce7SRobert Mustacchi 	}
260971815ce7SRobert Mustacchi }
261071815ce7SRobert Mustacchi 
261171815ce7SRobert Mustacchi /*
261271815ce7SRobert Mustacchi  * Fill common channel information. While the locations of many of the registers
261371815ce7SRobert Mustacchi  * changed between the DDR4-capable and DDR5-capable devices, the actual
261471815ce7SRobert Mustacchi  * contents are the same so we process them together.
261571815ce7SRobert Mustacchi  */
261671815ce7SRobert Mustacchi static boolean_t
zen_umc_fill_chan_hash(zen_umc_t * umc,zen_umc_df_t * df,zen_umc_chan_t * chan,boolean_t ddr4)261771815ce7SRobert Mustacchi zen_umc_fill_chan_hash(zen_umc_t *umc, zen_umc_df_t *df, zen_umc_chan_t *chan,
261871815ce7SRobert Mustacchi     boolean_t ddr4)
261971815ce7SRobert Mustacchi {
262071815ce7SRobert Mustacchi 	int ret;
2621ba215efeSKeith M Wesolowski 	smn_reg_t reg;
262271815ce7SRobert Mustacchi 	uint32_t val;
262371815ce7SRobert Mustacchi 
262471815ce7SRobert Mustacchi 	const umc_chan_hash_flags_t flags = umc->umc_fdata->zufd_chan_hash;
262571815ce7SRobert Mustacchi 	const uint32_t id = chan->chan_logid;
262671815ce7SRobert Mustacchi 	umc_chan_hash_t *chash = &chan->chan_hash;
262771815ce7SRobert Mustacchi 	chash->uch_flags = flags;
262871815ce7SRobert Mustacchi 
262971815ce7SRobert Mustacchi 	if ((flags & UMC_CHAN_HASH_F_BANK) != 0) {
263071815ce7SRobert Mustacchi 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_BANK_HASH; i++) {
263171815ce7SRobert Mustacchi 			umc_bank_hash_t *bank = &chash->uch_bank_hashes[i];
263271815ce7SRobert Mustacchi 
263371815ce7SRobert Mustacchi 			if (ddr4) {
263471815ce7SRobert Mustacchi 				reg = UMC_BANK_HASH_DDR4(id, i);
263571815ce7SRobert Mustacchi 			} else {
263671815ce7SRobert Mustacchi 				reg = UMC_BANK_HASH_DDR5(id, i);
263771815ce7SRobert Mustacchi 			}
263871815ce7SRobert Mustacchi 
26394adf43b0SKeith M Wesolowski 			if ((ret = amdzen_c_smn_read(df->zud_dfno, reg,
264071815ce7SRobert Mustacchi 			    &val)) != 0) {
264171815ce7SRobert Mustacchi 				dev_err(umc->umc_dip, CE_WARN, "failed to read "
2642ba215efeSKeith M Wesolowski 				    "bank hash register %x: %d",
2643ba215efeSKeith M Wesolowski 				    SMN_REG_ADDR(reg), ret);
264471815ce7SRobert Mustacchi 				return (B_FALSE);
264571815ce7SRobert Mustacchi 			}
264671815ce7SRobert Mustacchi 
264771815ce7SRobert Mustacchi 			bank->ubh_row_xor = UMC_BANK_HASH_GET_ROW(val);
264871815ce7SRobert Mustacchi 			bank->ubh_col_xor = UMC_BANK_HASH_GET_COL(val);
264971815ce7SRobert Mustacchi 			bank->ubh_en = UMC_BANK_HASH_GET_EN(val);
265071815ce7SRobert Mustacchi 		}
265171815ce7SRobert Mustacchi 	}
265271815ce7SRobert Mustacchi 
265371815ce7SRobert Mustacchi 	if ((flags & UMC_CHAN_HASH_F_RM) != 0) {
265471815ce7SRobert Mustacchi 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_RM_HASH; i++) {
265571815ce7SRobert Mustacchi 			uint64_t addr;
265671815ce7SRobert Mustacchi 			umc_addr_hash_t *rm = &chash->uch_rm_hashes[i];
265771815ce7SRobert Mustacchi 
265871815ce7SRobert Mustacchi 			if (ddr4) {
265971815ce7SRobert Mustacchi 				reg = UMC_RANK_HASH_DDR4(id, i);
266071815ce7SRobert Mustacchi 			} else {
266171815ce7SRobert Mustacchi 				reg = UMC_RANK_HASH_DDR5(id, i);
266271815ce7SRobert Mustacchi 			}
266371815ce7SRobert Mustacchi 
26644adf43b0SKeith M Wesolowski 			if ((ret = amdzen_c_smn_read(df->zud_dfno, reg,
266571815ce7SRobert Mustacchi 			    &val)) != 0) {
266671815ce7SRobert Mustacchi 				dev_err(umc->umc_dip, CE_WARN, "failed to read "
2667ba215efeSKeith M Wesolowski 				    "rm hash register %x: %d",
2668ba215efeSKeith M Wesolowski 				    SMN_REG_ADDR(reg), ret);
266971815ce7SRobert Mustacchi 				return (B_FALSE);
267071815ce7SRobert Mustacchi 			}
267171815ce7SRobert Mustacchi 
267271815ce7SRobert Mustacchi 			addr = UMC_RANK_HASH_GET_ADDR(val);
267371815ce7SRobert Mustacchi 			rm->uah_addr_xor = addr << UMC_RANK_HASH_SHIFT;
267471815ce7SRobert Mustacchi 			rm->uah_en = UMC_RANK_HASH_GET_EN(val);
267571815ce7SRobert Mustacchi 
267671815ce7SRobert Mustacchi 			if (ddr4 || (umc->umc_fdata->zufd_flags &
267771815ce7SRobert Mustacchi 			    ZEN_UMC_FAM_F_UMC_EADDR) == 0) {
267871815ce7SRobert Mustacchi 				continue;
267971815ce7SRobert Mustacchi 			}
268071815ce7SRobert Mustacchi 
268171815ce7SRobert Mustacchi 			reg = UMC_RANK_HASH_EXT_DDR5(id, i);
26824adf43b0SKeith M Wesolowski 			if ((ret = amdzen_c_smn_read(df->zud_dfno, reg,
268371815ce7SRobert Mustacchi 			    &val)) != 0) {
268471815ce7SRobert Mustacchi 				dev_err(umc->umc_dip, CE_WARN, "failed to read "
2685ba215efeSKeith M Wesolowski 				    "rm hash ext register %x: %d",
2686ba215efeSKeith M Wesolowski 				    SMN_REG_ADDR(reg), ret);
268771815ce7SRobert Mustacchi 				return (B_FALSE);
268871815ce7SRobert Mustacchi 			}
268971815ce7SRobert Mustacchi 
269071815ce7SRobert Mustacchi 			addr = UMC_RANK_HASH_EXT_GET_ADDR(val);
269171815ce7SRobert Mustacchi 			rm->uah_addr_xor |= addr <<
269271815ce7SRobert Mustacchi 			    UMC_RANK_HASH_EXT_ADDR_SHIFT;
269371815ce7SRobert Mustacchi 		}
269471815ce7SRobert Mustacchi 	}
269571815ce7SRobert Mustacchi 
269671815ce7SRobert Mustacchi 	if ((flags & UMC_CHAN_HASH_F_PC) != 0) {
269771815ce7SRobert Mustacchi 		umc_pc_hash_t *pc = &chash->uch_pc_hash;
269871815ce7SRobert Mustacchi 
269971815ce7SRobert Mustacchi 		if (ddr4) {
270071815ce7SRobert Mustacchi 			reg = UMC_PC_HASH_DDR4(id);
270171815ce7SRobert Mustacchi 		} else {
270271815ce7SRobert Mustacchi 			reg = UMC_PC_HASH_DDR5(id);
270371815ce7SRobert Mustacchi 		}
270471815ce7SRobert Mustacchi 
27054adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
270671815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read pc hash "
2707ba215efeSKeith M Wesolowski 			    "register %x: %d", SMN_REG_ADDR(reg), ret);
270871815ce7SRobert Mustacchi 			return (B_FALSE);
270971815ce7SRobert Mustacchi 		}
271071815ce7SRobert Mustacchi 
271171815ce7SRobert Mustacchi 		pc->uph_row_xor = UMC_PC_HASH_GET_ROW(val);
271271815ce7SRobert Mustacchi 		pc->uph_col_xor = UMC_PC_HASH_GET_COL(val);
271371815ce7SRobert Mustacchi 		pc->uph_en = UMC_PC_HASH_GET_EN(val);
271471815ce7SRobert Mustacchi 
271571815ce7SRobert Mustacchi 		if (ddr4) {
271671815ce7SRobert Mustacchi 			reg = UMC_PC_HASH2_DDR4(id);
271771815ce7SRobert Mustacchi 		} else {
271871815ce7SRobert Mustacchi 			reg = UMC_PC_HASH2_DDR5(id);
271971815ce7SRobert Mustacchi 		}
272071815ce7SRobert Mustacchi 
27214adf43b0SKeith M Wesolowski 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
272271815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read pc hash "
2723ba215efeSKeith M Wesolowski 			    "2 register %x: %d", SMN_REG_ADDR(reg), ret);
272471815ce7SRobert Mustacchi 			return (B_FALSE);
272571815ce7SRobert Mustacchi 		}
272671815ce7SRobert Mustacchi 
272771815ce7SRobert Mustacchi 		pc->uph_bank_xor = UMC_PC_HASH2_GET_BANK(val);
272871815ce7SRobert Mustacchi 	}
272971815ce7SRobert Mustacchi 
273071815ce7SRobert Mustacchi 	if ((flags & UMC_CHAN_HASH_F_CS) != 0) {
273171815ce7SRobert Mustacchi 		for (uint_t i = 0; i < ZEN_UMC_MAX_CHAN_CS_HASH; i++) {
273271815ce7SRobert Mustacchi 			uint64_t addr;
273371815ce7SRobert Mustacchi 			umc_addr_hash_t *rm = &chash->uch_cs_hashes[i];
273471815ce7SRobert Mustacchi 
273571815ce7SRobert Mustacchi 			if (ddr4) {
273671815ce7SRobert Mustacchi 				reg = UMC_CS_HASH_DDR4(id, i);
273771815ce7SRobert Mustacchi 			} else {
273871815ce7SRobert Mustacchi 				reg = UMC_CS_HASH_DDR5(id, i);
273971815ce7SRobert Mustacchi 			}
274071815ce7SRobert Mustacchi 
27414adf43b0SKeith M Wesolowski 			if ((ret = amdzen_c_smn_read(df->zud_dfno, reg,
274271815ce7SRobert Mustacchi 			    &val)) != 0) {
274371815ce7SRobert Mustacchi 				dev_err(umc->umc_dip, CE_WARN, "failed to read "
2744ba215efeSKeith M Wesolowski 				    "cs hash register %x", SMN_REG_ADDR(reg));
274571815ce7SRobert Mustacchi 				return (B_FALSE);
274671815ce7SRobert Mustacchi 			}
274771815ce7SRobert Mustacchi 
274871815ce7SRobert Mustacchi 			addr = UMC_CS_HASH_GET_ADDR(val);
274971815ce7SRobert Mustacchi 			rm->uah_addr_xor = addr << UMC_CS_HASH_SHIFT;
275071815ce7SRobert Mustacchi 			rm->uah_en = UMC_CS_HASH_GET_EN(val);
275171815ce7SRobert Mustacchi 
275271815ce7SRobert Mustacchi 			if (ddr4 || (umc->umc_fdata->zufd_flags &
275371815ce7SRobert Mustacchi 			    ZEN_UMC_FAM_F_UMC_EADDR) == 0) {
275471815ce7SRobert Mustacchi 				continue;
275571815ce7SRobert Mustacchi 			}
275671815ce7SRobert Mustacchi 
275771815ce7SRobert Mustacchi 			reg = UMC_CS_HASH_EXT_DDR5(id, i);
27584adf43b0SKeith M Wesolowski 			if ((ret = amdzen_c_smn_read(df->zud_dfno, reg,
275971815ce7SRobert Mustacchi 			    &val)) != 0) {
276071815ce7SRobert Mustacchi 				dev_err(umc->umc_dip, CE_WARN, "failed to read "
2761ba215efeSKeith M Wesolowski 				    "cs hash ext register %x",
2762ba215efeSKeith M Wesolowski 				    SMN_REG_ADDR(reg));
276371815ce7SRobert Mustacchi 				return (B_FALSE);
276471815ce7SRobert Mustacchi 			}
276571815ce7SRobert Mustacchi 
276671815ce7SRobert Mustacchi 			addr = UMC_CS_HASH_EXT_GET_ADDR(val);
276771815ce7SRobert Mustacchi 			rm->uah_addr_xor |= addr << UMC_CS_HASH_EXT_ADDR_SHIFT;
276871815ce7SRobert Mustacchi 		}
276971815ce7SRobert Mustacchi 	}
277071815ce7SRobert Mustacchi 
277171815ce7SRobert Mustacchi 	return (B_TRUE);
277271815ce7SRobert Mustacchi }
277371815ce7SRobert Mustacchi 
277471815ce7SRobert Mustacchi /*
277571815ce7SRobert Mustacchi  * This fills in settings that we care about which are valid for the entire
277671815ce7SRobert Mustacchi  * channel and are the same between DDR4/5 capable devices.
277771815ce7SRobert Mustacchi  */
277871815ce7SRobert Mustacchi static boolean_t
zen_umc_fill_chan(zen_umc_t * umc,zen_umc_df_t * df,zen_umc_chan_t * chan)277971815ce7SRobert Mustacchi zen_umc_fill_chan(zen_umc_t *umc, zen_umc_df_t *df, zen_umc_chan_t *chan)
278071815ce7SRobert Mustacchi {
2781ba215efeSKeith M Wesolowski 	uint32_t val;
2782ba215efeSKeith M Wesolowski 	smn_reg_t reg;
278371815ce7SRobert Mustacchi 	const uint32_t id = chan->chan_logid;
278471815ce7SRobert Mustacchi 	int ret;
278571815ce7SRobert Mustacchi 	boolean_t ddr4;
278671815ce7SRobert Mustacchi 
278771815ce7SRobert Mustacchi 	if (umc->umc_fdata->zufd_umc_style == ZEN_UMC_UMC_S_DDR4 ||
278871815ce7SRobert Mustacchi 	    umc->umc_fdata->zufd_umc_style == ZEN_UMC_UMC_S_DDR4_APU) {
278971815ce7SRobert Mustacchi 		ddr4 = B_TRUE;
279071815ce7SRobert Mustacchi 	} else {
279171815ce7SRobert Mustacchi 		ddr4 = B_FALSE;
279271815ce7SRobert Mustacchi 	}
279371815ce7SRobert Mustacchi 
279471815ce7SRobert Mustacchi 	/*
279571815ce7SRobert Mustacchi 	 * Begin by gathering all of the information related to hashing. What is
279671815ce7SRobert Mustacchi 	 * valid here varies based on the actual chip family and then the
279771815ce7SRobert Mustacchi 	 * registers vary based on DDR4 and DDR5.
279871815ce7SRobert Mustacchi 	 */
279971815ce7SRobert Mustacchi 	if (!zen_umc_fill_chan_hash(umc, df, chan, ddr4)) {
280071815ce7SRobert Mustacchi 		return (B_FALSE);
280171815ce7SRobert Mustacchi 	}
280271815ce7SRobert Mustacchi 
280371815ce7SRobert Mustacchi 	reg = UMC_UMCCFG(id);
28044adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
280571815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read UMC "
2806ba215efeSKeith M Wesolowski 		    "configuration register %x: %d", SMN_REG_ADDR(reg), ret);
280771815ce7SRobert Mustacchi 		return (B_FALSE);
280871815ce7SRobert Mustacchi 	}
280971815ce7SRobert Mustacchi 
281071815ce7SRobert Mustacchi 	chan->chan_umccfg_raw = val;
281171815ce7SRobert Mustacchi 	if (UMC_UMCCFG_GET_ECC_EN(val)) {
281271815ce7SRobert Mustacchi 		chan->chan_flags |= UMC_CHAN_F_ECC_EN;
281371815ce7SRobert Mustacchi 	}
281471815ce7SRobert Mustacchi 
2815*0dd92943SRobert Mustacchi 	/*
2816*0dd92943SRobert Mustacchi 	 * Grab the DRAM configuration register. This can be used to determine
2817*0dd92943SRobert Mustacchi 	 * the frequency and speed of the memory channel. At this time we only
2818*0dd92943SRobert Mustacchi 	 * capture Memory P-state 0.
2819*0dd92943SRobert Mustacchi 	 */
2820*0dd92943SRobert Mustacchi 	reg = UMC_DRAMCFG(id, 0);
2821*0dd92943SRobert Mustacchi 
282271815ce7SRobert Mustacchi 	/*
282371815ce7SRobert Mustacchi 	 * This register contains information to determine the type of DIMM.
2824*0dd92943SRobert Mustacchi 	 * All DIMMs in the channel must be the same type so we leave this
2825*0dd92943SRobert Mustacchi 	 * setting on the channel. Once we have that, we proceed to obtain the
2826*0dd92943SRobert Mustacchi 	 * currently configuration information for the DRAM in each memory
2827*0dd92943SRobert Mustacchi 	 * P-state.
282871815ce7SRobert Mustacchi 	 */
2829*0dd92943SRobert Mustacchi 	zen_umc_fill_ddr_type(umc, chan);
2830*0dd92943SRobert Mustacchi 	for (uint_t i = 0; i < ZEN_UMC_NMEM_PSTATES; i++) {
2831*0dd92943SRobert Mustacchi 		chan->chan_clock[i] = ZEN_UMC_UNKNOWN_FREQ;
2832*0dd92943SRobert Mustacchi 		chan->chan_speed[i] = ZEN_UMC_UNKNOWN_FREQ;
2833*0dd92943SRobert Mustacchi 
2834*0dd92943SRobert Mustacchi 		reg = UMC_DRAMCFG(id, i);
2835*0dd92943SRobert Mustacchi 		if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
2836*0dd92943SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "failed to read DRAM "
2837*0dd92943SRobert Mustacchi 			    "Configuration register P-state %u %x: %d", i,
2838*0dd92943SRobert Mustacchi 			    SMN_REG_ADDR(reg), ret);
2839*0dd92943SRobert Mustacchi 			return (B_FALSE);
2840*0dd92943SRobert Mustacchi 		}
2841*0dd92943SRobert Mustacchi 		chan->chan_dramcfg_raw[i] = val;
2842*0dd92943SRobert Mustacchi 
2843*0dd92943SRobert Mustacchi 		zen_umc_fill_chan_freq(umc, chan, i);
2844*0dd92943SRobert Mustacchi 	}
284571815ce7SRobert Mustacchi 
284671815ce7SRobert Mustacchi 	/*
284771815ce7SRobert Mustacchi 	 * Grab data that we can use to determine if we're scrambling or
284871815ce7SRobert Mustacchi 	 * encrypting regions of memory.
284971815ce7SRobert Mustacchi 	 */
285071815ce7SRobert Mustacchi 	reg = UMC_DATACTL(id);
28514adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
285271815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read data control "
2853ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
285471815ce7SRobert Mustacchi 		return (B_FALSE);
285571815ce7SRobert Mustacchi 	}
285671815ce7SRobert Mustacchi 	chan->chan_datactl_raw = val;
285771815ce7SRobert Mustacchi 	if (UMC_DATACTL_GET_SCRAM_EN(val)) {
285871815ce7SRobert Mustacchi 		chan->chan_flags |= UMC_CHAN_F_SCRAMBLE_EN;
285971815ce7SRobert Mustacchi 	}
286071815ce7SRobert Mustacchi 
286171815ce7SRobert Mustacchi 	if (UMC_DATACTL_GET_ENCR_EN(val)) {
286271815ce7SRobert Mustacchi 		chan->chan_flags |= UMC_CHAN_F_ENCR_EN;
286371815ce7SRobert Mustacchi 	}
286471815ce7SRobert Mustacchi 
286571815ce7SRobert Mustacchi 	/*
286671815ce7SRobert Mustacchi 	 * At the moment we snapshot the raw ECC control information. When we do
286771815ce7SRobert Mustacchi 	 * further work of making this a part of the MCA/X decoding, we'll want
286871815ce7SRobert Mustacchi 	 * to further take this apart for syndrome decoding. Until then, simply
286971815ce7SRobert Mustacchi 	 * cache it for future us and observability.
287071815ce7SRobert Mustacchi 	 */
287171815ce7SRobert Mustacchi 	reg = UMC_ECCCTL(id);
28724adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
287371815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read ECC control "
2874ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
287571815ce7SRobert Mustacchi 		return (B_FALSE);
287671815ce7SRobert Mustacchi 	}
287771815ce7SRobert Mustacchi 	chan->chan_eccctl_raw = val;
287871815ce7SRobert Mustacchi 
287971815ce7SRobert Mustacchi 	/*
288071815ce7SRobert Mustacchi 	 * Read and snapshot the UMC capability registers for debugging in the
288171815ce7SRobert Mustacchi 	 * future.
288271815ce7SRobert Mustacchi 	 */
288371815ce7SRobert Mustacchi 	reg = UMC_UMCCAP(id);
28844adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
288571815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read UMC cap"
2886ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
288771815ce7SRobert Mustacchi 		return (B_FALSE);
288871815ce7SRobert Mustacchi 	}
288971815ce7SRobert Mustacchi 	chan->chan_umccap_raw = val;
289071815ce7SRobert Mustacchi 
289171815ce7SRobert Mustacchi 	reg = UMC_UMCCAP_HI(id);
28924adf43b0SKeith M Wesolowski 	if ((ret = amdzen_c_smn_read(df->zud_dfno, reg, &val)) != 0) {
289371815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "failed to read UMC cap high "
2894ba215efeSKeith M Wesolowski 		    "register %x: %d", SMN_REG_ADDR(reg), ret);
289571815ce7SRobert Mustacchi 		return (B_FALSE);
289671815ce7SRobert Mustacchi 	}
289771815ce7SRobert Mustacchi 	chan->chan_umccap_hi_raw = val;
289871815ce7SRobert Mustacchi 
289971815ce7SRobert Mustacchi 	return (B_TRUE);
290071815ce7SRobert Mustacchi }
290171815ce7SRobert Mustacchi 
290271815ce7SRobert Mustacchi static int
zen_umc_fill_umc_cb(const uint_t dfno,const uint32_t fabid,const uint32_t instid,void * arg)290371815ce7SRobert Mustacchi zen_umc_fill_umc_cb(const uint_t dfno, const uint32_t fabid,
290471815ce7SRobert Mustacchi     const uint32_t instid, void *arg)
290571815ce7SRobert Mustacchi {
290671815ce7SRobert Mustacchi 	zen_umc_t *umc = arg;
290771815ce7SRobert Mustacchi 	zen_umc_df_t *df = &umc->umc_dfs[dfno];
290871815ce7SRobert Mustacchi 	zen_umc_chan_t *chan = &df->zud_chan[df->zud_nchan];
290971815ce7SRobert Mustacchi 
291071815ce7SRobert Mustacchi 	df->zud_nchan++;
291171815ce7SRobert Mustacchi 	VERIFY3U(df->zud_nchan, <=, ZEN_UMC_MAX_UMCS);
291271815ce7SRobert Mustacchi 
291371815ce7SRobert Mustacchi 	/*
291471815ce7SRobert Mustacchi 	 * The data fabric is generally organized such that all UMC entries
291571815ce7SRobert Mustacchi 	 * should be continuous in their fabric ID space; however, we don't
291671815ce7SRobert Mustacchi 	 * want to rely on specific ID locations. The UMC SMN addresses are
291771815ce7SRobert Mustacchi 	 * organized in a relative order. To determine the SMN ID to use (the
291871815ce7SRobert Mustacchi 	 * chan_logid) we end up making the following assumptions:
291971815ce7SRobert Mustacchi 	 *
292071815ce7SRobert Mustacchi 	 *  o The iteration order will always be from the lowest component ID
292171815ce7SRobert Mustacchi 	 *    to the highest component ID.
292271815ce7SRobert Mustacchi 	 *  o The relative order that we encounter will be the same as the SMN
292371815ce7SRobert Mustacchi 	 *    order. That is, the first thing we find (regardless of component
292471815ce7SRobert Mustacchi 	 *    ID) will be SMN UMC entry 0, the next 1, etc.
292571815ce7SRobert Mustacchi 	 */
292671815ce7SRobert Mustacchi 	chan->chan_logid = df->zud_nchan - 1;
292771815ce7SRobert Mustacchi 	chan->chan_fabid = fabid;
292871815ce7SRobert Mustacchi 	chan->chan_instid = instid;
292971815ce7SRobert Mustacchi 	chan->chan_nrules = umc->umc_fdata->zufd_cs_nrules;
293071815ce7SRobert Mustacchi 	for (uint_t i = 0; i < umc->umc_fdata->zufd_cs_nrules; i++) {
293171815ce7SRobert Mustacchi 		if (zen_umc_read_dram_rule(umc, dfno, instid, i,
293271815ce7SRobert Mustacchi 		    &chan->chan_rules[i]) != 0) {
293371815ce7SRobert Mustacchi 			return (-1);
293471815ce7SRobert Mustacchi 		}
293571815ce7SRobert Mustacchi 	}
293671815ce7SRobert Mustacchi 
293771815ce7SRobert Mustacchi 	for (uint_t i = 0; i < umc->umc_fdata->zufd_cs_nrules - 1; i++) {
293871815ce7SRobert Mustacchi 		int ret;
293971815ce7SRobert Mustacchi 		uint32_t offset;
294071815ce7SRobert Mustacchi 		uint64_t t;
294171815ce7SRobert Mustacchi 		df_reg_def_t off_reg;
294271815ce7SRobert Mustacchi 		chan_offset_t *offp = &chan->chan_offsets[i];
294371815ce7SRobert Mustacchi 
294471815ce7SRobert Mustacchi 		switch (umc->umc_df_rev) {
294571815ce7SRobert Mustacchi 		case DF_REV_2:
294671815ce7SRobert Mustacchi 		case DF_REV_3:
294771815ce7SRobert Mustacchi 		case DF_REV_3P5:
294871815ce7SRobert Mustacchi 			ASSERT3U(i, ==, 0);
294971815ce7SRobert Mustacchi 			off_reg = DF_DRAM_OFFSET_V2;
295071815ce7SRobert Mustacchi 			break;
295171815ce7SRobert Mustacchi 		case DF_REV_4:
295271815ce7SRobert Mustacchi 			off_reg = DF_DRAM_OFFSET_V4(i);
295371815ce7SRobert Mustacchi 			break;
295471815ce7SRobert Mustacchi 		default:
295571815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!encountered "
295671815ce7SRobert Mustacchi 			    "unsupported DF revision processing DRAM Offsets: "
295771815ce7SRobert Mustacchi 			    "0x%x", umc->umc_df_rev);
295871815ce7SRobert Mustacchi 			return (-1);
295971815ce7SRobert Mustacchi 		}
296071815ce7SRobert Mustacchi 
296171815ce7SRobert Mustacchi 		if ((ret = amdzen_c_df_read32(dfno, instid, off_reg,
296271815ce7SRobert Mustacchi 		    &offset)) != 0) {
296371815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!failed to read DRAM "
296471815ce7SRobert Mustacchi 			    "offset %u on 0x%x/0x%x: %d", i, dfno, instid, ret);
296571815ce7SRobert Mustacchi 			return (-1);
296671815ce7SRobert Mustacchi 		}
296771815ce7SRobert Mustacchi 
296871815ce7SRobert Mustacchi 		offp->cho_raw = offset;
296971815ce7SRobert Mustacchi 		offp->cho_valid = DF_DRAM_OFFSET_GET_EN(offset);
297071815ce7SRobert Mustacchi 
297171815ce7SRobert Mustacchi 		switch (umc->umc_df_rev) {
297271815ce7SRobert Mustacchi 		case DF_REV_2:
297371815ce7SRobert Mustacchi 			t = DF_DRAM_OFFSET_V2_GET_OFFSET(offset);
297471815ce7SRobert Mustacchi 			break;
297571815ce7SRobert Mustacchi 		case DF_REV_3:
297671815ce7SRobert Mustacchi 		case DF_REV_3P5:
297771815ce7SRobert Mustacchi 			t = DF_DRAM_OFFSET_V3_GET_OFFSET(offset);
297871815ce7SRobert Mustacchi 			break;
297971815ce7SRobert Mustacchi 		case DF_REV_4:
298071815ce7SRobert Mustacchi 			t = DF_DRAM_OFFSET_V4_GET_OFFSET(offset);
298171815ce7SRobert Mustacchi 			break;
298271815ce7SRobert Mustacchi 		default:
298371815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!encountered "
298471815ce7SRobert Mustacchi 			    "unsupported DF revision processing DRAM Offsets: "
298571815ce7SRobert Mustacchi 			    "0x%x", umc->umc_df_rev);
298671815ce7SRobert Mustacchi 			return (-1);
298771815ce7SRobert Mustacchi 		}
298871815ce7SRobert Mustacchi 		offp->cho_offset = t << DF_DRAM_OFFSET_SHIFT;
298971815ce7SRobert Mustacchi 	}
299071815ce7SRobert Mustacchi 
299171815ce7SRobert Mustacchi 	/*
299271815ce7SRobert Mustacchi 	 * If this platform supports our favorete Zen 3 6-channel hash special
299371815ce7SRobert Mustacchi 	 * then we need to grab the NP2 configuration registers. This will only
299471815ce7SRobert Mustacchi 	 * be referenced if this channel is actually being used for a 6-channel
299571815ce7SRobert Mustacchi 	 * hash, so even if the contents are weird that should still be ok.
299671815ce7SRobert Mustacchi 	 */
299771815ce7SRobert Mustacchi 	if ((umc->umc_fdata->zufd_flags & ZEN_UMC_FAM_F_NP2) != 0) {
299871815ce7SRobert Mustacchi 		uint32_t np2;
299971815ce7SRobert Mustacchi 		int ret;
300071815ce7SRobert Mustacchi 
300171815ce7SRobert Mustacchi 		if ((ret = amdzen_c_df_read32(dfno, instid, DF_NP2_CONFIG_V3,
300271815ce7SRobert Mustacchi 		    &np2)) != 0) {
300371815ce7SRobert Mustacchi 			dev_err(umc->umc_dip, CE_WARN, "!failed to read NP2 "
300471815ce7SRobert Mustacchi 			    "config: %d", ret);
300571815ce7SRobert Mustacchi 			return (-1);
300671815ce7SRobert Mustacchi 		}
300771815ce7SRobert Mustacchi 
300871815ce7SRobert Mustacchi 		chan->chan_np2_raw = np2;
300971815ce7SRobert Mustacchi 		chan->chan_np2_space0 = DF_NP2_CONFIG_V3_GET_SPACE0(np2);
301071815ce7SRobert Mustacchi 	}
301171815ce7SRobert Mustacchi 
301271815ce7SRobert Mustacchi 	/*
301371815ce7SRobert Mustacchi 	 * Now that we have everything we need from the data fabric, read out
301471815ce7SRobert Mustacchi 	 * the rest of what we need from the UMC channel data in SMN register
301571815ce7SRobert Mustacchi 	 * space.
301671815ce7SRobert Mustacchi 	 */
301771815ce7SRobert Mustacchi 	switch (umc->umc_fdata->zufd_umc_style) {
301871815ce7SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4:
301971815ce7SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR4_APU:
302071815ce7SRobert Mustacchi 		for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
302171815ce7SRobert Mustacchi 			if (!zen_umc_fill_chan_dimm_ddr4(umc, df, chan, i)) {
302271815ce7SRobert Mustacchi 				return (-1);
302371815ce7SRobert Mustacchi 			}
302471815ce7SRobert Mustacchi 		}
302571815ce7SRobert Mustacchi 		break;
3026*0dd92943SRobert Mustacchi 	case ZEN_UMC_UMC_S_HYBRID_LPDDR5:
302771815ce7SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5:
302871815ce7SRobert Mustacchi 	case ZEN_UMC_UMC_S_DDR5_APU:
302971815ce7SRobert Mustacchi 		for (uint_t i = 0; i < ZEN_UMC_MAX_DIMMS; i++) {
303071815ce7SRobert Mustacchi 			for (uint_t r = 0; r < ZEN_UMC_MAX_CS_PER_DIMM; r++) {
303171815ce7SRobert Mustacchi 				if (!zen_umc_fill_chan_rank_ddr5(umc, df, chan,
303271815ce7SRobert Mustacchi 				    i, r)) {
303371815ce7SRobert Mustacchi 					return (-1);
303471815ce7SRobert Mustacchi 				}
303571815ce7SRobert Mustacchi 			}
303671815ce7SRobert Mustacchi 		}
303771815ce7SRobert Mustacchi 		break;
303871815ce7SRobert Mustacchi 	default:
303971815ce7SRobert Mustacchi 		dev_err(umc->umc_dip, CE_WARN, "!encountered unsupported "
304071815ce7SRobert Mustacchi 		    "Zen family: 0x%x", umc->umc_fdata->zufd_umc_style);
304171815ce7SRobert Mustacchi 		return (-1);
304271815ce7SRobert Mustacchi 	}
304371815ce7SRobert Mustacchi 
304471815ce7SRobert Mustacchi 	if (!zen_umc_fill_chan(umc, df, chan)) {
304571815ce7SRobert Mustacchi 		return (-1);
304671815ce7SRobert Mustacchi 	}
304771815ce7SRobert Mustacchi 
304871815ce7SRobert Mustacchi 	return (0);
304971815ce7SRobert Mustacchi }
305071815ce7SRobert Mustacchi 
305171815ce7SRobert Mustacchi /*
305271815ce7SRobert Mustacchi  * Today there are no privileges for the memory controller information, it is
305371815ce7SRobert Mustacchi  * restricted based on file system permissions.
305471815ce7SRobert Mustacchi  */
305571815ce7SRobert Mustacchi static int
zen_umc_open(dev_t * devp,int flag,int otyp,cred_t * credp)305671815ce7SRobert Mustacchi zen_umc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
305771815ce7SRobert Mustacchi {
305871815ce7SRobert Mustacchi 	zen_umc_t *umc = zen_umc;
305971815ce7SRobert Mustacchi 
306071815ce7SRobert Mustacchi 	if ((flag & (FEXCL | FNDELAY | FNONBLOCK | FWRITE)) != 0) {
306171815ce7SRobert Mustacchi 		return (EINVAL);
306271815ce7SRobert Mustacchi 	}
306371815ce7SRobert Mustacchi 
306471815ce7SRobert Mustacchi 	if (otyp != OTYP_CHR) {
306571815ce7SRobert Mustacchi 		return (EINVAL);
306671815ce7SRobert Mustacchi 	}
306771815ce7SRobert Mustacchi 
306871815ce7SRobert Mustacchi 	if (getminor(*devp) >= umc->umc_ndfs) {
306971815ce7SRobert Mustacchi 		return (ENXIO);
307071815ce7SRobert Mustacchi 	}
307171815ce7SRobert Mustacchi 
307271815ce7SRobert Mustacchi 	return (0);
307371815ce7SRobert Mustacchi }
307471815ce7SRobert Mustacchi 
307571815ce7SRobert Mustacchi static void
zen_umc_ioctl_decode(zen_umc_t * umc,mc_encode_ioc_t * encode)307671815ce7SRobert Mustacchi zen_umc_ioctl_decode(zen_umc_t *umc, mc_encode_ioc_t *encode)
307771815ce7SRobert Mustacchi {
307871815ce7SRobert Mustacchi 	zen_umc_decoder_t dec;
307971815ce7SRobert Mustacchi 	uint32_t sock, die, comp;
308071815ce7SRobert Mustacchi 
308171815ce7SRobert Mustacchi 	bzero(&dec, sizeof (dec));
308271815ce7SRobert Mustacchi 	if (!zen_umc_decode_pa(umc, encode->mcei_pa, &dec)) {
308371815ce7SRobert Mustacchi 		encode->mcei_err = (uint32_t)dec.dec_fail;
308471815ce7SRobert Mustacchi 		encode->mcei_errdata = dec.dec_fail_data;
308571815ce7SRobert Mustacchi 		return;
308671815ce7SRobert Mustacchi 	}
308771815ce7SRobert Mustacchi 
308871815ce7SRobert Mustacchi 	encode->mcei_errdata = 0;
308971815ce7SRobert Mustacchi 	encode->mcei_err = 0;
309071815ce7SRobert Mustacchi 	encode->mcei_chan_addr = dec.dec_norm_addr;
309171815ce7SRobert Mustacchi 	encode->mcei_rank_addr = UINT64_MAX;
309271815ce7SRobert Mustacchi 	encode->mcei_board = 0;
309371815ce7SRobert Mustacchi 	zen_fabric_id_decompose(&umc->umc_decomp, dec.dec_targ_fabid, &sock,
309471815ce7SRobert Mustacchi 	    &die, &comp);
309571815ce7SRobert Mustacchi 	encode->mcei_chip = sock;
309671815ce7SRobert Mustacchi 	encode->mcei_die = die;
309771815ce7SRobert Mustacchi 	encode->mcei_mc = dec.dec_umc_chan->chan_logid;
309871815ce7SRobert Mustacchi 	encode->mcei_chan = 0;
309971815ce7SRobert Mustacchi 	encode->mcei_dimm = dec.dec_dimm_no;
310071815ce7SRobert Mustacchi 	encode->mcei_row = dec.dec_dimm_row;
310171815ce7SRobert Mustacchi 	encode->mcei_column = dec.dec_dimm_col;
310271815ce7SRobert Mustacchi 	/*
310371815ce7SRobert Mustacchi 	 * We don't have a logical rank that something matches to, we have the
310471815ce7SRobert Mustacchi 	 * actual chip-select and rank multiplication. If we could figure out
310571815ce7SRobert Mustacchi 	 * how to transform that into an actual rank, that'd be grand.
310671815ce7SRobert Mustacchi 	 */
310771815ce7SRobert Mustacchi 	encode->mcei_rank = UINT8_MAX;
310871815ce7SRobert Mustacchi 	encode->mcei_cs = dec.dec_dimm_csno;
310971815ce7SRobert Mustacchi 	encode->mcei_rm = dec.dec_dimm_rm;
311071815ce7SRobert Mustacchi 	encode->mcei_bank = dec.dec_dimm_bank;
311171815ce7SRobert Mustacchi 	encode->mcei_bank_group = dec.dec_dimm_bank_group;
311271815ce7SRobert Mustacchi 	encode->mcei_subchan = dec.dec_dimm_subchan;
311371815ce7SRobert Mustacchi }
311471815ce7SRobert Mustacchi 
311571815ce7SRobert Mustacchi static void
umc_decoder_pack(zen_umc_t * umc)311671815ce7SRobert Mustacchi umc_decoder_pack(zen_umc_t *umc)
311771815ce7SRobert Mustacchi {
311871815ce7SRobert Mustacchi 	char *buf = NULL;
311971815ce7SRobert Mustacchi 	size_t len = 0;
312071815ce7SRobert Mustacchi 
312171815ce7SRobert Mustacchi 	ASSERT(MUTEX_HELD(&umc->umc_nvl_lock));
312271815ce7SRobert Mustacchi 	if (umc->umc_decoder_buf != NULL) {
312371815ce7SRobert Mustacchi 		return;
312471815ce7SRobert Mustacchi 	}
312571815ce7SRobert Mustacchi 
312671815ce7SRobert Mustacchi 	if (umc->umc_decoder_nvl == NULL) {
312771815ce7SRobert Mustacchi 		umc->umc_decoder_nvl = zen_umc_dump_decoder(umc);
312871815ce7SRobert Mustacchi 		if (umc->umc_decoder_nvl == NULL) {
312971815ce7SRobert Mustacchi 			return;
313071815ce7SRobert Mustacchi 		}
313171815ce7SRobert Mustacchi 	}
313271815ce7SRobert Mustacchi 
313371815ce7SRobert Mustacchi 	if (nvlist_pack(umc->umc_decoder_nvl, &buf, &len, NV_ENCODE_XDR,
313471815ce7SRobert Mustacchi 	    KM_NOSLEEP_LAZY) != 0) {
313571815ce7SRobert Mustacchi 		return;
313671815ce7SRobert Mustacchi 	}
313771815ce7SRobert Mustacchi 
313871815ce7SRobert Mustacchi 	umc->umc_decoder_buf = buf;
313971815ce7SRobert Mustacchi 	umc->umc_decoder_len = len;
314071815ce7SRobert Mustacchi }
314171815ce7SRobert Mustacchi 
314271815ce7SRobert Mustacchi static int
zen_umc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)314371815ce7SRobert Mustacchi zen_umc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
314471815ce7SRobert Mustacchi     int *rvalp)
314571815ce7SRobert Mustacchi {
314671815ce7SRobert Mustacchi 	int ret;
314771815ce7SRobert Mustacchi 	zen_umc_t *umc = zen_umc;
314871815ce7SRobert Mustacchi 	mc_encode_ioc_t encode;
314971815ce7SRobert Mustacchi 	mc_snapshot_info_t info;
315071815ce7SRobert Mustacchi 
315171815ce7SRobert Mustacchi 	if (getminor(dev) >= umc->umc_ndfs) {
315271815ce7SRobert Mustacchi 		return (ENXIO);
315371815ce7SRobert Mustacchi 	}
315471815ce7SRobert Mustacchi 
315571815ce7SRobert Mustacchi 	switch (cmd) {
315671815ce7SRobert Mustacchi 	case MC_IOC_DECODE_PA:
315771815ce7SRobert Mustacchi 		if (crgetzoneid(credp) != GLOBAL_ZONEID ||
315871815ce7SRobert Mustacchi 		    drv_priv(credp) != 0) {
315971815ce7SRobert Mustacchi 			ret = EPERM;
316071815ce7SRobert Mustacchi 			break;
316171815ce7SRobert Mustacchi 		}
316271815ce7SRobert Mustacchi 
316371815ce7SRobert Mustacchi 		if (ddi_copyin((void *)arg, &encode, sizeof (encode),
316471815ce7SRobert Mustacchi 		    mode & FKIOCTL) != 0) {
316571815ce7SRobert Mustacchi 			ret = EFAULT;
316671815ce7SRobert Mustacchi 			break;
316771815ce7SRobert Mustacchi 		}
316871815ce7SRobert Mustacchi 
316971815ce7SRobert Mustacchi 		zen_umc_ioctl_decode(umc, &encode);
317071815ce7SRobert Mustacchi 		ret = 0;
317171815ce7SRobert Mustacchi 
317271815ce7SRobert Mustacchi 		if (ddi_copyout(&encode, (void *)arg, sizeof (encode),
317371815ce7SRobert Mustacchi 		    mode & FKIOCTL) != 0) {
317471815ce7SRobert Mustacchi 			ret = EFAULT;
317571815ce7SRobert Mustacchi 			break;
317671815ce7SRobert Mustacchi 		}
317771815ce7SRobert Mustacchi 		break;
317871815ce7SRobert Mustacchi 	case MC_IOC_DECODE_SNAPSHOT_INFO:
317971815ce7SRobert Mustacchi 		mutex_enter(&umc->umc_nvl_lock);
318071815ce7SRobert Mustacchi 		umc_decoder_pack(umc);
318171815ce7SRobert Mustacchi 
318271815ce7SRobert Mustacchi 		if (umc->umc_decoder_buf == NULL) {
318371815ce7SRobert Mustacchi 			mutex_exit(&umc->umc_nvl_lock);
318471815ce7SRobert Mustacchi 			ret = EIO;
318571815ce7SRobert Mustacchi 			break;
318671815ce7SRobert Mustacchi 		}
318771815ce7SRobert Mustacchi 
318871815ce7SRobert Mustacchi 		if (umc->umc_decoder_len > UINT32_MAX) {
318971815ce7SRobert Mustacchi 			mutex_exit(&umc->umc_nvl_lock);
319071815ce7SRobert Mustacchi 			ret = EOVERFLOW;
319171815ce7SRobert Mustacchi 			break;
319271815ce7SRobert Mustacchi 		}
319371815ce7SRobert Mustacchi 
319471815ce7SRobert Mustacchi 		info.mcs_size = umc->umc_decoder_len;
319571815ce7SRobert Mustacchi 		info.mcs_gen = 0;
319671815ce7SRobert Mustacchi 		if (ddi_copyout(&info, (void *)arg, sizeof (info),
319771815ce7SRobert Mustacchi 		    mode & FKIOCTL) != 0) {
319871815ce7SRobert Mustacchi 			mutex_exit(&umc->umc_nvl_lock);
319971815ce7SRobert Mustacchi 			ret = EFAULT;
320071815ce7SRobert Mustacchi 			break;
320171815ce7SRobert Mustacchi 		}
320271815ce7SRobert Mustacchi 
320371815ce7SRobert Mustacchi 		mutex_exit(&umc->umc_nvl_lock);
320471815ce7SRobert Mustacchi 		ret = 0;
320571815ce7SRobert Mustacchi 		break;
320671815ce7SRobert Mustacchi 	case MC_IOC_DECODE_SNAPSHOT:
320771815ce7SRobert Mustacchi 		mutex_enter(&umc->umc_nvl_lock);
320871815ce7SRobert Mustacchi 		umc_decoder_pack(umc);
320971815ce7SRobert Mustacchi 
321071815ce7SRobert Mustacchi 		if (umc->umc_decoder_buf == NULL) {
321171815ce7SRobert Mustacchi 			mutex_exit(&umc->umc_nvl_lock);
321271815ce7SRobert Mustacchi 			ret = EIO;
321371815ce7SRobert Mustacchi 			break;
321471815ce7SRobert Mustacchi 		}
321571815ce7SRobert Mustacchi 
321671815ce7SRobert Mustacchi 		if (ddi_copyout(umc->umc_decoder_buf, (void *)arg,
321771815ce7SRobert Mustacchi 		    umc->umc_decoder_len, mode & FKIOCTL) != 0) {
321871815ce7SRobert Mustacchi 			mutex_exit(&umc->umc_nvl_lock);
321971815ce7SRobert Mustacchi 			ret = EFAULT;
322071815ce7SRobert Mustacchi 			break;
322171815ce7SRobert Mustacchi 		}
322271815ce7SRobert Mustacchi 
322371815ce7SRobert Mustacchi 		mutex_exit(&umc->umc_nvl_lock);
322471815ce7SRobert Mustacchi 		ret = 0;
322571815ce7SRobert Mustacchi 		break;
322671815ce7SRobert Mustacchi 	default:
322771815ce7SRobert Mustacchi 		ret = ENOTTY;
322871815ce7SRobert Mustacchi 		break;
322971815ce7SRobert Mustacchi 	}
323071815ce7SRobert Mustacchi 
323171815ce7SRobert Mustacchi 	return (ret);
323271815ce7SRobert Mustacchi }
323371815ce7SRobert Mustacchi 
323471815ce7SRobert Mustacchi static int
zen_umc_close(dev_t dev,int flag,int otyp,cred_t * credp)323571815ce7SRobert Mustacchi zen_umc_close(dev_t dev, int flag, int otyp, cred_t *credp)
323671815ce7SRobert Mustacchi {
323771815ce7SRobert Mustacchi 	return (0);
323871815ce7SRobert Mustacchi }
323971815ce7SRobert Mustacchi 
324071815ce7SRobert Mustacchi static void
zen_umc_cleanup(zen_umc_t * umc)324171815ce7SRobert Mustacchi zen_umc_cleanup(zen_umc_t *umc)
324271815ce7SRobert Mustacchi {
324371815ce7SRobert Mustacchi 	nvlist_free(umc->umc_decoder_nvl);
324471815ce7SRobert Mustacchi 	umc->umc_decoder_nvl = NULL;
324571815ce7SRobert Mustacchi 	if (umc->umc_decoder_buf != NULL) {
324671815ce7SRobert Mustacchi 		kmem_free(umc->umc_decoder_buf, umc->umc_decoder_len);
324771815ce7SRobert Mustacchi 		umc->umc_decoder_buf = NULL;
324871815ce7SRobert Mustacchi 		umc->umc_decoder_len = 0;
324971815ce7SRobert Mustacchi 	}
325071815ce7SRobert Mustacchi 
325171815ce7SRobert Mustacchi 	if (umc->umc_dip != NULL) {
325271815ce7SRobert Mustacchi 		ddi_remove_minor_node(umc->umc_dip, NULL);
325371815ce7SRobert Mustacchi 	}
325471815ce7SRobert Mustacchi 	mutex_destroy(&umc->umc_nvl_lock);
325571815ce7SRobert Mustacchi 	kmem_free(umc, sizeof (zen_umc_t));
325671815ce7SRobert Mustacchi }
325771815ce7SRobert Mustacchi 
325871815ce7SRobert Mustacchi static int
zen_umc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)325971815ce7SRobert Mustacchi zen_umc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
326071815ce7SRobert Mustacchi {
326171815ce7SRobert Mustacchi 	int ret;
326271815ce7SRobert Mustacchi 	zen_umc_t *umc;
326371815ce7SRobert Mustacchi 
326471815ce7SRobert Mustacchi 	if (cmd == DDI_RESUME) {
326571815ce7SRobert Mustacchi 		return (DDI_SUCCESS);
326671815ce7SRobert Mustacchi 	} else if (cmd != DDI_ATTACH) {
326771815ce7SRobert Mustacchi 		return (DDI_FAILURE);
326871815ce7SRobert Mustacchi 	}
326971815ce7SRobert Mustacchi 	if (zen_umc != NULL) {
327071815ce7SRobert Mustacchi 		dev_err(dip, CE_WARN, "!zen_umc is already attached to a "
327171815ce7SRobert Mustacchi 		    "dev_info_t: %p", zen_umc->umc_dip);
327271815ce7SRobert Mustacchi 		return (DDI_FAILURE);
327371815ce7SRobert Mustacchi 	}
327471815ce7SRobert Mustacchi 
327571815ce7SRobert Mustacchi 	/*
327671815ce7SRobert Mustacchi 	 * To get us going, we need to do several bits of set up. First, we need
327771815ce7SRobert Mustacchi 	 * to use the knowledge about the actual hardware that we're using to
327871815ce7SRobert Mustacchi 	 * encode a bunch of different data:
327971815ce7SRobert Mustacchi 	 *
328071815ce7SRobert Mustacchi 	 *  o The set of register styles and extra hardware features that exist
328171815ce7SRobert Mustacchi 	 *    on the hardware platform.
328271815ce7SRobert Mustacchi 	 *  o The number of actual rules there are for the CCMs and UMCs.
328371815ce7SRobert Mustacchi 	 *  o How many actual things exist (DFs, etc.)
328471815ce7SRobert Mustacchi 	 *  o Useful fabric and instance IDs for all of the different UMC
328571815ce7SRobert Mustacchi 	 *    entries so we can actually talk to them.
328671815ce7SRobert Mustacchi 	 *
328771815ce7SRobert Mustacchi 	 * Only once we have all the above will we go dig into the actual data.
328871815ce7SRobert Mustacchi 	 */
328971815ce7SRobert Mustacchi 	umc = kmem_zalloc(sizeof (zen_umc_t), KM_SLEEP);
329071815ce7SRobert Mustacchi 	mutex_init(&umc->umc_nvl_lock, NULL, MUTEX_DRIVER, NULL);
329122e4c3acSKeith M Wesolowski 	umc->umc_family = chiprev_family(cpuid_getchiprev(CPU));
329271815ce7SRobert Mustacchi 	umc->umc_ndfs = amdzen_c_df_count();
329371815ce7SRobert Mustacchi 	umc->umc_dip = dip;
329471815ce7SRobert Mustacchi 
329571815ce7SRobert Mustacchi 	if (!zen_umc_identify(umc)) {
329671815ce7SRobert Mustacchi 		dev_err(dip, CE_WARN, "!encountered unsupported CPU");
329771815ce7SRobert Mustacchi 		goto err;
329871815ce7SRobert Mustacchi 	}
329971815ce7SRobert Mustacchi 
330071815ce7SRobert Mustacchi 	umc->umc_df_rev = amdzen_c_df_rev();
330171815ce7SRobert Mustacchi 	switch (umc->umc_df_rev) {
330271815ce7SRobert Mustacchi 	case DF_REV_2:
330371815ce7SRobert Mustacchi 	case DF_REV_3:
330471815ce7SRobert Mustacchi 	case DF_REV_3P5:
330571815ce7SRobert Mustacchi 	case DF_REV_4:
330671815ce7SRobert Mustacchi 		break;
330771815ce7SRobert Mustacchi 	default:
330871815ce7SRobert Mustacchi 		dev_err(dip, CE_WARN, "!encountered unknown DF revision: %x",
330971815ce7SRobert Mustacchi 		    umc->umc_df_rev);
331071815ce7SRobert Mustacchi 		goto err;
331171815ce7SRobert Mustacchi 	}
331271815ce7SRobert Mustacchi 
331371815ce7SRobert Mustacchi 	if ((ret = amdzen_c_df_fabric_decomp(&umc->umc_decomp)) != 0) {
331471815ce7SRobert Mustacchi 		dev_err(dip, CE_WARN, "!failed to get fabric decomposition: %d",
331571815ce7SRobert Mustacchi 		    ret);
331671815ce7SRobert Mustacchi 	}
331771815ce7SRobert Mustacchi 
331871815ce7SRobert Mustacchi 	umc->umc_tom = rdmsr(MSR_AMD_TOM);
331971815ce7SRobert Mustacchi 	umc->umc_tom2 = rdmsr(MSR_AMD_TOM2);
332071815ce7SRobert Mustacchi 
332171815ce7SRobert Mustacchi 	/*
332271815ce7SRobert Mustacchi 	 * For each DF, start by reading all of the data that we need from it.
332371815ce7SRobert Mustacchi 	 * This involves finding a target CCM, reading all of the rules,
332471815ce7SRobert Mustacchi 	 * ancillary settings, and related. Then we'll do a pass over all of the
332571815ce7SRobert Mustacchi 	 * actual UMC targets there.
332671815ce7SRobert Mustacchi 	 */
332771815ce7SRobert Mustacchi 	for (uint_t i = 0; i < umc->umc_ndfs; i++) {
332871815ce7SRobert Mustacchi 		if (amdzen_c_df_iter(i, ZEN_DF_TYPE_CCM_CPU,
332971815ce7SRobert Mustacchi 		    zen_umc_fill_ccm_cb, umc) < 0 ||
333071815ce7SRobert Mustacchi 		    amdzen_c_df_iter(i, ZEN_DF_TYPE_CS_UMC, zen_umc_fill_umc_cb,
333171815ce7SRobert Mustacchi 		    umc) != 0) {
333271815ce7SRobert Mustacchi 			goto err;
333371815ce7SRobert Mustacchi 		}
333471815ce7SRobert Mustacchi 	}
333571815ce7SRobert Mustacchi 
333671815ce7SRobert Mustacchi 	/*
333771815ce7SRobert Mustacchi 	 * Create a minor node for each df that we encounter.
333871815ce7SRobert Mustacchi 	 */
333971815ce7SRobert Mustacchi 	for (uint_t i = 0; i < umc->umc_ndfs; i++) {
334071815ce7SRobert Mustacchi 		int ret;
334171815ce7SRobert Mustacchi 		char minor[64];
334271815ce7SRobert Mustacchi 
334371815ce7SRobert Mustacchi 		(void) snprintf(minor, sizeof (minor), "mc-umc-%u", i);
334471815ce7SRobert Mustacchi 		if ((ret = ddi_create_minor_node(umc->umc_dip, minor, S_IFCHR,
334571815ce7SRobert Mustacchi 		    i, "ddi_mem_ctrl", 0)) != 0) {
334671815ce7SRobert Mustacchi 			dev_err(dip, CE_WARN, "!failed to create minor %s: %d",
334771815ce7SRobert Mustacchi 			    minor, ret);
334871815ce7SRobert Mustacchi 			goto err;
334971815ce7SRobert Mustacchi 		}
335071815ce7SRobert Mustacchi 	}
335171815ce7SRobert Mustacchi 
335271815ce7SRobert Mustacchi 	zen_umc = umc;
335371815ce7SRobert Mustacchi 	return (DDI_SUCCESS);
335471815ce7SRobert Mustacchi 
335571815ce7SRobert Mustacchi err:
335671815ce7SRobert Mustacchi 	zen_umc_cleanup(umc);
335771815ce7SRobert Mustacchi 	return (DDI_FAILURE);
335871815ce7SRobert Mustacchi }
335971815ce7SRobert Mustacchi 
336071815ce7SRobert Mustacchi static int
zen_umc_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)336171815ce7SRobert Mustacchi zen_umc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
336271815ce7SRobert Mustacchi {
336371815ce7SRobert Mustacchi 	zen_umc_t *umc;
336471815ce7SRobert Mustacchi 
336571815ce7SRobert Mustacchi 	if (zen_umc == NULL || zen_umc->umc_dip == NULL) {
336671815ce7SRobert Mustacchi 		return (DDI_FAILURE);
336771815ce7SRobert Mustacchi 	}
336871815ce7SRobert Mustacchi 	umc = zen_umc;
336971815ce7SRobert Mustacchi 
337071815ce7SRobert Mustacchi 	switch (cmd) {
337171815ce7SRobert Mustacchi 	case DDI_INFO_DEVT2DEVINFO:
337271815ce7SRobert Mustacchi 		*resultp = (void *)umc->umc_dip;
337371815ce7SRobert Mustacchi 		break;
337471815ce7SRobert Mustacchi 	case DDI_INFO_DEVT2INSTANCE:
337571815ce7SRobert Mustacchi 		*resultp = (void *)(uintptr_t)ddi_get_instance(
337671815ce7SRobert Mustacchi 		    umc->umc_dip);
337771815ce7SRobert Mustacchi 		break;
337871815ce7SRobert Mustacchi 	default:
337971815ce7SRobert Mustacchi 		return (DDI_FAILURE);
338071815ce7SRobert Mustacchi 	}
338171815ce7SRobert Mustacchi 	return (DDI_SUCCESS);
338271815ce7SRobert Mustacchi }
338371815ce7SRobert Mustacchi 
338471815ce7SRobert Mustacchi static int
zen_umc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)338571815ce7SRobert Mustacchi zen_umc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
338671815ce7SRobert Mustacchi {
338771815ce7SRobert Mustacchi 	zen_umc_t *umc;
338871815ce7SRobert Mustacchi 
338971815ce7SRobert Mustacchi 	if (cmd == DDI_SUSPEND) {
339071815ce7SRobert Mustacchi 		return (DDI_SUCCESS);
339171815ce7SRobert Mustacchi 	} else if (cmd != DDI_DETACH) {
339271815ce7SRobert Mustacchi 		return (DDI_FAILURE);
339371815ce7SRobert Mustacchi 	}
339471815ce7SRobert Mustacchi 
339571815ce7SRobert Mustacchi 	if (zen_umc == NULL) {
339671815ce7SRobert Mustacchi 		dev_err(dip, CE_WARN, "!asked to detach zen_umc, but it "
339771815ce7SRobert Mustacchi 		    "was never successfully attached");
339871815ce7SRobert Mustacchi 		return (DDI_FAILURE);
339971815ce7SRobert Mustacchi 	}
340071815ce7SRobert Mustacchi 
340171815ce7SRobert Mustacchi 	umc = zen_umc;
340271815ce7SRobert Mustacchi 	zen_umc = NULL;
340371815ce7SRobert Mustacchi 	zen_umc_cleanup(umc);
340471815ce7SRobert Mustacchi 	return (DDI_SUCCESS);
340571815ce7SRobert Mustacchi }
340671815ce7SRobert Mustacchi 
340771815ce7SRobert Mustacchi static struct cb_ops zen_umc_cb_ops = {
340871815ce7SRobert Mustacchi 	.cb_open = zen_umc_open,
340971815ce7SRobert Mustacchi 	.cb_close = zen_umc_close,
341071815ce7SRobert Mustacchi 	.cb_strategy = nodev,
341171815ce7SRobert Mustacchi 	.cb_print = nodev,
341271815ce7SRobert Mustacchi 	.cb_dump = nodev,
341371815ce7SRobert Mustacchi 	.cb_read = nodev,
341471815ce7SRobert Mustacchi 	.cb_write = nodev,
341571815ce7SRobert Mustacchi 	.cb_ioctl = zen_umc_ioctl,
341671815ce7SRobert Mustacchi 	.cb_devmap = nodev,
341771815ce7SRobert Mustacchi 	.cb_mmap = nodev,
341871815ce7SRobert Mustacchi 	.cb_segmap = nodev,
341971815ce7SRobert Mustacchi 	.cb_chpoll = nochpoll,
342071815ce7SRobert Mustacchi 	.cb_prop_op = ddi_prop_op,
342171815ce7SRobert Mustacchi 	.cb_flag = D_MP,
342271815ce7SRobert Mustacchi 	.cb_rev = CB_REV,
342371815ce7SRobert Mustacchi 	.cb_aread = nodev,
342471815ce7SRobert Mustacchi 	.cb_awrite = nodev
342571815ce7SRobert Mustacchi };
342671815ce7SRobert Mustacchi 
342771815ce7SRobert Mustacchi static struct dev_ops zen_umc_dev_ops = {
342871815ce7SRobert Mustacchi 	.devo_rev = DEVO_REV,
342971815ce7SRobert Mustacchi 	.devo_refcnt = 0,
343071815ce7SRobert Mustacchi 	.devo_getinfo = zen_umc_getinfo,
343171815ce7SRobert Mustacchi 	.devo_identify = nulldev,
343271815ce7SRobert Mustacchi 	.devo_probe = nulldev,
343371815ce7SRobert Mustacchi 	.devo_attach = zen_umc_attach,
343471815ce7SRobert Mustacchi 	.devo_detach = zen_umc_detach,
343571815ce7SRobert Mustacchi 	.devo_reset = nodev,
343671815ce7SRobert Mustacchi 	.devo_quiesce = ddi_quiesce_not_needed,
343771815ce7SRobert Mustacchi 	.devo_cb_ops = &zen_umc_cb_ops
343871815ce7SRobert Mustacchi };
343971815ce7SRobert Mustacchi 
344071815ce7SRobert Mustacchi static struct modldrv zen_umc_modldrv = {
344171815ce7SRobert Mustacchi 	.drv_modops = &mod_driverops,
344271815ce7SRobert Mustacchi 	.drv_linkinfo = "AMD Zen Unified Memory Controller",
344371815ce7SRobert Mustacchi 	.drv_dev_ops = &zen_umc_dev_ops
344471815ce7SRobert Mustacchi };
344571815ce7SRobert Mustacchi 
344671815ce7SRobert Mustacchi static struct modlinkage zen_umc_modlinkage = {
344771815ce7SRobert Mustacchi 	.ml_rev = MODREV_1,
344871815ce7SRobert Mustacchi 	.ml_linkage = { &zen_umc_modldrv, NULL }
344971815ce7SRobert Mustacchi };
345071815ce7SRobert Mustacchi 
345171815ce7SRobert Mustacchi int
_init(void)345271815ce7SRobert Mustacchi _init(void)
345371815ce7SRobert Mustacchi {
345471815ce7SRobert Mustacchi 	return (mod_install(&zen_umc_modlinkage));
345571815ce7SRobert Mustacchi }
345671815ce7SRobert Mustacchi 
345771815ce7SRobert Mustacchi int
_info(struct modinfo * modinfop)345871815ce7SRobert Mustacchi _info(struct modinfo *modinfop)
345971815ce7SRobert Mustacchi {
346071815ce7SRobert Mustacchi 	return (mod_info(&zen_umc_modlinkage, modinfop));
346171815ce7SRobert Mustacchi }
346271815ce7SRobert Mustacchi 
346371815ce7SRobert Mustacchi int
_fini(void)346471815ce7SRobert Mustacchi _fini(void)
346571815ce7SRobert Mustacchi {
346671815ce7SRobert Mustacchi 	return (mod_remove(&zen_umc_modlinkage));
346771815ce7SRobert Mustacchi }
3468