aboutsummaryrefslogtreecommitdiff
path: root/hw/pau-hw-procedures.c
blob: bd299b3fcddc7dc32a9ec8210beaf0954713bb9e (plain)
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;
}