diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c | 1007 |
1 files changed, 1007 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c new file mode 100644 index 000000000..bbfde1cb3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgt215.c @@ -0,0 +1,1007 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + * Roy Spliet <rspliet@eclipso.eu> + */ +#define gt215_ram(p) container_of((p), struct gt215_ram, base) +#include "ram.h" +#include "ramfuc.h" + +#include <core/memory.h> +#include <core/option.h> +#include <subdev/bios.h> +#include <subdev/bios/M0205.h> +#include <subdev/bios/rammap.h> +#include <subdev/bios/timing.h> +#include <subdev/clk/gt215.h> +#include <subdev/gpio.h> + +struct gt215_ramfuc { + struct ramfuc base; + struct ramfuc_reg r_0x001610; + struct ramfuc_reg r_0x001700; + struct ramfuc_reg r_0x002504; + struct ramfuc_reg r_0x004000; + struct ramfuc_reg r_0x004004; + struct ramfuc_reg r_0x004018; + struct ramfuc_reg r_0x004128; + struct ramfuc_reg r_0x004168; + struct ramfuc_reg r_0x100080; + struct ramfuc_reg r_0x100200; + struct ramfuc_reg r_0x100210; + struct ramfuc_reg r_0x100220[9]; + struct ramfuc_reg r_0x100264; + struct ramfuc_reg r_0x1002d0; + struct ramfuc_reg r_0x1002d4; + struct ramfuc_reg r_0x1002dc; + struct ramfuc_reg r_0x10053c; + struct ramfuc_reg r_0x1005a0; + struct ramfuc_reg r_0x1005a4; + struct ramfuc_reg r_0x100700; + struct ramfuc_reg r_0x100714; + struct ramfuc_reg r_0x100718; + struct ramfuc_reg r_0x10071c; + struct ramfuc_reg r_0x100720; + struct ramfuc_reg r_0x100760; + struct ramfuc_reg r_0x1007a0; + struct ramfuc_reg r_0x1007e0; + struct ramfuc_reg r_0x100da0; + struct ramfuc_reg r_0x10f804; + struct ramfuc_reg r_0x1110e0; + struct ramfuc_reg r_0x111100; + struct ramfuc_reg r_0x111104; + struct ramfuc_reg r_0x1111e0; + struct ramfuc_reg r_0x111400; + struct ramfuc_reg r_0x611200; + struct ramfuc_reg r_mr[4]; + struct ramfuc_reg r_gpio[4]; +}; + +struct gt215_ltrain { + enum { + NVA3_TRAIN_UNKNOWN, + NVA3_TRAIN_UNSUPPORTED, + NVA3_TRAIN_ONCE, + NVA3_TRAIN_EXEC, + NVA3_TRAIN_DONE + } state; + u32 r_100720; + u32 r_1111e0; + u32 r_111400; + struct nvkm_memory *memory; +}; + +struct gt215_ram { + struct nvkm_ram base; + struct gt215_ramfuc fuc; + struct gt215_ltrain ltrain; +}; + +static void +gt215_link_train_calc(u32 *vals, struct gt215_ltrain *train) +{ + int i, lo, hi; + u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0; + + for (i = 0; i < 8; i++) { + for (lo = 0; lo < 0x40; lo++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (vals[lo] & (0x101 << i)) + break; + } + + if (lo == 0x40) + return; + + for (hi = lo + 1; hi < 0x40; hi++) { + if (!(vals[lo] & 0x80000000)) + continue; + if (!(vals[hi] & (0x101 << i))) { + hi--; + break; + } + } + + median[i] = ((hi - lo) >> 1) + lo; + bins[(median[i] & 0xf0) >> 4]++; + median[i] += 0x30; + } + + /* Find the best value for 0x1111e0 */ + for (i = 0; i < 4; i++) { + if (bins[i] > qty) { + bin = i + 3; + qty = bins[i]; + } + } + + train->r_100720 = 0; + for (i = 0; i < 8; i++) { + median[i] = max(median[i], (u8) (bin << 4)); + median[i] = min(median[i], (u8) ((bin << 4) | 0xf)); + + train->r_100720 |= ((median[i] & 0x0f) << (i << 2)); + } + + train->r_1111e0 = 0x02000000 | (bin * 0x101); + train->r_111400 = 0x0; +} + +/* + * Link training for (at least) DDR3 + */ +static int +gt215_link_train(struct gt215_ram *ram) +{ + struct gt215_ltrain *train = &ram->ltrain; + struct gt215_ramfuc *fuc = &ram->fuc; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvkm_clk *clk = device->clk; + u32 *result, r1700; + int ret, i; + struct nvbios_M0205T M0205T = { 0 }; + u8 ver, hdr, cnt, len, snr, ssz; + unsigned int clk_current; + unsigned long flags; + unsigned long *f = &flags; + + if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true) + return -ENOSYS; + + /* XXX: Multiple partitions? */ + result = kmalloc_array(64, sizeof(u32), GFP_KERNEL); + if (!result) + return -ENOMEM; + + train->state = NVA3_TRAIN_EXEC; + + /* Clock speeds for training and back */ + nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); + if (M0205T.freq == 0) { + kfree(result); + return -ENOENT; + } + + clk_current = nvkm_clk_read(clk, nv_clk_src_mem); + + ret = gt215_clk_pre(clk, f); + if (ret) + goto out; + + /* First: clock up/down */ + ret = ram->base.func->calc(&ram->base, (u32) M0205T.freq * 1000); + if (ret) + goto out; + + /* Do this *after* calc, eliminates write in script */ + nvkm_wr32(device, 0x111400, 0x00000000); + /* XXX: Magic writes that improve train reliability? */ + nvkm_mask(device, 0x100674, 0x0000ffff, 0x00000000); + nvkm_mask(device, 0x1005e4, 0x0000ffff, 0x00000000); + nvkm_mask(device, 0x100b0c, 0x000000ff, 0x00000000); + nvkm_wr32(device, 0x100c04, 0x00000400); + + /* Now the training script */ + r1700 = ram_rd32(fuc, 0x001700); + + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + ram_wr32(fuc, 0x611200, 0x3300); + ram_wait_vblank(fuc); + ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000003); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_wr32(fuc, 0x001700, 0x00000000); + + ram_train(fuc); + + /* Reset */ + ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000); + ram_wr32(fuc, 0x10053c, 0x0); + ram_wr32(fuc, 0x100720, train->r_100720); + ram_wr32(fuc, 0x1111e0, train->r_1111e0); + ram_wr32(fuc, 0x111400, train->r_111400); + ram_nuke(fuc, 0x100080); + ram_mask(fuc, 0x100080, 0x00000020, 0x00000020); + ram_nsec(fuc, 1000); + + ram_wr32(fuc, 0x001700, r1700); + ram_mask(fuc, 0x001610, 0x00000083, 0x00000080); + ram_wr32(fuc, 0x611200, 0x3330); + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + + ram_exec(fuc, true); + + ram->base.func->calc(&ram->base, clk_current); + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nvkm_mask(device, 0x616308, 0x10, 0x10); + nvkm_mask(device, 0x616b08, 0x10, 0x10); + + gt215_clk_post(clk, f); + + ram_train_result(ram->base.fb, result, 64); + for (i = 0; i < 64; i++) + nvkm_debug(subdev, "Train: %08x", result[i]); + gt215_link_train_calc(result, train); + + nvkm_debug(subdev, "Train: %08x %08x %08x", train->r_100720, + train->r_1111e0, train->r_111400); + + kfree(result); + + train->state = NVA3_TRAIN_DONE; + + return ret; + +out: + if(ret == -EBUSY) + f = NULL; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + gt215_clk_post(clk, f); + kfree(result); + return ret; +} + +static int +gt215_link_train_init(struct gt215_ram *ram) +{ + static const u32 pattern[16] = { + 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, + 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, + 0x33333333, 0x55555555, 0x77777777, 0x66666666, + 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, + }; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_device *device = ram->base.fb->subdev.device; + struct nvkm_bios *bios = device->bios; + struct nvbios_M0205E M0205E; + u8 ver, hdr, cnt, len; + u32 r001700; + u64 addr; + int ret, i = 0; + + train->state = NVA3_TRAIN_UNSUPPORTED; + + /* We support type "5" + * XXX: training pattern table appears to be unused for this routine */ + if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) + return -ENOENT; + + if (M0205E.type != 5) + return 0; + + train->state = NVA3_TRAIN_ONCE; + + ret = nvkm_ram_get(device, NVKM_RAM_MM_NORMAL, 0x01, 16, 0x8000, + true, true, &ram->ltrain.memory); + if (ret) + return ret; + + addr = nvkm_memory_addr(ram->ltrain.memory); + + nvkm_wr32(device, 0x100538, 0x10000000 | (addr >> 16)); + nvkm_wr32(device, 0x1005a8, 0x0000ffff); + nvkm_mask(device, 0x10f800, 0x00000001, 0x00000001); + + for (i = 0; i < 0x30; i++) { + nvkm_wr32(device, 0x10f8c0, (i << 8) | i); + nvkm_wr32(device, 0x10f900, pattern[i % 16]); + } + + for (i = 0; i < 0x30; i++) { + nvkm_wr32(device, 0x10f8e0, (i << 8) | i); + nvkm_wr32(device, 0x10f920, pattern[i % 16]); + } + + /* And upload the pattern */ + r001700 = nvkm_rd32(device, 0x1700); + nvkm_wr32(device, 0x1700, addr >> 16); + for (i = 0; i < 16; i++) + nvkm_wr32(device, 0x700000 + (i << 2), pattern[i]); + for (i = 0; i < 16; i++) + nvkm_wr32(device, 0x700100 + (i << 2), pattern[i]); + nvkm_wr32(device, 0x1700, r001700); + + train->r_100720 = nvkm_rd32(device, 0x100720); + train->r_1111e0 = nvkm_rd32(device, 0x1111e0); + train->r_111400 = nvkm_rd32(device, 0x111400); + return 0; +} + +static void +gt215_link_train_fini(struct gt215_ram *ram) +{ + nvkm_memory_unref(&ram->ltrain.memory); +} + +/* + * RAM reclocking + */ +#define T(t) cfg->timing_10_##t +static int +gt215_ram_timing_calc(struct gt215_ram *ram, u32 *timing) +{ + struct nvbios_ramcfg *cfg = &ram->base.target.bios; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + int tUNK_base, tUNK_40_0, prevCL; + u32 cur2, cur3, cur7, cur8; + + cur2 = nvkm_rd32(device, 0x100228); + cur3 = nvkm_rd32(device, 0x10022c); + cur7 = nvkm_rd32(device, 0x10023c); + cur8 = nvkm_rd32(device, 0x100240); + + + switch ((!T(CWL)) * ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + T(CWL) = T(CL) - 1; + break; + case NVKM_RAM_TYPE_GDDR3: + T(CWL) = ((cur2 & 0xff000000) >> 24) + 1; + break; + } + + prevCL = (cur3 & 0x000000ff) + 1; + tUNK_base = ((cur7 & 0x00ff0000) >> 16) - prevCL; + + timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); + timing[1] = (T(WR) + 1 + T(CWL)) << 24 | + max_t(u8,T(18), 1) << 16 | + (T(WTR) + 1 + T(CWL)) << 8 | + (5 + T(CL) - T(CWL)); + timing[2] = (T(CWL) - 1) << 24 | + (T(RRD) << 16) | + (T(RCDWR) << 8) | + T(RCDRD); + timing[3] = (cur3 & 0x00ff0000) | + (0x30 + T(CL)) << 24 | + (0xb + T(CL)) << 8 | + (T(CL) - 1); + timing[4] = T(20) << 24 | + T(21) << 16 | + T(13) << 8 | + T(13); + timing[5] = T(RFC) << 24 | + max_t(u8,T(RCDRD), T(RCDWR)) << 16 | + max_t(u8, (T(CWL) + 6), (T(CL) + 2)) << 8 | + T(RP); + timing[6] = (0x5a + T(CL)) << 16 | + max_t(u8, 1, (6 - T(CL) + T(CWL))) << 8 | + (0x50 + T(CL) - T(CWL)); + timing[7] = (cur7 & 0xff000000) | + ((tUNK_base + T(CL)) << 16) | + 0x202; + timing[8] = cur8 & 0xffffff00; + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_GDDR3: + tUNK_40_0 = prevCL - (cur8 & 0xff); + if (tUNK_40_0 > 0) + timing[8] |= T(CL); + break; + default: + break; + } + + nvkm_debug(subdev, "Entry: 220: %08x %08x %08x %08x\n", + timing[0], timing[1], timing[2], timing[3]); + nvkm_debug(subdev, " 230: %08x %08x %08x %08x\n", + timing[4], timing[5], timing[6], timing[7]); + nvkm_debug(subdev, " 240: %08x\n", timing[8]); + return 0; +} +#undef T + +static void +nvkm_sddr2_dll_reset(struct gt215_ramfuc *fuc) +{ + ram_mask(fuc, mr[0], 0x100, 0x100); + ram_nsec(fuc, 1000); + ram_mask(fuc, mr[0], 0x100, 0x000); + ram_nsec(fuc, 1000); +} + +static void +nvkm_sddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x1)) { + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +nvkm_gddr3_dll_disable(struct gt215_ramfuc *fuc, u32 *mr) +{ + u32 mr1_old = ram_rd32(fuc, mr[1]); + + if (!(mr1_old & 0x40)) { + ram_wr32(fuc, mr[1], mr[1]); + ram_nsec(fuc, 1000); + } +} + +static void +gt215_ram_lock_pll(struct gt215_ramfuc *fuc, struct gt215_clk_info *mclk) +{ + ram_wr32(fuc, 0x004004, mclk->pll); + ram_mask(fuc, 0x004000, 0x00000001, 0x00000001); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000000); + ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); + ram_mask(fuc, 0x004000, 0x00000010, 0x00000010); +} + +static void +gt215_ram_gpio(struct gt215_ramfuc *fuc, u8 tag, u32 val) +{ + struct nvkm_gpio *gpio = fuc->base.fb->subdev.device->gpio; + struct dcb_gpio_func func; + u32 reg, sh, gpio_val; + int ret; + + if (nvkm_gpio_get(gpio, 0, tag, DCB_GPIO_UNUSED) != val) { + ret = nvkm_gpio_find(gpio, 0, tag, DCB_GPIO_UNUSED, &func); + if (ret) + return; + + reg = func.line >> 3; + sh = (func.line & 0x7) << 2; + gpio_val = ram_rd32(fuc, gpio[reg]); + if (gpio_val & (8 << sh)) + val = !val; + if (!(func.log[1] & 1)) + val = !val; + + ram_mask(fuc, gpio[reg], (0x3 << sh), ((val | 0x2) << sh)); + ram_nsec(fuc, 20000); + } +} + +static int +gt215_ram_calc(struct nvkm_ram *base, u32 freq) +{ + struct gt215_ram *ram = gt215_ram(base); + struct gt215_ramfuc *fuc = &ram->fuc; + struct gt215_ltrain *train = &ram->ltrain; + struct nvkm_subdev *subdev = &ram->base.fb->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct gt215_clk_info mclk; + struct nvkm_gpio *gpio = device->gpio; + struct nvkm_ram_data *next; + u8 ver, hdr, cnt, len, strap; + u32 data; + u32 r004018, r100760, r100da0, r111100, ctrl; + u32 unk714, unk718, unk71c; + int ret, i; + u32 timing[9]; + bool pll2pll; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; + + if (ram->ltrain.state == NVA3_TRAIN_ONCE) + gt215_link_train(ram); + + /* lookup memory config data relevant to the target frequency */ + data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x05) { + nvkm_error(subdev, "invalid/missing rammap entry\n"); + return -EINVAL; + } + + /* locate specific data set for the attached memory */ + strap = nvbios_ramcfg_index(subdev); + if (strap >= cnt) { + nvkm_error(subdev, "invalid ramcfg strap\n"); + return -EINVAL; + } + + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, + &ver, &hdr, &next->bios); + if (!data || ver != 0x10 || hdr < 0x09) { + nvkm_error(subdev, "invalid/missing ramcfg entry\n"); + return -EINVAL; + } + + /* lookup memory timings, if bios says they're present */ + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x17) { + nvkm_error(subdev, "invalid/missing timing entry\n"); + return -EINVAL; + } + } + + ret = gt215_pll_info(device->clk, 0x12, 0x4000, freq, &mclk); + if (ret < 0) { + nvkm_error(subdev, "failed mclk calculation\n"); + return ret; + } + + gt215_ram_timing_calc(ram, timing); + + ret = ram_init(fuc, ram->base.fb); + if (ret) + return ret; + + /* Determine ram-specific MR values */ + ram->base.mr[0] = ram_rd32(fuc, mr[0]); + ram->base.mr[1] = ram_rd32(fuc, mr[1]); + ram->base.mr[2] = ram_rd32(fuc, mr[2]); + + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + ret = nvkm_sddr2_calc(&ram->base); + break; + case NVKM_RAM_TYPE_DDR3: + ret = nvkm_sddr3_calc(&ram->base); + break; + case NVKM_RAM_TYPE_GDDR3: + ret = nvkm_gddr3_calc(&ram->base); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) + return ret; + + /* XXX: 750MHz seems rather arbitrary */ + if (freq <= 750000) { + r004018 = 0x10000000; + r100760 = 0x22222222; + r100da0 = 0x00000010; + } else { + r004018 = 0x00000000; + r100760 = 0x00000000; + r100da0 = 0x00000000; + } + + if (!next->bios.ramcfg_DLLoff) + r004018 |= 0x00004000; + + /* pll2pll requires to switch to a safe clock first */ + ctrl = ram_rd32(fuc, 0x004000); + pll2pll = (!(ctrl & 0x00000008)) && mclk.pll; + + /* Pre, NVIDIA does this outside the script */ + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); + } else { + ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); + ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); + } + /* Always disable this bit during reclock */ + ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); + + /* If switching from non-pll to pll, lock before disabling FB */ + if (mclk.pll && !pll2pll) { + ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101); + gt215_ram_lock_pll(fuc, &mclk); + } + + /* Start with disabling some CRTCs and PFIFO? */ + ram_wait_vblank(fuc); + ram_wr32(fuc, 0x611200, 0x3300); + ram_mask(fuc, 0x002504, 0x1, 0x1); + ram_nsec(fuc, 10000); + ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */ + ram_block(fuc); + ram_nsec(fuc, 2000); + + if (!next->bios.ramcfg_10_02_10) { + if (ram->base.type == NVKM_RAM_TYPE_GDDR3) + ram_mask(fuc, 0x111100, 0x04020000, 0x00020000); + else + ram_mask(fuc, 0x111100, 0x04020000, 0x04020000); + } + + /* If we're disabling the DLL, do it now */ + switch (next->bios.ramcfg_DLLoff * ram->base.type) { + case NVKM_RAM_TYPE_DDR3: + nvkm_sddr3_dll_disable(fuc, ram->base.mr); + break; + case NVKM_RAM_TYPE_GDDR3: + nvkm_gddr3_dll_disable(fuc, ram->base.mr); + break; + } + + if (next->bios.timing_10_ODT) + gt215_ram_gpio(fuc, 0x2e, 1); + + /* Brace RAM for impact */ + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x1002d0, 0x00000001); + ram_wr32(fuc, 0x100210, 0x00000000); + ram_wr32(fuc, 0x1002dc, 0x00000001); + ram_nsec(fuc, 2000); + + if (device->chipset == 0xa3 && freq <= 500000) + ram_mask(fuc, 0x100700, 0x00000006, 0x00000006); + + /* Alter FBVDD/Q, apparently must be done with PLL disabled, thus + * set it to bypass */ + if (nvkm_gpio_get(gpio, 0, 0x18, DCB_GPIO_UNUSED) == + next->bios.ramcfg_FBVDDQ) { + data = ram_rd32(fuc, 0x004000) & 0x9; + + if (data == 0x1) + ram_mask(fuc, 0x004000, 0x8, 0x8); + if (data & 0x1) + ram_mask(fuc, 0x004000, 0x1, 0x0); + + gt215_ram_gpio(fuc, 0x18, !next->bios.ramcfg_FBVDDQ); + + if (data & 0x1) + ram_mask(fuc, 0x004000, 0x1, 0x1); + } + + /* Fiddle with clocks */ + /* There's 4 scenario's + * pll->pll: first switch to a 324MHz clock, set up new PLL, switch + * clk->pll: Set up new PLL, switch + * pll->clk: Set up clock, switch + * clk->clk: Overwrite ctrl and other bits, switch */ + + /* Switch to regular clock - 324MHz */ + if (pll2pll) { + ram_mask(fuc, 0x004000, 0x00000004, 0x00000004); + ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00001000); + gt215_ram_lock_pll(fuc, &mclk); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004000, 0x00000105, 0x00000105); + ram_wr32(fuc, 0x004018, 0x00001000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } else { + ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101); + ram_mask(fuc, 0x004000, 0x00000108, 0x00000008); + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); + ram_wr32(fuc, 0x004018, 0x00009000 | r004018); + ram_wr32(fuc, 0x100da0, r100da0); + } + ram_nsec(fuc, 20000); + + if (next->bios.rammap_10_04_08) { + ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | + next->bios.ramcfg_10_05 << 8 | + next->bios.ramcfg_10_05); + ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | + next->bios.ramcfg_10_07); + ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | + next->bios.ramcfg_10_03_0f << 16 | + next->bios.ramcfg_10_09_0f | + 0x80000000); + ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); + } else { + if (train->state == NVA3_TRAIN_DONE) { + ram_wr32(fuc, 0x100080, 0x1020); + ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400); + ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0); + ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720); + } + ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); + ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); + ram_mask(fuc, 0x100760, 0x22222222, r100760); + ram_mask(fuc, 0x1007a0, 0x22222222, r100760); + ram_mask(fuc, 0x1007e0, 0x22222222, r100760); + } + + if (device->chipset == 0xa3 && freq > 500000) { + ram_mask(fuc, 0x100700, 0x00000006, 0x00000000); + } + + /* Final switch */ + if (mclk.pll) { + ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); + ram_mask(fuc, 0x004000, 0x00000008, 0x00000000); + } + + ram_wr32(fuc, 0x1002dc, 0x00000000); + ram_wr32(fuc, 0x1002d4, 0x00000001); + ram_wr32(fuc, 0x100210, 0x80000000); + ram_nsec(fuc, 2000); + + /* Set RAM MR parameters and timings */ + for (i = 2; i >= 0; i--) { + if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) { + ram_wr32(fuc, mr[i], ram->base.mr[i]); + ram_nsec(fuc, 1000); + } + } + + ram_wr32(fuc, 0x100220[3], timing[3]); + ram_wr32(fuc, 0x100220[1], timing[1]); + ram_wr32(fuc, 0x100220[6], timing[6]); + ram_wr32(fuc, 0x100220[7], timing[7]); + ram_wr32(fuc, 0x100220[2], timing[2]); + ram_wr32(fuc, 0x100220[4], timing[4]); + ram_wr32(fuc, 0x100220[5], timing[5]); + ram_wr32(fuc, 0x100220[0], timing[0]); + ram_wr32(fuc, 0x100220[8], timing[8]); + + /* Misc */ + ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); + + /* XXX: A lot of "chipset"/"ram type" specific stuff...? */ + unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130; + unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; + unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; + r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000; + + /* NVA8 seems to skip various bits related to ramcfg_10_02_04 */ + if (device->chipset == 0xa8) { + r111100 |= 0x08000000; + if (!next->bios.ramcfg_10_02_04) + unk714 |= 0x00000010; + } else { + if (next->bios.ramcfg_10_02_04) { + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_DDR3: + r111100 &= ~0x00000020; + if (next->bios.ramcfg_10_02_10) + r111100 |= 0x08000004; + else + r111100 |= 0x00000024; + break; + default: + break; + } + } else { + switch (ram->base.type) { + case NVKM_RAM_TYPE_DDR2: + case NVKM_RAM_TYPE_DDR3: + r111100 &= ~0x00000024; + r111100 |= 0x12800000; + + if (next->bios.ramcfg_10_02_10) + r111100 |= 0x08000000; + unk714 |= 0x00000010; + break; + case NVKM_RAM_TYPE_GDDR3: + r111100 |= 0x30000000; + unk714 |= 0x00000020; + break; + default: + break; + } + } + } + + unk714 |= (next->bios.ramcfg_10_04_01) << 8; + + if (next->bios.ramcfg_10_02_20) + unk714 |= 0xf0000000; + if (next->bios.ramcfg_10_02_02) + unk718 |= 0x00000100; + if (next->bios.ramcfg_10_02_01) + unk71c |= 0x00000100; + if (next->bios.timing_10_24 != 0xff) { + unk718 &= ~0xf0000000; + unk718 |= next->bios.timing_10_24 << 28; + } + if (next->bios.ramcfg_10_02_10) + r111100 &= ~0x04020000; + + ram_mask(fuc, 0x100714, 0xffffffff, unk714); + ram_mask(fuc, 0x10071c, 0xffffffff, unk71c); + ram_mask(fuc, 0x100718, 0xffffffff, unk718); + ram_mask(fuc, 0x111100, 0xffffffff, r111100); + + if (!next->bios.timing_10_ODT) + gt215_ram_gpio(fuc, 0x2e, 0); + + /* Reset DLL */ + if (!next->bios.ramcfg_DLLoff) + nvkm_sddr2_dll_reset(fuc); + + if (ram->base.type == NVKM_RAM_TYPE_GDDR3) { + ram_nsec(fuc, 31000); + } else { + ram_nsec(fuc, 14000); + } + + if (ram->base.type == NVKM_RAM_TYPE_DDR3) { + ram_wr32(fuc, 0x100264, 0x1); + ram_nsec(fuc, 2000); + } + + ram_nuke(fuc, 0x100700); + ram_mask(fuc, 0x100700, 0x01000000, 0x01000000); + ram_mask(fuc, 0x100700, 0x01000000, 0x00000000); + + /* Re-enable FB */ + ram_unblock(fuc); + ram_wr32(fuc, 0x611200, 0x3330); + + /* Post fiddlings */ + if (next->bios.rammap_10_04_02) + ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); + if (next->bios.ramcfg_10_02_10) { + ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); + ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); + } else { + ram_mask(fuc, 0x111104, 0x00000600, 0x00000600); + } + + if (mclk.pll) { + ram_mask(fuc, 0x004168, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004168, 0x00000100, 0x00000000); + } else { + ram_mask(fuc, 0x004000, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000001, 0x00000000); + ram_mask(fuc, 0x004128, 0x00000100, 0x00000000); + } + + return 0; +} + +static int +gt215_ram_prog(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + struct gt215_ramfuc *fuc = &ram->fuc; + struct nvkm_device *device = ram->base.fb->subdev.device; + bool exec = nvkm_boolopt(device->cfgopt, "NvMemExec", true); + + if (exec) { + nvkm_mask(device, 0x001534, 0x2, 0x2); + + ram_exec(fuc, true); + + /* Post-processing, avoids flicker */ + nvkm_mask(device, 0x002504, 0x1, 0x0); + nvkm_mask(device, 0x001534, 0x2, 0x0); + + nvkm_mask(device, 0x616308, 0x10, 0x10); + nvkm_mask(device, 0x616b08, 0x10, 0x10); + } else { + ram_exec(fuc, false); + } + return 0; +} + +static void +gt215_ram_tidy(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + ram_exec(&ram->fuc, false); +} + +static int +gt215_ram_init(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + gt215_link_train_init(ram); + return 0; +} + +static void * +gt215_ram_dtor(struct nvkm_ram *base) +{ + struct gt215_ram *ram = gt215_ram(base); + gt215_link_train_fini(ram); + return ram; +} + +static const struct nvkm_ram_func +gt215_ram_func = { + .dtor = gt215_ram_dtor, + .init = gt215_ram_init, + .calc = gt215_ram_calc, + .prog = gt215_ram_prog, + .tidy = gt215_ram_tidy, +}; + +int +gt215_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram) +{ + struct gt215_ram *ram; + int ret, i; + + if (!(ram = kzalloc(sizeof(*ram), GFP_KERNEL))) + return -ENOMEM; + *pram = &ram->base; + + ret = nv50_ram_ctor(>215_ram_func, fb, &ram->base); + if (ret) + return ret; + + ram->fuc.r_0x001610 = ramfuc_reg(0x001610); + ram->fuc.r_0x001700 = ramfuc_reg(0x001700); + ram->fuc.r_0x002504 = ramfuc_reg(0x002504); + ram->fuc.r_0x004000 = ramfuc_reg(0x004000); + ram->fuc.r_0x004004 = ramfuc_reg(0x004004); + ram->fuc.r_0x004018 = ramfuc_reg(0x004018); + ram->fuc.r_0x004128 = ramfuc_reg(0x004128); + ram->fuc.r_0x004168 = ramfuc_reg(0x004168); + ram->fuc.r_0x100080 = ramfuc_reg(0x100080); + ram->fuc.r_0x100200 = ramfuc_reg(0x100200); + ram->fuc.r_0x100210 = ramfuc_reg(0x100210); + for (i = 0; i < 9; i++) + ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4)); + ram->fuc.r_0x100264 = ramfuc_reg(0x100264); + ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0); + ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4); + ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc); + ram->fuc.r_0x10053c = ramfuc_reg(0x10053c); + ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0); + ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4); + ram->fuc.r_0x100700 = ramfuc_reg(0x100700); + ram->fuc.r_0x100714 = ramfuc_reg(0x100714); + ram->fuc.r_0x100718 = ramfuc_reg(0x100718); + ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); + ram->fuc.r_0x100720 = ramfuc_reg(0x100720); + ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); + ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); + ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); + ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask); + ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); + ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); + ram->fuc.r_0x111100 = ramfuc_reg(0x111100); + ram->fuc.r_0x111104 = ramfuc_reg(0x111104); + ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0); + ram->fuc.r_0x111400 = ramfuc_reg(0x111400); + ram->fuc.r_0x611200 = ramfuc_reg(0x611200); + + if (ram->base.ranks > 1) { + ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8); + ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc); + ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8); + ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec); + } else { + ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0); + ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4); + ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0); + ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4); + } + ram->fuc.r_gpio[0] = ramfuc_reg(0x00e104); + ram->fuc.r_gpio[1] = ramfuc_reg(0x00e108); + ram->fuc.r_gpio[2] = ramfuc_reg(0x00e120); + ram->fuc.r_gpio[3] = ramfuc_reg(0x00e124); + + return 0; +} |