Print this page
XXXX introduce drv_sectohz
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/sparc/io/pciex/pcieb_sparc.c
+++ new/usr/src/uts/sparc/io/pciex/pcieb_sparc.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 /* SPARC specific code used by the pcieb driver */
27 27
28 28 #include <sys/types.h>
29 29 #include <sys/ddi.h>
30 30 #include <sys/kmem.h>
31 31 #include <sys/sysmacros.h>
32 32 #include <sys/sunddi.h>
33 33 #include <sys/sunndi.h>
34 34 #include <sys/pcie.h>
35 35 #include <sys/pci_cap.h>
36 36 #include <sys/pcie_impl.h>
37 37 #include <io/pciex/pcieb.h>
38 38 #include "pcieb_plx.h"
39 39
40 40 /*LINTLIBRARY*/
41 41
42 42 /* PLX specific functions */
43 43 #ifdef PX_PLX
44 44 static void plx_ro_disable(pcieb_devstate_t *pcieb);
45 45 #ifdef PRINT_PLX_SEEPROM_CRC
46 46 static void pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p);
47 47 #endif /* PRINT_PLX_SEEPROM_CRC */
48 48 #endif /* PX_PLX */
49 49
50 50 int
51 51 pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
52 52 void *arg, void *result)
53 53 {
54 54 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
55 55 }
56 56
57 57 /*ARGSUSED*/
58 58 void
59 59 pcieb_set_prot_scan(dev_info_t *dip, ddi_acc_impl_t *hdlp)
60 60 {
61 61 }
62 62
63 63 /*ARGSUSED*/
64 64 void
65 65 pcieb_plat_attach_workaround(dev_info_t *dip)
66 66 {
67 67 }
68 68
69 69 int
70 70 pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
71 71 ddi_intr_handle_impl_t *hdlp, void *result)
72 72 {
73 73 dev_info_t *cdip = rdip;
74 74 pci_regspec_t *pci_rp;
75 75 int reglen, len;
76 76 uint32_t d, intr;
77 77
78 78 if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) ||
79 79 (hdlp->ih_type != DDI_INTR_TYPE_FIXED))
80 80 goto done;
81 81
82 82 /*
83 83 * If the interrupt-map property is defined at this
84 84 * node, it will have performed the interrupt
85 85 * translation as part of the property, so no
86 86 * rotation needs to be done.
87 87 */
88 88 if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
89 89 "interrupt-map", &len) == DDI_PROP_SUCCESS)
90 90 goto done;
91 91
92 92 cdip = pcie_get_my_childs_dip(dip, rdip);
93 93
94 94 /*
95 95 * Use the devices reg property to determine its
96 96 * PCI bus number and device number.
97 97 */
98 98 if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
99 99 "reg", (caddr_t)&pci_rp, ®len) != DDI_SUCCESS)
100 100 return (DDI_FAILURE);
101 101
102 102 intr = hdlp->ih_vector;
103 103
104 104 d = (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED) ? 0 :
105 105 PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
106 106
107 107 /* spin the interrupt */
108 108 if ((intr >= PCI_INTA) && (intr <= PCI_INTD))
109 109 hdlp->ih_vector = ((intr - 1 + (d % 4)) % 4 + 1);
110 110 else
111 111 cmn_err(CE_WARN, "%s%d: %s: PCI intr=%x out of range",
112 112 ddi_driver_name(rdip), ddi_get_instance(rdip),
113 113 ddi_driver_name(dip), intr);
114 114
115 115 kmem_free(pci_rp, reglen);
116 116
117 117 done:
118 118 /* Pass up the request to our parent. */
119 119 return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
120 120 }
121 121
122 122 int
123 123 pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
124 124 {
125 125 uint16_t cap_ptr;
126 126 if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr)) !=
127 127 DDI_FAILURE) {
128 128 return (DDI_SUCCESS);
129 129 }
130 130
131 131 return (DDI_FAILURE);
132 132 }
133 133
134 134 /*
135 135 * Disable PM on PLX. For PLX Transitioning one port on this switch to
136 136 * low power causes links on other ports on the same station to die.
137 137 * Due to PLX erratum #34, we can't allow the downstream device go to
138 138 * non-D0 state.
139 139 */
140 140 boolean_t
141 141 pcieb_plat_pwr_disable(dev_info_t *dip)
142 142 {
143 143 uint16_t vendor_id = (PCIE_DIP2UPBUS(dip)->bus_dev_ven_id) & 0xFFFF;
144 144 return (IS_PLX_VENDORID(vendor_id) ? B_TRUE : B_FALSE);
145 145 }
146 146
147 147 /*ARGSUSED*/
148 148 boolean_t
149 149 pcieb_plat_msi_supported(dev_info_t *dip)
150 150 {
151 151 return (B_TRUE);
152 152 }
153 153
154 154 /*ARGSUSED*/
155 155 void
156 156 pcieb_plat_intr_attach(pcieb_devstate_t *pcieb)
157 157 {
158 158 }
159 159
160 160 /*ARGSUSED*/
161 161 int
162 162 pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg)
163 163 {
164 164 return (DDI_SUCCESS);
165 165 }
166 166
167 167 void
168 168 pcieb_plat_initchild(dev_info_t *child)
169 169 {
170 170 intptr_t ppd = NULL;
171 171 /*
172 172 * XXX set ppd to 1 to disable iommu BDF protection on SPARC.
173 173 * It relies on unused parent private data for PCI devices.
174 174 */
175 175 if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
176 176 "dvma-share"))
177 177 ppd = 1;
178 178
179 179 ddi_set_parent_data(child, (void *)ppd);
180 180 }
181 181
182 182 void
183 183 pcieb_plat_uninitchild(dev_info_t *child)
184 184 {
185 185 /*
186 186 * XXX Clear parent private data used as a flag to disable
187 187 * iommu BDF protection
188 188 */
189 189 if ((intptr_t)ddi_get_parent_data(child) == 1)
190 190 ddi_set_parent_data(child, NULL);
191 191 }
192 192
193 193 #ifdef PX_PLX
194 194 /*
195 195 * These are PLX specific workarounds needed during attach.
196 196 */
197 197 void
198 198 pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb)
199 199 {
200 200 dev_info_t *dip = pcieb->pcieb_dip;
201 201 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
202 202 ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl;
203 203 uint_t bus_num, primary, secondary;
204 204 uint8_t dev_type = bus_p->bus_dev_type;
205 205 uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF;
206 206 uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
207 207 int ce_mask = 0;
208 208
209 209 if (!IS_PLX_VENDORID(vendor_id))
210 210 return;
211 211
212 212 if ((device_id == PXB_DEVICE_PLX_8532) &&
213 213 (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
214 214 /* Clear hotplug capability */
215 215 bus_p->bus_hp_sup_modes = PCIE_NONE_HP_MODE;
216 216
217 217 /*
218 218 * Due to a PLX HW bug we need to disable the receiver error CE on all
219 219 * ports. To this end we create a property "pcie_ce_mask" with value
220 220 * set to PCIE_AER_CE_RECEIVER_ERR. The pcie module will check for this
221 221 * property before setting the AER CE mask. Be sure to honor all other
222 222 * pcie_ce_mask settings.
223 223 */
224 224 ce_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
225 225 "pcie_ce_mask", 0);
226 226 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
227 227 "pcie_ce_mask", (PCIE_AER_CE_RECEIVER_ERR|ce_mask));
228 228
229 229 /*
230 230 * There is a bug in the PLX 8114 bridge, such that an 8-bit
231 231 * write to the secondary bus number register will corrupt an
232 232 * internal shadow copy of the primary bus number. Reading
233 233 * out the registers and writing the same values back as
234 234 * 16-bits resolves the problem. This bug was reported by
235 235 * PLX as errata #19.
236 236 */
237 237 primary = pci_config_get8(config_handle, PCI_BCNF_PRIBUS);
238 238 secondary = pci_config_get8(config_handle, PCI_BCNF_SECBUS);
239 239 bus_num = (secondary << 8) | primary;
240 240 pci_config_put16(config_handle, PCI_BCNF_PRIBUS, bus_num);
241 241
242 242 /*
243 243 * Workaround for a race condition between hotplug
244 244 * initialization and actual MSI interrupt registration
245 245 * for hotplug functionality. The hotplug initialization
246 246 * generates an INTx interrupt for hotplug events and this
247 247 * INTx interrupt may interfere with shared leaf drivers
248 248 * using same INTx interrupt, which may eventually block
249 249 * the leaf drivers.
250 250 */
251 251 if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
252 252 (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
253 253 (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ||
254 254 (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) {
255 255 pci_config_put16(config_handle, PCI_CONF_COMM,
256 256 pci_config_get16(config_handle, PCI_CONF_COMM) |
257 257 PCI_COMM_INTX_DISABLE);
258 258 }
259 259
260 260 /*
261 261 * Disable PLX Special Relaxed Ordering
262 262 */
263 263 plx_ro_disable(pcieb);
264 264
265 265 #ifdef PRINT_PLX_SEEPROM_CRC
266 266 /* check seeprom CRC to ensure the platform config is right */
267 267 (void) pcieb_print_plx_seeprom_crc_data(pcieb);
268 268 #endif /* PRINT_PLX_SEEPROM_CRC */
269 269 }
270 270
271 271 /*
272 272 * These are PLX specific workarounds called during child's initchild.
273 273 */
274 274 int
275 275 pcieb_init_plx_workarounds(pcieb_devstate_t *pcieb, dev_info_t *child)
276 276 {
277 277 int i;
278 278 int result = DDI_FAILURE;
279 279 uint16_t reg = 0;
280 280 ddi_acc_handle_t config_handle;
281 281 uint16_t vendor_id =
282 282 (PCIE_DIP2UPBUS(pcieb->pcieb_dip))->bus_dev_ven_id & 0xFFFF;
283 283
284 284 if (!IS_PLX_VENDORID(vendor_id))
285 285 return (DDI_SUCCESS);
286 286
287 287 /*
288 288 * Due to a PLX HW bug, a SW workaround to prevent the chip from
289 289 * wedging is needed. SW just needs to tranfer 64 TLPs from
290 290 * the downstream port to the child device.
291 291 * The most benign way of doing this is to read the ID register
292 292 * 64 times. This SW workaround should have minimum performance
293 293 * impact and shouldn't cause a problem for all other bridges
294 294 * and switches.
295 295 *
296 296 * The code needs to be written in a way to make sure it isn't
297 297 * optimized out.
298 298 */
299 299 if (!pxb_tlp_count) {
300 300 result = DDI_SUCCESS;
301 301 goto done;
302 302 }
303 303
304 304 if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
305 305 result = DDI_FAILURE;
306 306 goto done;
307 307 }
308 308
309 309 for (i = 0; i < pxb_tlp_count; i += 1)
310 310 reg |= pci_config_get16(config_handle, PCI_CONF_VENID);
311 311
312 312 if (PCIE_IS_PCIE_BDG(PCIE_DIP2BUS(pcieb->pcieb_dip)))
313 313 pcieb_set_pci_perf_parameters(child, config_handle);
314 314
315 315 pci_config_teardown(&config_handle);
316 316 result = DDI_SUCCESS;
317 317 done:
318 318 return (result);
319 319 }
320 320
321 321 /*
322 322 * Disable PLX specific relaxed ordering mode. Due to PLX
323 323 * erratum #6, use of this mode with Cut-Through Cancellation
324 324 * can result in dropped Completion type packets.
325 325 *
326 326 * Clear the Relaxed Ordering Mode on 8533 and 8548 switches.
327 327 * To disable RO, clear bit 5 in offset 0x664, an undocumented
328 328 * bit in the PLX spec, on Ports 0, 8 and 12. Proprietary PLX
329 329 * registers are normally accessible only via memspace from Port
330 330 * 0. If port 0 is attached go ahead and disable RO on Port 0,
331 331 * 8 and 12, if they exist.
332 332 */
333 333 static void
334 334 plx_ro_disable(pcieb_devstate_t *pcieb)
335 335 {
336 336 pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
337 337 dev_info_t *dip = pcieb->pcieb_dip;
338 338 uint16_t device_id = bus_p->bus_dev_ven_id >> 16;
339 339 pci_regspec_t *reg_spec, *addr_spec;
340 340 int rlen, alen;
341 341 int orig_rsize, new_rsize;
342 342 uint_t rnum, anum;
343 343 ddi_device_acc_attr_t attr;
344 344 ddi_acc_handle_t hdl;
345 345 caddr_t regsp;
346 346 uint32_t val, port_enable;
347 347 char *offset;
348 348 char *port_offset;
349 349
350 350 if (!((device_id == PXB_DEVICE_PLX_8533) ||
351 351 (device_id == PXB_DEVICE_PLX_8548)))
352 352 return;
353 353
354 354 /* You can also only do this on Port 0 */
355 355 val = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
356 356 val = (val >> PCIE_LINKCAP_PORT_NUMBER_SHIFT) &
357 357 PCIE_LINKCAP_PORT_NUMBER_MASK;
358 358
359 359 PCIEB_DEBUG(DBG_ATTACH, dip, "PLX RO Disable : bdf=0x%x port=%d\n",
360 360 bus_p->bus_bdf, val);
361 361
362 362 if (val != 0)
363 363 return;
364 364
365 365 /*
366 366 * Read the reg property, but allocate extra space incase we need to add
367 367 * a new entry later.
368 368 */
369 369 if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
370 370 &orig_rsize) != DDI_SUCCESS)
371 371 return;
372 372
373 373 new_rsize = orig_rsize + sizeof (pci_regspec_t);
374 374 reg_spec = kmem_alloc(new_rsize, KM_SLEEP);
375 375
376 376 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
377 377 (caddr_t)reg_spec, &orig_rsize) != DDI_SUCCESS)
378 378 goto fail;
379 379
380 380 /* Find the mem32 reg property */
381 381 rlen = orig_rsize / sizeof (pci_regspec_t);
382 382 for (rnum = 0; rnum < rlen; rnum++) {
383 383 if ((reg_spec[rnum].pci_phys_hi & PCI_ADDR_MASK) ==
384 384 PCI_ADDR_MEM32)
385 385 goto fix;
386 386 }
387 387
388 388 /*
389 389 * Mem32 reg property was not found.
390 390 * Look for it in assign-address property.
391 391 */
392 392 addr_spec = bus_p->bus_assigned_addr;
393 393 alen = bus_p->bus_assigned_entries;
394 394 for (anum = 0; anum < alen; anum++) {
395 395 if ((addr_spec[anum].pci_phys_hi & PCI_ADDR_MASK) ==
396 396 PCI_ADDR_MEM32)
397 397 goto update;
398 398 }
399 399
400 400 /* Unable to find mem space assigned address, give up. */
401 401 goto fail;
402 402
403 403 update:
404 404 /*
405 405 * Add the mem32 access to the reg spec.
406 406 * Use the last entry which was previously allocated.
407 407 */
408 408 reg_spec[rnum].pci_phys_hi = (addr_spec[anum].pci_phys_hi &
409 409 ~PCI_REG_REL_M);
410 410 reg_spec[rnum].pci_phys_mid = 0;
411 411 reg_spec[rnum].pci_phys_low = 0;
412 412 reg_spec[rnum].pci_size_hi = addr_spec[anum].pci_size_hi;
413 413 reg_spec[rnum].pci_size_low = addr_spec[anum].pci_size_low;
414 414
415 415 /* Create the new reg_spec data and update the property */
416 416 if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
417 417 (int *)reg_spec, (new_rsize / sizeof (int))) != DDI_SUCCESS)
418 418 goto fail;
419 419
420 420 fix:
421 421 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
422 422 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
423 423 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
424 424
425 425 if (ddi_regs_map_setup(dip, rnum, ®sp, 0, 0, &attr,
426 426 &hdl) != DDI_SUCCESS)
427 427 goto fail;
428 428
429 429 /* Grab register which shows which ports are enabled */
430 430 offset = (char *)regsp + PLX_INGRESS_PORT_ENABLE;
431 431 port_enable = ddi_get32(hdl, (uint32_t *)offset);
432 432
433 433 if ((port_enable == 0xFFFFFFFF) || (port_enable == 0))
434 434 goto done;
435 435
436 436 offset = (char *)regsp + PLX_INGRESS_CONTROL_SHADOW;
437 437
438 438 /* Disable RO on Port 0 */
439 439 port_offset = 0x0 + offset;
440 440 val = ddi_get32(hdl, (uint32_t *)port_offset);
441 441 if (val & PLX_RO_MODE_BIT)
442 442 val ^= PLX_RO_MODE_BIT;
443 443 ddi_put32(hdl, (uint32_t *)port_offset, val);
444 444
445 445 /* Disable RO on Port 8, but make sure its enabled */
446 446 if (!(port_enable & (1 << 8)))
447 447 goto port12;
448 448
449 449 port_offset = (8 * 0x1000) + offset;
450 450 val = ddi_get32(hdl, (uint32_t *)port_offset);
451 451 if (val & PLX_RO_MODE_BIT)
452 452 val ^= PLX_RO_MODE_BIT;
453 453 ddi_put32(hdl, (uint32_t *)port_offset, val);
454 454
455 455 port12:
456 456 /* Disable RO on Port 12, but make sure it exists */
457 457 if (!(port_enable & (1 << 12)))
458 458 goto done;
459 459
460 460 port_offset = (12 * 0x1000) + offset;
461 461 val = ddi_get32(hdl, (uint32_t *)port_offset);
462 462 if (val & PLX_RO_MODE_BIT)
463 463 val ^= PLX_RO_MODE_BIT;
464 464 ddi_put32(hdl, (uint32_t *)port_offset, val);
465 465
466 466 goto done;
467 467
468 468 done:
469 469 ddi_regs_map_free(&hdl);
470 470 fail:
471 471 kmem_free(reg_spec, new_rsize);
472 472 }
473 473
474 474 #ifdef PRINT_PLX_SEEPROM_CRC
475 475 static void
476 476 pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p)
477 477 {
478 478 ddi_acc_handle_t h;
479 479 dev_info_t *dip = pcieb_p->pcieb_dip;
480 480 uint16_t vendorid = (PCIE_DIP2BUS(dip)->bus_dev_ven_id) & 0xFFFF;
481 481 int nregs;
482 482 caddr_t mp;
483 483 off_t bar_size;
484 484 ddi_device_acc_attr_t mattr = {
485 485 DDI_DEVICE_ATTR_V0,
486 486 DDI_STRUCTURE_LE_ACC,
487 487 DDI_STRICTORDER_ACC
488 488 };
489 489 uint32_t addr_reg_off = 0x260, data_reg_off = 0x264, data = 0x6BE4;
490 490
491 491 if (vendorid != PXB_VENDOR_PLX)
492 492 return;
↓ open down ↓ |
492 lines elided |
↑ open up ↑ |
493 493 if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS)
494 494 return;
495 495 if (nregs < 2) /* check for CONF entry only, no BARs */
496 496 return;
497 497 if (ddi_dev_regsize(dip, 1, &bar_size) != DDI_SUCCESS)
498 498 return;
499 499 if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mp, 0, bar_size,
500 500 &mattr, &h) != DDI_SUCCESS)
501 501 return;
502 502 ddi_put32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off), data);
503 - delay(drv_usectohz(1000000));
503 + delay(drv_sectohz(1));
504 504 printf("%s#%d: EEPROM StatusReg = %x, CRC = %x\n",
505 505 ddi_driver_name(dip), ddi_get_instance(dip),
506 506 ddi_get32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off)),
507 507 ddi_get32(h, (uint32_t *)((uchar_t *)mp + data_reg_off)));
508 508 #ifdef PLX_HOT_RESET_DISABLE
509 509 /* prevent hot reset from propogating downstream. */
510 510 data = ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC));
511 511 ddi_put32(h, (uint32_t *)((uchar_t *)mp + 0x1DC), data | 0x80000);
512 - delay(drv_usectohz(1000000));
512 + delay(drv_sectohz(1));
513 513 printf("%s#%d: EEPROM 0x1DC prewrite=%x postwrite=%x\n",
514 514 ddi_driver_name(dip), ddi_get_instance(dip), data,
515 515 ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC)));
516 516 #endif /* PLX_HOT_RESET_DISABLE */
517 517 ddi_regs_map_free(&h);
518 518 }
519 519 #endif /* PRINT_PLX_SEEPROM_CRC */
520 520 #endif /* PX_PLX */
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX