Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  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.
  6 *
  7 * The driver name refers to Native PCIe Enclosure Management. It is
  8 * first indication oriented standard with specification.
  9 *
 10 * Native PCIe Enclosure Management (NPEM)
 11 *	PCIe Base Specification r6.1 sec 6.28, 7.9.19
 12 *
 13 * _DSM Definitions for PCIe SSD Status LED
 14 *	 PCI Firmware Specification, r3.3 sec 4.7
 15 *
 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.
 19 *
 20 * Copyright (c) 2021-2022 Dell Inc.
 21 * Copyright (c) 2023-2024 Intel Corporation
 22 *	Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
 23 */
 24
 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>
 35
 36#include "pci.h"
 37
 38struct indication {
 39	u32 bit;
 40	const char *name;
 41};
 42
 43static 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"},
 62	{0,			NULL}
 63};
 64
 65/* _DSM PCIe SSD LED States correspond to NPEM register values */
 66static 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"},
 77	{0,			NULL}
 78};
 79
 80#define for_each_indication(ind, inds) \
 81	for (ind = inds; ind->bit; ind++)
 82
 83/*
 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.
 87 *
 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.
 90 *
 91 * Additionally, it excludes NPEM commands like RESET or ENABLE.
 92 */
 93static u32 reg_to_indications(u32 caps, const struct indication *inds)
 94{
 95	const struct indication *ind;
 96	u32 supported_indications = 0;
 97
 98	for_each_indication(ind, inds)
 99		supported_indications |= ind->bit;
100
101	return caps & supported_indications;
102}
103
104/**
105 * struct npem_led - LED details
106 * @indication: indication details
107 * @npem: NPEM device
108 * @name: LED name
109 * @led: LED device
110 */
111struct npem_led {
112	const struct indication *indication;
113	struct npem *npem;
114	char name[LED_MAX_NAME_SIZE];
115	struct led_classdev led;
116};
117
118/**
119 * struct npem_ops - backend specific callbacks
120 * @get_active_indications: get active indications
121 *	npem: NPEM device
122 *	inds: response buffer
123 * @set_active_indications: set new indications
124 *	npem: npem device
125 *	inds: bit mask to set
126 * @inds: supported indications array, set of indications is backend specific
127 * @name: backend name
128 */
129struct npem_ops {
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;
133	const char *name;
134};
135
136/**
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
159 */
160struct npem {
161	struct pci_dev *dev;
162	const struct npem_ops *ops;
163	struct mutex lock;
164	u16 pos;
165	u32 supported_indications;
166	u32 active_indications;
167	unsigned int active_inds_initialized:1;
168	int led_cnt;
169	struct npem_led leds[];
170};
171
172static int npem_read_reg(struct npem *npem, u16 reg, u32 *val)
173{
174	int ret = pci_read_config_dword(npem->dev, npem->pos + reg, val);
175
176	return pcibios_err_to_errno(ret);
177}
178
179static int npem_write_ctrl(struct npem *npem, u32 reg)
180{
181	int pos = npem->pos + PCI_NPEM_CTRL;
182	int ret = pci_write_config_dword(npem->dev, pos, reg);
183
184	return pcibios_err_to_errno(ret);
185}
186
187static int npem_get_active_indications(struct npem *npem, u32 *inds)
188{
189	u32 ctrl;
190	int ret;
191
192	ret = npem_read_reg(npem, PCI_NPEM_CTRL, &ctrl);
193	if (ret)
194		return ret;
195
196	/* If PCI_NPEM_CTRL_ENABLE is not set then no indication should blink */
197	if (!(ctrl & PCI_NPEM_CTRL_ENABLE)) {
198		*inds = 0;
199		return 0;
200	}
201
202	*inds = ctrl & npem->supported_indications;
203
204	return 0;
205}
206
207static int npem_set_active_indications(struct npem *npem, u32 inds)
208{
209	int ctrl, ret, ret_val;
210	u32 cc_status;
211
212	lockdep_assert_held(&npem->lock);
213
214	/* This bit is always required */
215	ctrl = inds | PCI_NPEM_CTRL_ENABLE;
216
217	ret = npem_write_ctrl(npem, ctrl);
218	if (ret)
219		return ret;
220
221	/*
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.
226	 *
227	 * PCIe r6.1 sec 6.28 "Implementation Note: Software Polling of NPEM
228	 * Command Completed"
229	 */
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);
234	if (ret)
235		return ret;
236	if (ret_val)
237		return ret_val;
238
239	/*
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.
243	 *
244	 * PCIe Base Specification r6.1 sec 7.9.19.3
245	 *
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.
249	 */
250	return npem_get_active_indications(npem, &npem->active_indications);
251}
252
253static 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,
258};
259
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
265
266static const guid_t dsm_guid = DSM_GUID;
267
268static bool npem_has_dsm(struct pci_dev *pdev)
269{
270	acpi_handle handle;
271
272	handle = ACPI_HANDLE(&pdev->dev);
273	if (!handle)
274		return false;
275
276	return acpi_check_dsm(handle, &dsm_guid, 0x1,
277			      BIT(GET_SUPPORTED_STATES_DSM) |
278			      BIT(GET_STATE_DSM) | BIT(SET_STATE_DSM));
279}
280
281struct dsm_output {
282	u16 status;
283	u8 function_specific_err;
284	u8 vendor_specific_err;
285	u32 state;
286};
287
288/**
289 * dsm_evaluate() - send DSM PCIe SSD Status LED command
290 * @pdev: PCI device
291 * @dsm_func: DSM LED Function
292 * @output: buffer to copy DSM Response
293 * @value_to_set: value for SET_STATE_DSM function
294 *
295 * To not bother caller with ACPI context, the returned _DSM Output Buffer is
296 * copied.
297 */
298static int dsm_evaluate(struct pci_dev *pdev, u64 dsm_func,
299			struct dsm_output *output, u32 value_to_set)
300{
301	acpi_handle handle = ACPI_HANDLE(&pdev->dev);
302	union acpi_object *out_obj, arg3[2];
303	union acpi_object *arg3_p = NULL;
304
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];
309
310		arg3[1].type = ACPI_TYPE_BUFFER;
311		arg3[1].buffer.length = 4;
312		arg3[1].buffer.pointer = (u8 *)&value_to_set;
313
314		arg3_p = arg3;
315	}
316
317	out_obj = acpi_evaluate_dsm_typed(handle, &dsm_guid, 0x1, dsm_func,
318					  arg3_p, ACPI_TYPE_BUFFER);
319	if (!out_obj)
320		return -EIO;
321
322	if (out_obj->buffer.length < sizeof(struct dsm_output)) {
323		ACPI_FREE(out_obj);
324		return -EIO;
325	}
326
327	memcpy(output, out_obj->buffer.pointer, sizeof(struct dsm_output));
328
329	ACPI_FREE(out_obj);
330	return 0;
331}
332
333static int dsm_get(struct pci_dev *pdev, u64 dsm_func, u32 *buf)
334{
335	struct dsm_output output;
336	int ret = dsm_evaluate(pdev, dsm_func, &output, 0);
337
338	if (ret)
339		return ret;
340
341	if (output.status != 0)
342		return -EIO;
343
344	*buf = output.state;
345	return 0;
346}
347
348static int dsm_get_active_indications(struct npem *npem, u32 *buf)
349{
350	int ret = dsm_get(npem->dev, GET_STATE_DSM, buf);
351
352	/* Filter out not supported indications in response */
353	*buf &= npem->supported_indications;
354	return ret;
355}
356
357static int dsm_set_active_indications(struct npem *npem, u32 value)
358{
359	struct dsm_output output;
360	int ret = dsm_evaluate(npem->dev, SET_STATE_DSM, &output, value);
361
362	if (ret)
363		return ret;
364
365	switch (output.status) {
366	case 4:
367		/*
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.
372		 *
373		 * PCI Firmware Specification, r3.3 Table 4-19.
374		 */
375		if (output.function_specific_err != 1)
376			return -EIO;
377		fallthrough;
378	case 0:
379		break;
380	default:
381		return -EIO;
382	}
383
384	npem->active_indications = output.state;
385
386	return 0;
387}
388
389static 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,
394};
395
396static int npem_initialize_active_indications(struct npem *npem)
397{
398	int ret;
399
400	lockdep_assert_held(&npem->lock);
401
402	if (npem->active_inds_initialized)
403		return 0;
404
405	ret = npem->ops->get_active_indications(npem,
406						&npem->active_indications);
407	if (ret)
408		return ret;
409
410	npem->active_inds_initialized = true;
411	return 0;
412}
413
414/*
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.
418 */
419static enum led_brightness brightness_get(struct led_classdev *led)
420{
421	struct npem_led *nled = container_of(led, struct npem_led, led);
422	struct npem *npem = nled->npem;
423	int ret, val = 0;
424
425	ret = mutex_lock_interruptible(&npem->lock);
426	if (ret)
427		return ret;
428
429	ret = npem_initialize_active_indications(npem);
430	if (ret)
431		goto out;
432
433	if (npem->active_indications & nled->indication->bit)
434		val = 1;
435
436out:
437	mutex_unlock(&npem->lock);
438	return val;
439}
440
441static int brightness_set(struct led_classdev *led,
442			  enum led_brightness brightness)
443{
444	struct npem_led *nled = container_of(led, struct npem_led, led);
445	struct npem *npem = nled->npem;
446	u32 indications;
447	int ret;
448
449	ret = mutex_lock_interruptible(&npem->lock);
450	if (ret)
451		return ret;
452
453	ret = npem_initialize_active_indications(npem);
454	if (ret)
455		goto out;
456
457	if (brightness == 0)
458		indications = npem->active_indications & ~(nled->indication->bit);
459	else
460		indications = npem->active_indications | nled->indication->bit;
461
462	ret = npem->ops->set_active_indications(npem, indications);
463
464out:
465	mutex_unlock(&npem->lock);
466	return ret;
467}
468
469static void npem_free(struct npem *npem)
470{
471	struct npem_led *nled;
472	int cnt;
473
474	if (!npem)
475		return;
476
477	for (cnt = 0; cnt < npem->led_cnt; cnt++) {
478		nled = &npem->leds[cnt];
479
480		if (nled->name[0])
481			led_classdev_unregister(&nled->led);
482	}
483
484	mutex_destroy(&npem->lock);
485	kfree(npem);
486}
487
488static int pci_npem_set_led_classdev(struct npem *npem, struct npem_led *nled)
489{
490	struct led_classdev *led = &nled->led;
491	struct led_init_data init_data = {};
492	char *name = nled->name;
493	int ret;
494
495	init_data.devicename = pci_name(npem->dev);
496	init_data.default_label = nled->indication->name;
497
498	ret = led_compose_name(&npem->dev->dev, &init_data, name);
499	if (ret)
500		return ret;
501
502	led->name = name;
503	led->brightness_set_blocking = brightness_set;
504	led->brightness_get = brightness_get;
505	led->max_brightness = 1;
506	led->default_trigger = "none";
507	led->flags = 0;
508
509	ret = led_classdev_register(&npem->dev->dev, led);
510	if (ret)
511		/* Clear the name to indicate that it is not registered. */
512		name[0] = 0;
513	return ret;
514}
515
516static int pci_npem_init(struct pci_dev *dev, const struct npem_ops *ops,
517			 int pos, u32 caps)
518{
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;
523	struct npem *npem;
524	int led_idx = 0;
525	int ret;
526
527	npem = kzalloc(struct_size(npem, leds, supported_cnt), GFP_KERNEL);
528	if (!npem)
529		return -ENOMEM;
530
531	npem->supported_indications = supported;
532	npem->led_cnt = supported_cnt;
533	npem->pos = pos;
534	npem->dev = dev;
535	npem->ops = ops;
536
537	mutex_init(&npem->lock);
538
539	for_each_indication(indication, npem_indications) {
540		if (!(npem->supported_indications & indication->bit))
541			continue;
542
543		nled = &npem->leds[led_idx++];
544		nled->indication = indication;
545		nled->npem = npem;
546
547		ret = pci_npem_set_led_classdev(npem, nled);
548		if (ret) {
549			npem_free(npem);
550			return ret;
551		}
552	}
553
554	dev->npem = npem;
555	return 0;
556}
557
558void pci_npem_remove(struct pci_dev *dev)
559{
560	npem_free(dev->npem);
561}
562
563void pci_npem_create(struct pci_dev *dev)
564{
565	const struct npem_ops *ops = &npem_ops;
566	int pos = 0, ret;
567	u32 cap;
568
569	if (npem_has_dsm(dev)) {
570		/*
571		 * OS should use the DSM for LED control if it is available
572		 * PCI Firmware Spec r3.3 sec 4.7.
573		 */
574		ret = dsm_get(dev, GET_SUPPORTED_STATES_DSM, &cap);
575		if (ret)
576			return;
577
578		ops = &dsm_ops;
579	} else {
580		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_NPEM);
581		if (pos == 0)
582			return;
583
584		if (pci_read_config_dword(dev, pos + PCI_NPEM_CAP, &cap) != 0 ||
585		    (cap & PCI_NPEM_CAP_CAPABLE) == 0)
586			return;
587	}
588
589	pci_info(dev, "Configuring %s\n", ops->name);
590
591	ret = pci_npem_init(dev, ops, pos, cap);
592	if (ret)
593		pci_err(dev, "Failed to register %s, err: %d\n", ops->name,
594			ret);
595}