1 // SPDX-License-Identifier: GPL-2.0
3 * PCIe Enclosure management driver created for LED interfaces based on
4 * indications. It says *what indications* blink but does not specify *how*
5 * they blink - it is hardware defined.
7 * The driver name refers to Native PCIe Enclosure Management. It is
8 * first indication oriented standard with specification.
10 * Native PCIe Enclosure Management (NPEM)
11 * PCIe Base Specification r6.1 sec 6.28, 7.9.19
13 * _DSM Definitions for PCIe SSD Status LED
14 * PCI Firmware Specification, r3.3 sec 4.7
16 * Two backends are supported to manipulate indications: Direct NPEM register
17 * access (npem_ops) and indirect access through the ACPI _DSM (dsm_ops).
18 * _DSM is used if supported, else NPEM.
20 * Copyright (c) 2021-2022 Dell Inc.
21 * Copyright (c) 2023-2024 Intel Corporation
22 * Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
25 #include <linux/acpi.h>
26 #include <linux/bitops.h>
27 #include <linux/errno.h>
28 #include <linux/iopoll.h>
29 #include <linux/leds.h>
30 #include <linux/mutex.h>
31 #include <linux/pci.h>
32 #include <linux/pci_regs.h>
33 #include <linux/types.h>
34 #include <linux/uleds.h>
43 static const struct indication npem_indications
[] = {
44 {PCI_NPEM_IND_OK
, "enclosure:ok"},
45 {PCI_NPEM_IND_LOCATE
, "enclosure:locate"},
46 {PCI_NPEM_IND_FAIL
, "enclosure:fail"},
47 {PCI_NPEM_IND_REBUILD
, "enclosure:rebuild"},
48 {PCI_NPEM_IND_PFA
, "enclosure:pfa"},
49 {PCI_NPEM_IND_HOTSPARE
, "enclosure:hotspare"},
50 {PCI_NPEM_IND_ICA
, "enclosure:ica"},
51 {PCI_NPEM_IND_IFA
, "enclosure:ifa"},
52 {PCI_NPEM_IND_IDT
, "enclosure:idt"},
53 {PCI_NPEM_IND_DISABLED
, "enclosure:disabled"},
54 {PCI_NPEM_IND_SPEC_0
, "enclosure:specific_0"},
55 {PCI_NPEM_IND_SPEC_1
, "enclosure:specific_1"},
56 {PCI_NPEM_IND_SPEC_2
, "enclosure:specific_2"},
57 {PCI_NPEM_IND_SPEC_3
, "enclosure:specific_3"},
58 {PCI_NPEM_IND_SPEC_4
, "enclosure:specific_4"},
59 {PCI_NPEM_IND_SPEC_5
, "enclosure:specific_5"},
60 {PCI_NPEM_IND_SPEC_6
, "enclosure:specific_6"},
61 {PCI_NPEM_IND_SPEC_7
, "enclosure:specific_7"},
65 /* _DSM PCIe SSD LED States correspond to NPEM register values */
66 static const struct indication dsm_indications
[] = {
67 {PCI_NPEM_IND_OK
, "enclosure:ok"},
68 {PCI_NPEM_IND_LOCATE
, "enclosure:locate"},
69 {PCI_NPEM_IND_FAIL
, "enclosure:fail"},
70 {PCI_NPEM_IND_REBUILD
, "enclosure:rebuild"},
71 {PCI_NPEM_IND_PFA
, "enclosure:pfa"},
72 {PCI_NPEM_IND_HOTSPARE
, "enclosure:hotspare"},
73 {PCI_NPEM_IND_ICA
, "enclosure:ica"},
74 {PCI_NPEM_IND_IFA
, "enclosure:ifa"},
75 {PCI_NPEM_IND_IDT
, "enclosure:idt"},
76 {PCI_NPEM_IND_DISABLED
, "enclosure:disabled"},
80 #define for_each_indication(ind, inds) \
81 for (ind = inds; ind->bit; ind++)
84 * The driver has internal list of supported indications. Ideally, the driver
85 * should not touch bits that are not defined and for which LED devices are
86 * not exposed but in reality, it needs to turn them off.
88 * Otherwise, there will be no possibility to turn off indications turned on by
89 * other utilities or turned on by default and it leads to bad user experience.
91 * Additionally, it excludes NPEM commands like RESET or ENABLE.
93 static u32
reg_to_indications(u32 caps
, const struct indication
*inds
)
95 const struct indication
*ind
;
96 u32 supported_indications
= 0;
98 for_each_indication(ind
, inds
)
99 supported_indications
|= ind
->bit
;
101 return caps
& supported_indications
;
105 * struct npem_led - LED details
106 * @indication: indication details
112 const struct indication
*indication
;
114 char name
[LED_MAX_NAME_SIZE
];
115 struct led_classdev led
;
119 * struct npem_ops - backend specific callbacks
120 * @get_active_indications: get active indications
122 * inds: response buffer
123 * @set_active_indications: set new indications
125 * inds: bit mask to set
126 * @inds: supported indications array, set of indications is backend specific
127 * @name: backend name
130 int (*get_active_indications
)(struct npem
*npem
, u32
*inds
);
131 int (*set_active_indications
)(struct npem
*npem
, u32 inds
);
132 const struct indication
*inds
;
137 * struct npem - NPEM device properties
138 * @dev: PCI device this driver is attached to
139 * @ops: backend specific callbacks
140 * @lock: serializes concurrent access to NPEM device by multiple LED devices
141 * @pos: cached offset of NPEM Capability Register in Configuration Space;
142 * only used if NPEM registers are accessed directly and not through _DSM
143 * @supported_indications: cached bit mask of supported indications;
144 * non-indication and reserved bits in the NPEM Capability Register are
145 * cleared in this bit mask
146 * @active_indications: cached bit mask of active indications;
147 * non-indication and reserved bits in the NPEM Control Register are
148 * cleared in this bit mask
149 * @active_inds_initialized: whether @active_indications has been initialized;
150 * On Dell platforms, it is required that IPMI drivers are loaded before
151 * the GET_STATE_DSM method is invoked: They use an IPMI OpRegion to
152 * get/set the active LEDs. By initializing @active_indications lazily
153 * (on first access to an LED), IPMI drivers are given a chance to load.
154 * If they are not loaded in time, users will see various errors on LED
155 * access in dmesg. Once they are loaded, the errors go away and LED
156 * access becomes possible.
157 * @led_cnt: size of @leds array
158 * @leds: array containing LED class devices of all supported LEDs
162 const struct npem_ops
*ops
;
165 u32 supported_indications
;
166 u32 active_indications
;
167 unsigned int active_inds_initialized
:1;
169 struct npem_led leds
[];
172 static int npem_read_reg(struct npem
*npem
, u16 reg
, u32
*val
)
174 int ret
= pci_read_config_dword(npem
->dev
, npem
->pos
+ reg
, val
);
176 return pcibios_err_to_errno(ret
);
179 static int npem_write_ctrl(struct npem
*npem
, u32 reg
)
181 int pos
= npem
->pos
+ PCI_NPEM_CTRL
;
182 int ret
= pci_write_config_dword(npem
->dev
, pos
, reg
);
184 return pcibios_err_to_errno(ret
);
187 static int npem_get_active_indications(struct npem
*npem
, u32
*inds
)
192 ret
= npem_read_reg(npem
, PCI_NPEM_CTRL
, &ctrl
);
196 /* If PCI_NPEM_CTRL_ENABLE is not set then no indication should blink */
197 if (!(ctrl
& PCI_NPEM_CTRL_ENABLE
)) {
202 *inds
= ctrl
& npem
->supported_indications
;
207 static int npem_set_active_indications(struct npem
*npem
, u32 inds
)
209 int ctrl
, ret
, ret_val
;
212 lockdep_assert_held(&npem
->lock
);
214 /* This bit is always required */
215 ctrl
= inds
| PCI_NPEM_CTRL_ENABLE
;
217 ret
= npem_write_ctrl(npem
, ctrl
);
222 * For the case where a NPEM command has not completed immediately,
223 * it is recommended that software not continuously "spin" on polling
224 * the status register, but rather poll under interrupt at a reduced
225 * rate; for example at 10 ms intervals.
227 * PCIe r6.1 sec 6.28 "Implementation Note: Software Polling of NPEM
230 ret
= read_poll_timeout(npem_read_reg
, ret_val
,
231 ret_val
|| (cc_status
& PCI_NPEM_STATUS_CC
),
232 10 * USEC_PER_MSEC
, USEC_PER_SEC
, false, npem
,
233 PCI_NPEM_STATUS
, &cc_status
);
240 * All writes to control register, including writes that do not change
241 * the register value, are NPEM commands and should eventually result
242 * in a command completion indication in the NPEM Status Register.
244 * PCIe Base Specification r6.1 sec 7.9.19.3
246 * Register may not be updated, or other conflicting bits may be
247 * cleared. Spec is not strict here. Read NPEM Control register after
248 * write to keep cache in-sync.
250 return npem_get_active_indications(npem
, &npem
->active_indications
);
253 static const struct npem_ops npem_ops
= {
254 .get_active_indications
= npem_get_active_indications
,
255 .set_active_indications
= npem_set_active_indications
,
256 .name
= "Native PCIe Enclosure Management",
257 .inds
= npem_indications
,
260 #define DSM_GUID GUID_INIT(0x5d524d9d, 0xfff9, 0x4d4b, 0x8c, 0xb7, 0x74, 0x7e,\
261 0xd5, 0x1e, 0x19, 0x4d)
262 #define GET_SUPPORTED_STATES_DSM 1
263 #define GET_STATE_DSM 2
264 #define SET_STATE_DSM 3
266 static const guid_t dsm_guid
= DSM_GUID
;
268 static bool npem_has_dsm(struct pci_dev
*pdev
)
272 handle
= ACPI_HANDLE(&pdev
->dev
);
276 return acpi_check_dsm(handle
, &dsm_guid
, 0x1,
277 BIT(GET_SUPPORTED_STATES_DSM
) |
278 BIT(GET_STATE_DSM
) | BIT(SET_STATE_DSM
));
283 u8 function_specific_err
;
284 u8 vendor_specific_err
;
289 * dsm_evaluate() - send DSM PCIe SSD Status LED command
291 * @dsm_func: DSM LED Function
292 * @output: buffer to copy DSM Response
293 * @value_to_set: value for SET_STATE_DSM function
295 * To not bother caller with ACPI context, the returned _DSM Output Buffer is
298 static int dsm_evaluate(struct pci_dev
*pdev
, u64 dsm_func
,
299 struct dsm_output
*output
, u32 value_to_set
)
301 acpi_handle handle
= ACPI_HANDLE(&pdev
->dev
);
302 union acpi_object
*out_obj
, arg3
[2];
303 union acpi_object
*arg3_p
= NULL
;
305 if (dsm_func
== SET_STATE_DSM
) {
306 arg3
[0].type
= ACPI_TYPE_PACKAGE
;
307 arg3
[0].package
.count
= 1;
308 arg3
[0].package
.elements
= &arg3
[1];
310 arg3
[1].type
= ACPI_TYPE_BUFFER
;
311 arg3
[1].buffer
.length
= 4;
312 arg3
[1].buffer
.pointer
= (u8
*)&value_to_set
;
317 out_obj
= acpi_evaluate_dsm_typed(handle
, &dsm_guid
, 0x1, dsm_func
,
318 arg3_p
, ACPI_TYPE_BUFFER
);
322 if (out_obj
->buffer
.length
< sizeof(struct dsm_output
)) {
327 memcpy(output
, out_obj
->buffer
.pointer
, sizeof(struct dsm_output
));
333 static int dsm_get(struct pci_dev
*pdev
, u64 dsm_func
, u32
*buf
)
335 struct dsm_output output
;
336 int ret
= dsm_evaluate(pdev
, dsm_func
, &output
, 0);
341 if (output
.status
!= 0)
348 static int dsm_get_active_indications(struct npem
*npem
, u32
*buf
)
350 int ret
= dsm_get(npem
->dev
, GET_STATE_DSM
, buf
);
352 /* Filter out not supported indications in response */
353 *buf
&= npem
->supported_indications
;
357 static int dsm_set_active_indications(struct npem
*npem
, u32 value
)
359 struct dsm_output output
;
360 int ret
= dsm_evaluate(npem
->dev
, SET_STATE_DSM
, &output
, value
);
365 switch (output
.status
) {
368 * Not all bits are set. If this bit is set, the platform
369 * disregarded some or all of the request state changes. OSPM
370 * should check the resulting PCIe SSD Status LED States to see
371 * what, if anything, has changed.
373 * PCI Firmware Specification, r3.3 Table 4-19.
375 if (output
.function_specific_err
!= 1)
384 npem
->active_indications
= output
.state
;
389 static const struct npem_ops dsm_ops
= {
390 .get_active_indications
= dsm_get_active_indications
,
391 .set_active_indications
= dsm_set_active_indications
,
392 .name
= "_DSM PCIe SSD Status LED Management",
393 .inds
= dsm_indications
,
396 static int npem_initialize_active_indications(struct npem
*npem
)
400 lockdep_assert_held(&npem
->lock
);
402 if (npem
->active_inds_initialized
)
405 ret
= npem
->ops
->get_active_indications(npem
,
406 &npem
->active_indications
);
410 npem
->active_inds_initialized
= true;
415 * The status of each indicator is cached on first brightness_ get/set time
416 * and updated at write time. brightness_get() is only responsible for
417 * reflecting the last written/cached value.
419 static enum led_brightness
brightness_get(struct led_classdev
*led
)
421 struct npem_led
*nled
= container_of(led
, struct npem_led
, led
);
422 struct npem
*npem
= nled
->npem
;
425 ret
= mutex_lock_interruptible(&npem
->lock
);
429 ret
= npem_initialize_active_indications(npem
);
433 if (npem
->active_indications
& nled
->indication
->bit
)
437 mutex_unlock(&npem
->lock
);
441 static int brightness_set(struct led_classdev
*led
,
442 enum led_brightness brightness
)
444 struct npem_led
*nled
= container_of(led
, struct npem_led
, led
);
445 struct npem
*npem
= nled
->npem
;
449 ret
= mutex_lock_interruptible(&npem
->lock
);
453 ret
= npem_initialize_active_indications(npem
);
458 indications
= npem
->active_indications
& ~(nled
->indication
->bit
);
460 indications
= npem
->active_indications
| nled
->indication
->bit
;
462 ret
= npem
->ops
->set_active_indications(npem
, indications
);
465 mutex_unlock(&npem
->lock
);
469 static void npem_free(struct npem
*npem
)
471 struct npem_led
*nled
;
477 for (cnt
= 0; cnt
< npem
->led_cnt
; cnt
++) {
478 nled
= &npem
->leds
[cnt
];
481 led_classdev_unregister(&nled
->led
);
484 mutex_destroy(&npem
->lock
);
488 static int pci_npem_set_led_classdev(struct npem
*npem
, struct npem_led
*nled
)
490 struct led_classdev
*led
= &nled
->led
;
491 struct led_init_data init_data
= {};
492 char *name
= nled
->name
;
495 init_data
.devicename
= pci_name(npem
->dev
);
496 init_data
.default_label
= nled
->indication
->name
;
498 ret
= led_compose_name(&npem
->dev
->dev
, &init_data
, name
);
503 led
->brightness_set_blocking
= brightness_set
;
504 led
->brightness_get
= brightness_get
;
505 led
->max_brightness
= 1;
506 led
->default_trigger
= "none";
509 ret
= led_classdev_register(&npem
->dev
->dev
, led
);
511 /* Clear the name to indicate that it is not registered. */
516 static int pci_npem_init(struct pci_dev
*dev
, const struct npem_ops
*ops
,
519 u32 supported
= reg_to_indications(caps
, ops
->inds
);
520 int supported_cnt
= hweight32(supported
);
521 const struct indication
*indication
;
522 struct npem_led
*nled
;
527 npem
= kzalloc(struct_size(npem
, leds
, supported_cnt
), GFP_KERNEL
);
531 npem
->supported_indications
= supported
;
532 npem
->led_cnt
= supported_cnt
;
537 mutex_init(&npem
->lock
);
539 for_each_indication(indication
, npem_indications
) {
540 if (!(npem
->supported_indications
& indication
->bit
))
543 nled
= &npem
->leds
[led_idx
++];
544 nled
->indication
= indication
;
547 ret
= pci_npem_set_led_classdev(npem
, nled
);
558 void pci_npem_remove(struct pci_dev
*dev
)
560 npem_free(dev
->npem
);
563 void pci_npem_create(struct pci_dev
*dev
)
565 const struct npem_ops
*ops
= &npem_ops
;
569 if (npem_has_dsm(dev
)) {
571 * OS should use the DSM for LED control if it is available
572 * PCI Firmware Spec r3.3 sec 4.7.
574 ret
= dsm_get(dev
, GET_SUPPORTED_STATES_DSM
, &cap
);
580 pos
= pci_find_ext_capability(dev
, PCI_EXT_CAP_ID_NPEM
);
584 if (pci_read_config_dword(dev
, pos
+ PCI_NPEM_CAP
, &cap
) != 0 ||
585 (cap
& PCI_NPEM_CAP_CAPABLE
) == 0)
589 pci_info(dev
, "Configuring %s\n", ops
->name
);
591 ret
= pci_npem_init(dev
, ops
, pos
, cap
);
593 pci_err(dev
, "Failed to register %s, err: %d\n", ops
->name
,