Преглед изворни кода

[pci] Add support for reading and writing PCI Vital Product Data (VPD)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown пре 14 година
родитељ
комит
69db6e7d8f
3 измењених фајлова са 552 додато и 0 уклоњено
  1. 386
    0
      src/drivers/bus/pcivpd.c
  2. 1
    0
      src/include/ipxe/errfile.h
  3. 165
    0
      src/include/ipxe/pcivpd.h

+ 386
- 0
src/drivers/bus/pcivpd.c Прегледај датотеку

@@ -0,0 +1,386 @@
1
+/*
2
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+#include <stdint.h>
22
+#include <unistd.h>
23
+#include <errno.h>
24
+#include <byteswap.h>
25
+#include <ipxe/pci.h>
26
+#include <ipxe/isapnp.h>
27
+#include <ipxe/pcivpd.h>
28
+
29
+/** @file
30
+ *
31
+ * PCI Vital Product Data
32
+ *
33
+ */
34
+
35
+/**
36
+ * Initialise PCI Vital Product Data
37
+ *
38
+ * @v vpd		PCI VPD
39
+ * @v pci		PCI device
40
+ * @ret rc		Return status code
41
+ */
42
+int pci_vpd_init ( struct pci_vpd *vpd, struct pci_device *pci ) {
43
+
44
+	/* Initialise structure */
45
+	vpd->pci = pci;
46
+	pci_vpd_invalidate_cache ( vpd );
47
+
48
+	/* Locate VPD capability */
49
+	vpd->cap = pci_find_capability ( pci, PCI_CAP_ID_VPD );
50
+	if ( ! vpd->cap ) {
51
+		DBGC ( vpd, PCI_FMT " does not support VPD\n",
52
+		       PCI_ARGS ( pci ) );
53
+		return -ENOTTY;
54
+	}
55
+
56
+	DBGC ( vpd, PCI_FMT " VPD is at offset %02x\n",
57
+	       PCI_ARGS ( pci ), vpd->cap );
58
+	return 0;
59
+}
60
+
61
+/**
62
+ * Read one dword of PCI Vital Product Data
63
+ *
64
+ * @v vpd		PCI VPD
65
+ * @v address		Address to read
66
+ * @ret data		Read data
67
+ * @ret rc		Return status code
68
+ */
69
+static int pci_vpd_read_dword ( struct pci_vpd *vpd, int address,
70
+				uint32_t *data ) {
71
+	struct pci_device *pci = vpd->pci;
72
+	unsigned int cap = vpd->cap;
73
+	unsigned int retries;
74
+	uint16_t flag;
75
+
76
+	/* Return cached value, if present */
77
+	if ( pci_vpd_cache_is_valid ( vpd ) &&
78
+	     ( vpd->cache.address == address ) ) {
79
+		*data = vpd->cache.data;
80
+		return 0;
81
+	}
82
+
83
+	/* Initiate read */
84
+	pci_write_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), address );
85
+
86
+	/* Wait for read to complete */
87
+	for ( retries = 0 ; retries < PCI_VPD_MAX_WAIT_MS ; retries++ ) {
88
+
89
+		/* Check if data is ready */
90
+		pci_read_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), &flag );
91
+		if ( flag & PCI_VPD_FLAG ) {
92
+
93
+			/* Read data */
94
+			pci_read_config_dword ( pci, ( cap + PCI_VPD_DATA ),
95
+						data );
96
+			DBGC2 ( vpd, PCI_FMT " VPD %04x => %08x\n",
97
+				PCI_ARGS ( pci ), address, htonl ( *data ) );
98
+
99
+			/* Populate cache */
100
+			vpd->cache.address = address;
101
+			vpd->cache.data = *data;
102
+
103
+			return 0;
104
+		}
105
+
106
+		/* Wait 1ms before retrying */
107
+		mdelay ( 1 );
108
+	}
109
+
110
+	DBGC ( vpd, PCI_FMT " VPD %04x read via %02x timed out\n",
111
+	       PCI_ARGS ( pci ), address, cap );
112
+	return -ETIMEDOUT;
113
+}
114
+
115
+/**
116
+ * Write one dword of PCI Vital Product Data
117
+ *
118
+ * @v vpd		PCI VPD
119
+ * @v address		Address to write
120
+ * @v data		Data to write
121
+ * @ret rc		Return status code
122
+ */
123
+static int pci_vpd_write_dword ( struct pci_vpd *vpd, int address,
124
+				 uint32_t data ) {
125
+	struct pci_device *pci = vpd->pci;
126
+	unsigned int cap = vpd->cap;
127
+	unsigned int retries;
128
+	uint16_t flag;
129
+
130
+	/* Invalidate cache */
131
+	pci_vpd_invalidate_cache ( vpd );
132
+
133
+	DBGC2 ( vpd, PCI_FMT " VPD %04x <= %08x\n",
134
+		PCI_ARGS ( pci ), address, htonl ( data ) );
135
+
136
+	/* Write data */
137
+	pci_write_config_dword ( pci, ( cap + PCI_VPD_DATA ), data );
138
+
139
+	/* Initiate write */
140
+	pci_write_config_word ( pci, ( cap + PCI_VPD_ADDRESS ),
141
+				( address | PCI_VPD_FLAG ) );
142
+
143
+	/* Wait for write to complete */
144
+	for ( retries = 0 ; retries < PCI_VPD_MAX_WAIT_MS ; retries++ ) {
145
+
146
+		/* Check if write has completed */
147
+		pci_read_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), &flag );
148
+		if ( ! ( flag & PCI_VPD_FLAG ) )
149
+			return 0;
150
+
151
+		/* Wait 1ms before retrying */
152
+		mdelay ( 1 );
153
+	}
154
+
155
+	DBGC ( vpd, PCI_FMT " VPD %04x write via %02x timed out\n",
156
+	       PCI_ARGS ( pci ), address, cap );
157
+	return -ETIMEDOUT;
158
+}
159
+
160
+/**
161
+ * Read PCI VPD
162
+ *
163
+ * @v vpd		PCI VPD
164
+ * @v address		Starting address
165
+ * @v buf		Data buffer
166
+ * @v len		Length of data buffer
167
+ * @ret rc		Return status code
168
+ */
169
+int pci_vpd_read ( struct pci_vpd *vpd, unsigned int address, void *buf,
170
+		   size_t len ) {
171
+	uint8_t *bytes = buf;
172
+	uint32_t data;
173
+	size_t skip_len;
174
+	unsigned int i;
175
+	int rc;
176
+
177
+	/* Calculate length to skip at start of data */
178
+	skip_len = ( address & 0x03 );
179
+
180
+	/* Read data, a dword at a time */
181
+	for ( address &= ~0x03 ; len ; address += 4 ) {
182
+
183
+		/* Read whole dword */
184
+		if ( ( rc = pci_vpd_read_dword ( vpd, address, &data ) ) != 0 )
185
+			return rc;
186
+
187
+		/* Copy data to buffer */
188
+		for ( i = 4 ; i ; i-- ) {
189
+			if ( skip_len ) {
190
+				skip_len--;
191
+			} else if ( len ) {
192
+				*(bytes++) = data;
193
+				len--;
194
+			}
195
+			data = ( ( data << 24 ) | ( data >> 8 ) );
196
+		}
197
+	}
198
+
199
+	return 0;
200
+}
201
+
202
+/**
203
+ * Write PCI VPD
204
+ *
205
+ * @v vpd		PCI VPD
206
+ * @v address		Starting address
207
+ * @v buf		Data buffer
208
+ * @v len		Length of data buffer
209
+ * @ret rc		Return status code
210
+ */
211
+int pci_vpd_write ( struct pci_vpd *vpd, unsigned int address, const void *buf,
212
+		    size_t len ) {
213
+	const uint8_t *bytes = buf;
214
+	uint32_t data;
215
+	size_t skip_len;
216
+	unsigned int i;
217
+	int rc;
218
+
219
+	/* Calculate length to skip at start of data */
220
+	skip_len = ( address & 0x03 );
221
+
222
+	/* Write data, a dword at a time */
223
+	for ( address &= ~0x03 ; len ; address += 4 ) {
224
+
225
+		/* Read existing dword, if necessary */
226
+		if ( skip_len || ( len <= 0x03 ) ) {
227
+			if ( ( rc = pci_vpd_read_dword ( vpd, address,
228
+							 &data ) ) != 0 )
229
+				return rc;
230
+		}
231
+
232
+		/* Copy data from buffer */
233
+		for ( i = 4 ; i ; i-- ) {
234
+			if ( skip_len ) {
235
+				skip_len--;
236
+			} else if ( len ) {
237
+				data = ( ( data & ~0xff ) | *(bytes++) );
238
+				len--;
239
+			}
240
+			data = ( ( data << 24 ) | ( data >> 8 ) );
241
+		}
242
+
243
+		/* Write whole dword */
244
+		if ( ( rc = pci_vpd_write_dword ( vpd, address, data ) ) != 0 )
245
+			return rc;
246
+	}
247
+	return 0;
248
+}
249
+
250
+/**
251
+ * Dump PCI VPD region (for debugging)
252
+ *
253
+ * @v vpd		PCI VPD
254
+ * @v address		Starting address
255
+ * @v len		Length of data
256
+ */
257
+static void pci_vpd_dump ( struct pci_vpd *vpd, unsigned int address,
258
+			   size_t len ) {
259
+	int rc;
260
+
261
+	/* Do nothing in non-debug builds */
262
+	if ( ! DBG_LOG )
263
+		return;
264
+
265
+	/* Read data */
266
+	{
267
+		char buf[len];
268
+		if ( ( rc = pci_vpd_read ( vpd, address, buf,
269
+					   sizeof ( buf ) ) ) != 0 )
270
+			return;
271
+		DBGC_HDA ( vpd, address, buf, sizeof ( buf ) );
272
+	}
273
+}
274
+
275
+/**
276
+ * Locate PCI VPD tag
277
+ *
278
+ * @v vpd		PCI VPD
279
+ * @v tag		ISAPnP tag
280
+ * @ret address		Address of tag body
281
+ * @ret len		Length of tag body
282
+ * @ret rc		Return status code
283
+ */
284
+static int pci_vpd_find_tag ( struct pci_vpd *vpd, unsigned int tag,
285
+			      unsigned int *address, size_t *len ) {
286
+	uint8_t read_tag;
287
+	uint16_t read_len;
288
+	int rc;
289
+
290
+	/* Scan through tags looking for a match */
291
+	*address = 0;
292
+	do {
293
+		/* Read tag byte */
294
+		if ( ( rc = pci_vpd_read ( vpd, (*address)++, &read_tag,
295
+					   sizeof ( read_tag ) ) ) != 0 )
296
+			return rc;
297
+
298
+		/* Extract tag and length */
299
+		if ( ISAPNP_IS_LARGE_TAG ( read_tag ) ) {
300
+			if ( ( rc = pci_vpd_read ( vpd, *address, &read_len,
301
+						   sizeof ( read_len ) ) ) != 0)
302
+				return rc;
303
+			*address += sizeof ( read_len );
304
+			read_len = le16_to_cpu ( read_len );
305
+			read_tag = ISAPNP_LARGE_TAG_NAME ( read_tag );
306
+		} else {
307
+			read_len = ISAPNP_SMALL_TAG_LEN ( read_tag );
308
+			read_tag = ISAPNP_SMALL_TAG_NAME ( read_tag );
309
+		}
310
+
311
+		/* Check for tag match */
312
+		if ( tag == read_tag ) {
313
+			*len = read_len;
314
+			DBGC ( vpd, PCI_FMT " VPD tag %02x is at "
315
+			       "[%04x,%04zx)\n", PCI_ARGS ( vpd->pci ), tag,
316
+			       *address, ( *address + *len ) );
317
+			return 0;
318
+		}
319
+
320
+		/* Move to next tag */
321
+		*address += read_len;
322
+
323
+	} while ( read_tag != ISAPNP_TAG_END );
324
+
325
+	DBGC ( vpd, PCI_FMT " VPD tag %02x not found\n",
326
+	       PCI_ARGS ( vpd->pci ), tag );
327
+	return -ENOENT;
328
+}
329
+
330
+/**
331
+ * Locate PCI VPD field
332
+ *
333
+ * @v vpd		PCI VPD
334
+ * @v field		VPD field descriptor
335
+ * @ret address		Address of field body
336
+ * @ret len		Length of field body
337
+ * @ret rc		Return status code
338
+ */
339
+int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field,
340
+		   unsigned int *address, size_t *len ) {
341
+	struct pci_vpd_field read_field;
342
+	int rc;
343
+
344
+	/* Locate containing tag */
345
+	if ( ( rc = pci_vpd_find_tag ( vpd, PCI_VPD_TAG ( field ),
346
+				       address, len ) ) != 0 )
347
+		return rc;
348
+
349
+	/* Return immediately if we are searching for a whole-tag field */
350
+	if ( ! PCI_VPD_KEYWORD ( field ) ) {
351
+		pci_vpd_dump ( vpd, *address, *len );
352
+		return 0;
353
+	}
354
+
355
+	/* Scan through fields looking for a match */
356
+	while ( *len >= sizeof ( read_field ) ) {
357
+
358
+		/* Read field header */
359
+		if ( ( rc = pci_vpd_read ( vpd, *address, &read_field,
360
+					   sizeof ( read_field ) ) ) != 0 )
361
+			return rc;
362
+		*address += sizeof ( read_field );
363
+		*len -= sizeof ( read_field );
364
+
365
+		/* Check for keyword match */
366
+		if ( read_field.keyword == PCI_VPD_KEYWORD ( field ) ) {
367
+			*len = read_field.len;
368
+			DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
369
+			       " is at [%04x,%04zx)\n", PCI_ARGS ( vpd->pci ),
370
+			       PCI_VPD_FIELD_ARGS ( field ),
371
+			       *address, ( *address + *len ) );
372
+			pci_vpd_dump ( vpd, *address, *len );
373
+			return 0;
374
+		}
375
+
376
+		/* Move to next field */
377
+		if ( read_field.len > *len )
378
+			break;
379
+		*address += read_field.len;
380
+		*len -= read_field.len;
381
+	}
382
+
383
+	DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " not found\n",
384
+	       PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ) );
385
+	return -ENOENT;
386
+}

+ 1
- 0
src/include/ipxe/errfile.h Прегледај датотеку

@@ -67,6 +67,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
67 67
 #define ERRFILE_mca		     ( ERRFILE_DRIVER | 0x00030000 )
68 68
 #define ERRFILE_pci		     ( ERRFILE_DRIVER | 0x00040000 )
69 69
 #define ERRFILE_linux		     ( ERRFILE_DRIVER | 0x00050000 )
70
+#define ERRFILE_pcivpd		     ( ERRFILE_DRIVER | 0x00060000 )
70 71
 
71 72
 #define ERRFILE_nvs		     ( ERRFILE_DRIVER | 0x00100000 )
72 73
 #define ERRFILE_spi		     ( ERRFILE_DRIVER | 0x00110000 )

+ 165
- 0
src/include/ipxe/pcivpd.h Прегледај датотеку

@@ -0,0 +1,165 @@
1
+#ifndef _IPXE_PCIVPD_H
2
+#define _IPXE_PCIVPD_H
3
+
4
+/**
5
+ * @file
6
+ *
7
+ * PCI Vital Product Data
8
+ *
9
+ */
10
+
11
+FILE_LICENCE ( GPL2_OR_LATER );
12
+
13
+#include <stdint.h>
14
+#include <byteswap.h>
15
+#include <ipxe/isapnp.h>
16
+#include <ipxe/pci.h>
17
+
18
+/** PCI VPD address register */
19
+#define PCI_VPD_ADDRESS 0x02
20
+
21
+/** PCI VPD write flag */
22
+#define PCI_VPD_FLAG 0x8000
23
+
24
+/** PCI VPD data register */
25
+#define PCI_VPD_DATA 0x04
26
+
27
+/** A PCI VPD field */
28
+struct pci_vpd_field {
29
+	/** Keyword */
30
+	uint16_t keyword;
31
+	/** Length */
32
+	uint8_t len;
33
+} __attribute__ (( packed ));
34
+
35
+/** Construct PCI VPD field descriptor
36
+ *
37
+ * @v tag		ISAPnP tag
38
+ * @v keyword1		First character of keyword
39
+ * @v keyword2		Second character of keyword
40
+ * @ret field		VPD field descriptor
41
+ */
42
+#define PCI_VPD_FIELD( tag, keyword1, keyword2 ) \
43
+	( ( (tag) << 16 ) | ( (keyword2) << 8 ) | ( (keyword1) << 0 ) )
44
+
45
+/** Construct PCI VPD whole-tag field descriptor
46
+ *
47
+ * @v tag		ISAPnP tag
48
+ * @ret field		VPD field descriptor
49
+ */
50
+#define PCI_VPD_WHOLE_TAG_FIELD( tag ) PCI_VPD_FIELD ( (tag), '\0', '\0' )
51
+
52
+/** Extract PCI VPD ISAPnP tag
53
+ *
54
+ * @v field		VPD field descriptor
55
+ * @ret tag		ISAPnP tag
56
+ */
57
+#define PCI_VPD_TAG( field ) ( (field) >> 16 )
58
+
59
+/** Extract PCI VPD keyword
60
+ *
61
+ * @v field		VPD field descriptor
62
+ * @ret keyword		Keyword
63
+ */
64
+#define PCI_VPD_KEYWORD( field ) ( cpu_to_le16 ( (field) & 0xffff ) )
65
+
66
+/** PCI VPD field debug message format */
67
+#define PCI_VPD_FIELD_FMT "%c%c"
68
+
69
+/** PCI VPD field debug message arguments */
70
+#define PCI_VPD_FIELD_ARGS( field ) \
71
+	( (field) >> 0 ), ( (field) >> 8 )
72
+
73
+/** PCI VPD Read-Only field tag */
74
+#define PCI_VPD_TAG_RO 0x90
75
+
76
+/** PCI VPD Read-Write field tag */
77
+#define PCI_VPD_TAG_RW 0x91
78
+
79
+/** PCI VPD Card Name field descriptor */
80
+#define PCI_VPD_FIELD_NAME PCI_VPD_WHOLE_TAG_FIELD ( ISAPNP_TAG_ANSISTR )
81
+
82
+/** PCI VPD Part Number field descriptor */
83
+#define PCI_VPD_FIELD_PN PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'N' )
84
+
85
+/** PCI VPD Engineering Change Level field descriptor */
86
+#define PCI_VPD_FIELD_EC PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'E', 'C' )
87
+
88
+/** PCI VPD Fabric Geography field descriptor */
89
+#define PCI_VPD_FIELD_FG PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'F', 'G' )
90
+
91
+/** PCI VPD Location field descriptor */
92
+#define PCI_VPD_FIELD_LC PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'L', 'C' )
93
+
94
+/** PCI VPD Manufacturer ID field descriptor */
95
+#define PCI_VPD_FIELD_MN PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'M', 'N' )
96
+
97
+/** PCI VPD PCI Geography field descriptor */
98
+#define PCI_VPD_FIELD_PG PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'G' )
99
+
100
+/** PCI VPD Serial Number field descriptor */
101
+#define PCI_VPD_FIELD_SN PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'S', 'N' )
102
+
103
+/** PCI VPD Extended Capability field descriptor */
104
+#define PCI_VPD_FIELD_CP PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'C', 'P' )
105
+
106
+/** PCI VPD Checksum and Reserved field descriptor */
107
+#define PCI_VPD_FIELD_RV PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'R', 'V' )
108
+
109
+/** PCI VPD Asset Tag field descriptor */
110
+#define PCI_VPD_FIELD_YA PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'Y', 'A' )
111
+
112
+/** PCI VPD Remaining Read/Write Area field descriptor */
113
+#define PCI_VPD_FIELD_RW PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'R', 'W' )
114
+
115
+/** Maximum wait for PCI VPD (in ms) */
116
+#define PCI_VPD_MAX_WAIT_MS 100
117
+
118
+/** PCI VPD cache */
119
+struct pci_vpd_cache {
120
+	/** Address */
121
+	int address;
122
+	/** Data */
123
+	uint32_t data;
124
+};
125
+
126
+/** PCI VPD */
127
+struct pci_vpd {
128
+	/** PCI device */
129
+	struct pci_device *pci;
130
+	/** VPD capability offset */
131
+	int cap;
132
+	/** Read cache */
133
+	struct pci_vpd_cache cache;
134
+};
135
+
136
+/**
137
+ * Check if PCI VPD read cache is valid
138
+ *
139
+ * @v vpd		PCI VPD
140
+ * @ret is_valid	Read cache is valid
141
+ */
142
+static inline __attribute__ (( always_inline )) int
143
+pci_vpd_cache_is_valid ( struct pci_vpd *vpd ) {
144
+	return ( vpd->cache.address >= 0 );
145
+}
146
+
147
+/**
148
+ * Invalidate PCI VPD read cache
149
+ *
150
+ * @v vpd		PCI VPD
151
+ */
152
+static inline __attribute__ (( always_inline )) void
153
+pci_vpd_invalidate_cache ( struct pci_vpd *vpd ) {
154
+	vpd->cache.address = -1;
155
+}
156
+
157
+extern int pci_vpd_init ( struct pci_vpd *vpd, struct pci_device *pci );
158
+extern int pci_vpd_read ( struct pci_vpd *vpd, unsigned int address,
159
+			  void *buf, size_t len );
160
+extern int pci_vpd_write ( struct pci_vpd *vpd, unsigned int address,
161
+			   const void *buf, size_t len );
162
+extern int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field,
163
+			  unsigned int *address, size_t *len );
164
+
165
+#endif /* _IPXE_PCIVPD_H */

Loading…
Откажи
Сачувај