/* Ground-truth C for FUN_0000d328 @ blob offset 0xd328 (104 bytes / 26 insts). * * **The first real poll-site function we lift to C.** * Contains 4 of our 16 timeout-less polls (sites 12, 13, 14, 15). * * Pattern: PHY-block training step — poke a control register, wait for * two status bits, apply two intermediate values with a * handshake on a state register, ack the event. * * Signature: void train_phy_block(struct phy_ctx *ctx); * (X0 = ctx, returns void) * * Layout: * ctx (X0) — opaque per-rank/per-channel context * ctx->base[0xb8] — 64-bit pointer to a PHY block base * block + 0x8000 — addressed sub-block (likely "Master" bank in DWC PUB) * * The sub-block at +0x8000 has these registers (offsets within +0x8000): * +0x110 CTL — write 0xF000F000 to start, 0xF0000000 to clear * +0x118 STAT_A — bit[31:28] non-zero = step A done * +0x120 STAT_B — bit[31:28] non-zero = step B done * +0x154 CFG_A — write training value * +0x160 CFG_B — write training value * +0x184 HANDSHAKE — bits[1:0] toggle between 0 and !=0 to ack writes * * The 4 polls (in order): * site 12 (B.EQ): STAT_A bit[31:28] non-zero? * site 13 (B.EQ): STAT_B bit[31:28] non-zero? * site 14 (B.EQ): HANDSHAKE bits[1:0] non-zero? (ack of step-1 writes) * site 15 (B.NE): HANDSHAKE bits[1:0] zero? (ack of step-2 write) */ #include struct phy_ctx { uint8_t pad[0xB8]; uint8_t *block; /* base pointer used at +0xB8 in struct */ /* ... rest of struct unknown */ }; #define PHY_CTL 0x110 #define PHY_STAT_A 0x118 #define PHY_STAT_B 0x120 #define PHY_CFG_A 0x154 #define PHY_CFG_B 0x160 #define PHY_HANDSHAKE 0x184 #define PHY_CTL_GO 0xF000F000U #define PHY_CTL_CLR 0xF0000000U #define PHY_STAT_DONE 0xF0000000U #define PHY_CFG_VAL_RUN 0x00030003U #define PHY_CFG_VAL_END 0x00030000U #define PHY_HS_BUSY 0x3U static inline uint32_t mmio_r(volatile uint8_t *base, unsigned off) { return *(volatile uint32_t *)(base + off); } static inline void mmio_w(volatile uint8_t *base, unsigned off, uint32_t v) { *(volatile uint32_t *)(base + off) = v; } void train_phy_block(struct phy_ctx *ctx) { volatile uint8_t *phy = (volatile uint8_t *)(ctx->block + 0x8000); mmio_w(phy, PHY_CTL, PHY_CTL_GO); /* site 12 — wait for step A complete */ while ((mmio_r(phy, PHY_STAT_A) & PHY_STAT_DONE) == 0) ; /* site 13 — wait for step B complete */ while ((mmio_r(phy, PHY_STAT_B) & PHY_STAT_DONE) == 0) ; mmio_w(phy, PHY_CFG_B, PHY_CFG_VAL_RUN); mmio_w(phy, PHY_CFG_A, PHY_CFG_VAL_RUN); /* site 14 — wait for handshake to assert */ while ((mmio_r(phy, PHY_HANDSHAKE) & PHY_HS_BUSY) == 0) ; mmio_w(phy, PHY_CFG_A, PHY_CFG_VAL_END); /* site 15 — wait for handshake to deassert */ while ((mmio_r(phy, PHY_HANDSHAKE) & PHY_HS_BUSY) != 0) ; mmio_w(phy, PHY_CFG_B, PHY_CFG_VAL_END); mmio_w(phy, PHY_CTL, PHY_CTL_CLR); }