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