^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * PowerNV LED Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright IBM Corp. 2015
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <asm/opal.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) /* Map LED type to description. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) struct led_type_map {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) const int type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) const char *desc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static const struct led_type_map led_type_map[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) {OPAL_SLOT_LED_TYPE_ID, "identify"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) {OPAL_SLOT_LED_TYPE_FAULT, "fault"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) {OPAL_SLOT_LED_TYPE_ATTN, "attention"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) {-1, NULL},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct powernv_led_common {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * By default unload path resets all the LEDs. But on PowerNV
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * platform we want to retain LED state across reboot as these
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * are controlled by firmware. Also service processor can modify
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * the LEDs independent of OS. Hence avoid resetting LEDs in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * unload path.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) bool led_disabled;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) /* Max supported LED type */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) __be64 max_led_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) /* glabal lock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) struct mutex lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) /* PowerNV LED data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) struct powernv_led_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct led_classdev cdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) char *loc_code; /* LED location code */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) int led_type; /* OPAL_SLOT_LED_TYPE_* */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) struct powernv_led_common *common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) /* Returns OPAL_SLOT_LED_TYPE_* for given led type string */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) static int powernv_get_led_type(const char *led_type_desc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) for (i = 0; i < ARRAY_SIZE(led_type_map); i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (!strcmp(led_type_map[i].desc, led_type_desc))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) return led_type_map[i].type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * This commits the state change of the requested LED through an OPAL call.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * This function is called from work queue task context when ever it gets
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * scheduled. This function can sleep at opal_async_wait_response call.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int powernv_led_set(struct powernv_led_data *powernv_led,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) int rc, token;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) u64 led_mask, led_value = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) __be64 max_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct opal_msg msg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct device *dev = powernv_led->cdev.dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct powernv_led_common *powernv_led_common = powernv_led->common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) /* Prepare for the OPAL call */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) max_type = powernv_led_common->max_led_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) led_mask = OPAL_SLOT_LED_STATE_ON << powernv_led->led_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) if (value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) led_value = led_mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) /* OPAL async call */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) token = opal_async_get_token_interruptible();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (token < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (token != -ERESTARTSYS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) dev_err(dev, "%s: Couldn't get OPAL async token\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return token;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) rc = opal_leds_set_ind(token, powernv_led->loc_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) led_mask, led_value, &max_type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) if (rc != OPAL_ASYNC_COMPLETION) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) dev_err(dev, "%s: OPAL set LED call failed for %s [rc=%d]\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) __func__, powernv_led->loc_code, rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) goto out_token;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) rc = opal_async_wait_response(token, &msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) dev_err(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) "%s: Failed to wait for the async response [rc=%d]\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) __func__, rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) goto out_token;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) rc = opal_get_async_rc(msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if (rc != OPAL_SUCCESS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) dev_err(dev, "%s : OAPL async call returned failed [rc=%d]\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) __func__, rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) out_token:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) opal_async_release_token(token);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) * This function fetches the LED state for a given LED type for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) * mentioned LED classdev structure.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) __be64 mask, value, max_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) u64 led_mask, led_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) struct device *dev = powernv_led->cdev.dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) struct powernv_led_common *powernv_led_common = powernv_led->common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) /* Fetch all LED status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) mask = cpu_to_be64(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) value = cpu_to_be64(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) max_type = powernv_led_common->max_led_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) rc = opal_leds_get_ind(powernv_led->loc_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) &mask, &value, &max_type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) dev_err(dev, "%s: OPAL get led call failed [rc=%d]\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) __func__, rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) return LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) led_mask = be64_to_cpu(mask);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) led_value = be64_to_cpu(value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) /* LED status available */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) if (!((led_mask >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) dev_err(dev, "%s: LED status not available for %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) __func__, powernv_led->cdev.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) /* LED status value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) if ((led_value >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return LED_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) return LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) * LED classdev 'brightness_get' function. This schedules work
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) * to update LED state.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) static int powernv_brightness_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) struct powernv_led_data *powernv_led =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) container_of(led_cdev, struct powernv_led_data, cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) struct powernv_led_common *powernv_led_common = powernv_led->common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) /* Do not modify LED in unload path */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) if (powernv_led_common->led_disabled)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) mutex_lock(&powernv_led_common->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) rc = powernv_led_set(powernv_led, value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) mutex_unlock(&powernv_led_common->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) /* LED classdev 'brightness_get' function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) static enum led_brightness powernv_brightness_get(struct led_classdev *led_cdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) struct powernv_led_data *powernv_led =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) container_of(led_cdev, struct powernv_led_data, cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) return powernv_led_get(powernv_led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) * This function registers classdev structure for any given type of LED on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) * a given child LED device node.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) static int powernv_led_create(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) struct powernv_led_data *powernv_led,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) const char *led_type_desc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) /* Make sure LED type is supported */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) powernv_led->led_type = powernv_get_led_type(led_type_desc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) if (powernv_led->led_type == -1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) dev_warn(dev, "%s: No support for led type : %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) __func__, led_type_desc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) /* Create the name for classdev */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) powernv_led->loc_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) led_type_desc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if (!powernv_led->cdev.name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) powernv_led->cdev.brightness_get = powernv_brightness_get;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) powernv_led->cdev.brightness = LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) powernv_led->cdev.max_brightness = LED_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) /* Register the classdev */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) rc = devm_led_classdev_register(dev, &powernv_led->cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) dev_err(dev, "%s: Classdev registration failed for %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) __func__, powernv_led->cdev.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) /* Go through LED device tree node and register LED classdev structure */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) static int powernv_led_classdev(struct platform_device *pdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) struct device_node *led_node,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) struct powernv_led_common *powernv_led_common)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) const char *cur = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) int rc = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) struct property *p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) struct device_node *np;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) struct powernv_led_data *powernv_led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) for_each_available_child_of_node(led_node, np) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) p = of_find_property(np, "led-types", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) while ((cur = of_prop_next_string(p, cur)) != NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (!powernv_led) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) of_node_put(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) powernv_led->common = powernv_led_common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) powernv_led->loc_code = (char *)np->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) rc = powernv_led_create(dev, powernv_led, cur);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) of_node_put(np);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) } /* while end */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) /* Platform driver probe */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) static int powernv_led_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) struct device_node *led_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) struct powernv_led_common *powernv_led_common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) led_node = of_find_node_by_path("/ibm,opal/leds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) if (!led_node) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) dev_err(dev, "%s: LED parent device node not found\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) if (!powernv_led_common) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) rc = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) mutex_init(&powernv_led_common->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) platform_set_drvdata(pdev, powernv_led_common);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) rc = powernv_led_classdev(pdev, led_node, powernv_led_common);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) of_node_put(led_node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) /* Platform driver remove */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) static int powernv_led_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) struct powernv_led_common *powernv_led_common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) /* Disable LED operation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) powernv_led_common = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) powernv_led_common->led_disabled = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) /* Destroy lock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) mutex_destroy(&powernv_led_common->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) dev_info(&pdev->dev, "PowerNV led module unregistered\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) /* Platform driver property match */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) static const struct of_device_id powernv_led_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) .compatible = "ibm,opal-v3-led",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) MODULE_DEVICE_TABLE(of, powernv_led_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) static struct platform_driver powernv_led_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) .probe = powernv_led_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) .remove = powernv_led_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) .name = "powernv-led-driver",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) .of_match_table = powernv_led_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) module_platform_driver(powernv_led_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) MODULE_DESCRIPTION("PowerNV LED driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) MODULE_AUTHOR("Vasant Hegde <hegdevasant@linux.vnet.ibm.com>");