1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2020 IBM Corp.
*/
#include <timebase.h>
#include <pau.h>
#define PAU_PHY_INIT_TIMEOUT 8000 /* ms */
#define PAU_PHY_ADDR_REG 0x10012C0D
#define PAU_PHY_ADDR_CHIPLET PPC_BITMASK(32, 39)
#define PAU_PHY_ADDR_SRAM_ADDR PPC_BITMASK(15, 31)
#define PAU_PHY_DATA_REG 0x10012C0E
#define PAU_PHY_DATA_CHIPLET PPC_BITMASK(32, 39)
#define PAU_MAX_PHY_LANE 18
/*
* We configure the PHY using the memory mapped SRAM, which is
* accessible through a pair of (addr, data) registers. The caveat is
* that accesses to the SRAM must be 64-bit aligned, yet the PHY
* registers are 16-bit, so special care is needed.
*
* A PAU chiplet may control up to 2 OP units = 4 links and each link
* has its own virtual PHB in skiboot. They can be initialized or
* reset concurrently so we need a lock when accessing the SRAM.
* See section "5.2.5 PPE SRAM" of the workbook for the layout of the
* SRAM registers. Here is the subset of the table which is meaningful
* for us, since we're only touching a few registers:
*
* Address Bytes Linker Symbol Description
* FFFF_11B0 16 _fw_regs0_start fw_regs for thread 0
* FFFF_11C0 16 _fw_regs1_start fw_regs for thread 1
*
* FFFF_2800 1024 _mem_regs0_start mem_regs for thread 0
* FFFF_2C00 1024 _mem_regs1_start mem_regs for thread 1
*
* In each PAU, per-group registers are replicated for every OP (each
* OP units is being called a 'thread' in the workbook).
* Per-lane registers have an offset < 0x10 and are replicated for
* each lane. Their offset in their section is:
* 0byyyyyxxxx (y = 5-bit lane number, x = 4-bit per-lane register offset)
*/
struct PPE_sram_section {
uint32_t offset;
uint32_t size;
};
static struct PPE_sram_section PPE_FIRMWARE = { 0x111B0, 0x10 };
static struct PPE_sram_section PPE_MEMORY = { 0x12800, 0x400 };
struct PPE_sram_reg {
struct PPE_sram_section *section;
uint32_t offset;
};
/* PPE firmware */
static struct PPE_sram_reg PAU_PHY_EXT_CMD_LANES_00_15 = { &PPE_FIRMWARE, 0x000 };
static struct PPE_sram_reg PAU_PHY_EXT_CMD_LANES_16_31 = { &PPE_FIRMWARE, 0x001 };
static struct PPE_sram_reg PAU_PHY_EXT_CMD_REQ = { &PPE_FIRMWARE, 0x002 };
#define PAU_PHY_EXT_CMD_REQ_IO_RESET PPC_BIT16(1)
#define PAU_PHY_EXT_CMD_REQ_DCCAL PPC_BIT16(3)
#define PAU_PHY_EXT_CMD_REQ_TX_ZCAL PPC_BIT16(4)
#define PAU_PHY_EXT_CMD_REQ_TX_FFE PPC_BIT16(5)
#define PAU_PHY_EXT_CMD_REQ_POWER_ON PPC_BIT16(7)
static struct PPE_sram_reg PAU_PHY_EXT_CMD_DONE = { &PPE_FIRMWARE, 0x005 };
/* PPE memory */
static struct PPE_sram_reg PAU_PHY_RX_PPE_CNTL1 = { &PPE_MEMORY, 0x000 };
#define PAU_PHY_RX_ENABLE_AUTO_RECAL PPC_BIT16(1)
enum pau_phy_status {
PAU_PROC_INPROGRESS,
PAU_PROC_COMPLETE,
PAU_PROC_NEXT,
PAU_PROC_FAILED
};
struct procedure {
const char *name;
uint32_t (*steps[])(struct pau_dev *);
};
#define DEFINE_PROCEDURE(NAME, STEPS...) \
static struct procedure procedure_##NAME = { \
.name = #NAME, \
.steps = { STEPS } \
}
/*
* We could/should have one phy_sram_lock per PAU chiplet. Each PAU
* chiplet drives 2 OPT units. Since we don't have a PAU chiplet
* structure to host the lock and don't anticipate much contention, we
* go with a global lock for now
*/
static struct lock phy_sram_lock = LOCK_UNLOCKED;
static int get_thread_id(uint32_t op_unit)
{
int ppe_thread[8] = { 0, 1, 1, 0, 1, 0, 1, 0 };
/* static mapping between OP unit and PPE thread ID */
if (op_unit >= sizeof(ppe_thread))
return -1;
return ppe_thread[op_unit];
}
/*
* Compute the address in the memory mapped SRAM of a 16-bit PHY register
*/
static uint32_t pau_phy_sram_addr(struct pau_dev *dev,
struct PPE_sram_reg *reg,
int lane)
{
uint32_t base, addr;
base = reg->section->offset +
reg->section->size * get_thread_id(dev->op_unit);
addr = reg->offset;
if (lane >= 0) {
assert(reg->offset < 0x10);
addr += lane << 4;
}
addr <<= 1; // each register is 16-bit
return base + addr;
}
static void pau_phy_set_access(struct pau_dev *dev,
struct PPE_sram_reg *reg, int lane,
uint64_t *data_addr, uint64_t *mask)
{
struct pau *pau = dev->pau;
uint64_t scom_addr, sram_addr, addr, bit_start;
scom_addr = SETFIELD(PAU_PHY_ADDR_CHIPLET, PAU_PHY_ADDR_REG,
pau->op_chiplet);
sram_addr = pau_phy_sram_addr(dev, reg, lane);
bit_start = 8 * (sram_addr & 7);
addr = SETFIELD(PAU_PHY_ADDR_SRAM_ADDR, 0ull, sram_addr & 0xFFFFFFF8);
xscom_write(pau->chip_id, scom_addr, addr);
*data_addr = SETFIELD(PAU_PHY_DATA_CHIPLET, PAU_PHY_DATA_REG,
pau->op_chiplet);
*mask = PPC_BITMASK(bit_start, bit_start + 15);
}
static void pau_phy_write_lane(struct pau_dev *dev,
struct PPE_sram_reg *reg, int lane,
uint16_t val)
{
struct pau *pau = dev->pau;
uint64_t data_addr, scom_val, mask;
lock(&phy_sram_lock);
pau_phy_set_access(dev, reg, lane, &data_addr, &mask);
xscom_read(pau->chip_id, data_addr, &scom_val);
scom_val = SETFIELD(mask, scom_val, val);
xscom_write(pau->chip_id, data_addr, scom_val);
unlock(&phy_sram_lock);
}
static uint16_t pau_phy_read_lane(struct pau_dev *dev,
struct PPE_sram_reg *reg, int lane)
{
struct pau *pau = dev->pau;
uint64_t data_addr, scom_val, mask;
uint16_t res;
lock(&phy_sram_lock);
pau_phy_set_access(dev, reg, lane, &data_addr, &mask);
xscom_read(pau->chip_id, data_addr, &scom_val);
res = GETFIELD(mask, scom_val);
unlock(&phy_sram_lock);
return res;
}
static void pau_phy_write(struct pau_dev *dev, struct PPE_sram_reg *reg,
uint16_t val)
{
pau_phy_write_lane(dev, reg, -1, val);
}
static uint16_t pau_phy_read(struct pau_dev *dev, struct PPE_sram_reg *reg)
{
return pau_phy_read_lane(dev, reg, -1);
}
static uint16_t get_reset_request_val(void)
{
return PAU_PHY_EXT_CMD_REQ_IO_RESET |
PAU_PHY_EXT_CMD_REQ_DCCAL |
PAU_PHY_EXT_CMD_REQ_TX_ZCAL |
PAU_PHY_EXT_CMD_REQ_TX_FFE |
PAU_PHY_EXT_CMD_REQ_POWER_ON;
}
static uint32_t reset_start(struct pau_dev *dev)
{
uint16_t val16;
// Procedure IO_INIT_RESET_PON
// Clear external command request / done registers
val16 = 0;
pau_phy_write(dev, &PAU_PHY_EXT_CMD_REQ, val16);
pau_phy_write(dev, &PAU_PHY_EXT_CMD_DONE, val16);
// Write the external command lanes to target
val16 = dev->phy_lane_mask >> 16;
pau_phy_write(dev, &PAU_PHY_EXT_CMD_LANES_00_15, val16);
val16 = dev->phy_lane_mask & 0xFFFF;
pau_phy_write(dev, &PAU_PHY_EXT_CMD_LANES_16_31, val16);
// Initialize PHY Lanes
val16 = get_reset_request_val();
pau_phy_write(dev, &PAU_PHY_EXT_CMD_REQ, val16);
return PAU_PROC_NEXT;
}
static uint32_t reset_check(struct pau_dev *dev)
{
uint16_t val16, done;
val16 = get_reset_request_val();
done = pau_phy_read(dev, &PAU_PHY_EXT_CMD_DONE);
if (val16 == done)
return PAU_PROC_NEXT;
else
return PAU_PROC_INPROGRESS;
}
static uint32_t enable_recal(struct pau_dev *dev)
{
uint32_t lane;
// Enable auto-recalibration
for (lane = 0; lane <= PAU_MAX_PHY_LANE; lane++)
if (!(dev->phy_lane_mask & (1 << (31 - lane))))
continue;
else
pau_phy_write_lane(dev, &PAU_PHY_RX_PPE_CNTL1,
lane, PAU_PHY_RX_ENABLE_AUTO_RECAL);
return PAU_PROC_COMPLETE;
}
DEFINE_PROCEDURE(phy_reset, reset_start, reset_check, enable_recal);
static enum pau_phy_status run_steps(struct pau_dev *dev)
{
struct procedure *p = &procedure_phy_reset;
struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
enum pau_phy_status rc;
do {
rc = p->steps[procedure_state->step](dev);
if (rc == PAU_PROC_NEXT) {
procedure_state->step++;
PAUDEVDBG(dev, "Running procedure %s step %d\n",
p->name, procedure_state->step);
}
} while (rc == PAU_PROC_NEXT);
return rc;
}
static enum pau_phy_status run_procedure(struct pau_dev *dev)
{
struct procedure *p = &procedure_phy_reset;
struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
enum pau_phy_status rc;
do {
rc = run_steps(dev);
if (rc == PAU_PROC_INPROGRESS) {
if (tb_compare(mftb(), procedure_state->timeout) == TB_AAFTERB) {
PAUDEVERR(dev, "Procedure %s timed out\n", p->name);
rc = PAU_PROC_FAILED;
} else {
time_wait_ms(1);
}
}
} while (rc == PAU_PROC_INPROGRESS);
return rc;
}
int pau_dev_phy_reset(struct pau_dev *dev)
{
struct procedure *p = &procedure_phy_reset;
struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
enum pau_phy_status rc;
lock(&procedure_state->lock);
procedure_state->step = 0;
procedure_state->timeout = mftb() + msecs_to_tb(PAU_PHY_INIT_TIMEOUT);
PAUDEVDBG(dev, "Running procedure %s step %d\n",
p->name, procedure_state->step);
rc = run_procedure(dev);
unlock(&procedure_state->lock);
if (rc == PAU_PROC_COMPLETE) {
PAUDEVDBG(dev, "Procedure %s complete\n", p->name);
return OPAL_SUCCESS;
}
PAUDEVDBG(dev, "Procedure %s failed\n", p->name);
return OPAL_HARDWARE;
}
|