0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/leds.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016 #include <linux/types.h>
0017
0018 #include <asm/opal.h>
0019
0020
0021 struct led_type_map {
0022 const int type;
0023 const char *desc;
0024 };
0025 static const struct led_type_map led_type_map[] = {
0026 {OPAL_SLOT_LED_TYPE_ID, "identify"},
0027 {OPAL_SLOT_LED_TYPE_FAULT, "fault"},
0028 {OPAL_SLOT_LED_TYPE_ATTN, "attention"},
0029 {-1, NULL},
0030 };
0031
0032 struct powernv_led_common {
0033
0034
0035
0036
0037
0038
0039
0040 bool led_disabled;
0041
0042
0043 __be64 max_led_type;
0044
0045
0046 struct mutex lock;
0047 };
0048
0049
0050 struct powernv_led_data {
0051 struct led_classdev cdev;
0052 char *loc_code;
0053 int led_type;
0054
0055 struct powernv_led_common *common;
0056 };
0057
0058
0059
0060 static int powernv_get_led_type(const char *led_type_desc)
0061 {
0062 int i;
0063
0064 for (i = 0; i < ARRAY_SIZE(led_type_map); i++)
0065 if (!strcmp(led_type_map[i].desc, led_type_desc))
0066 return led_type_map[i].type;
0067
0068 return -1;
0069 }
0070
0071
0072
0073
0074
0075
0076 static int powernv_led_set(struct powernv_led_data *powernv_led,
0077 enum led_brightness value)
0078 {
0079 int rc, token;
0080 u64 led_mask, led_value = 0;
0081 __be64 max_type;
0082 struct opal_msg msg;
0083 struct device *dev = powernv_led->cdev.dev;
0084 struct powernv_led_common *powernv_led_common = powernv_led->common;
0085
0086
0087 max_type = powernv_led_common->max_led_type;
0088 led_mask = OPAL_SLOT_LED_STATE_ON << powernv_led->led_type;
0089 if (value)
0090 led_value = led_mask;
0091
0092
0093 token = opal_async_get_token_interruptible();
0094 if (token < 0) {
0095 if (token != -ERESTARTSYS)
0096 dev_err(dev, "%s: Couldn't get OPAL async token\n",
0097 __func__);
0098 return token;
0099 }
0100
0101 rc = opal_leds_set_ind(token, powernv_led->loc_code,
0102 led_mask, led_value, &max_type);
0103 if (rc != OPAL_ASYNC_COMPLETION) {
0104 dev_err(dev, "%s: OPAL set LED call failed for %s [rc=%d]\n",
0105 __func__, powernv_led->loc_code, rc);
0106 goto out_token;
0107 }
0108
0109 rc = opal_async_wait_response(token, &msg);
0110 if (rc) {
0111 dev_err(dev,
0112 "%s: Failed to wait for the async response [rc=%d]\n",
0113 __func__, rc);
0114 goto out_token;
0115 }
0116
0117 rc = opal_get_async_rc(msg);
0118 if (rc != OPAL_SUCCESS)
0119 dev_err(dev, "%s : OAPL async call returned failed [rc=%d]\n",
0120 __func__, rc);
0121
0122 out_token:
0123 opal_async_release_token(token);
0124 return rc;
0125 }
0126
0127
0128
0129
0130
0131 static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
0132 {
0133 int rc;
0134 __be64 mask, value, max_type;
0135 u64 led_mask, led_value;
0136 struct device *dev = powernv_led->cdev.dev;
0137 struct powernv_led_common *powernv_led_common = powernv_led->common;
0138
0139
0140 mask = cpu_to_be64(0);
0141 value = cpu_to_be64(0);
0142 max_type = powernv_led_common->max_led_type;
0143
0144 rc = opal_leds_get_ind(powernv_led->loc_code,
0145 &mask, &value, &max_type);
0146 if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
0147 dev_err(dev, "%s: OPAL get led call failed [rc=%d]\n",
0148 __func__, rc);
0149 return LED_OFF;
0150 }
0151
0152 led_mask = be64_to_cpu(mask);
0153 led_value = be64_to_cpu(value);
0154
0155
0156 if (!((led_mask >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)) {
0157 dev_err(dev, "%s: LED status not available for %s\n",
0158 __func__, powernv_led->cdev.name);
0159 return LED_OFF;
0160 }
0161
0162
0163 if ((led_value >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)
0164 return LED_FULL;
0165
0166 return LED_OFF;
0167 }
0168
0169
0170
0171
0172
0173 static int powernv_brightness_set(struct led_classdev *led_cdev,
0174 enum led_brightness value)
0175 {
0176 struct powernv_led_data *powernv_led =
0177 container_of(led_cdev, struct powernv_led_data, cdev);
0178 struct powernv_led_common *powernv_led_common = powernv_led->common;
0179 int rc;
0180
0181
0182 if (powernv_led_common->led_disabled)
0183 return 0;
0184
0185 mutex_lock(&powernv_led_common->lock);
0186 rc = powernv_led_set(powernv_led, value);
0187 mutex_unlock(&powernv_led_common->lock);
0188
0189 return rc;
0190 }
0191
0192
0193 static enum led_brightness powernv_brightness_get(struct led_classdev *led_cdev)
0194 {
0195 struct powernv_led_data *powernv_led =
0196 container_of(led_cdev, struct powernv_led_data, cdev);
0197
0198 return powernv_led_get(powernv_led);
0199 }
0200
0201
0202
0203
0204
0205 static int powernv_led_create(struct device *dev,
0206 struct powernv_led_data *powernv_led,
0207 const char *led_type_desc)
0208 {
0209 int rc;
0210
0211
0212 powernv_led->led_type = powernv_get_led_type(led_type_desc);
0213 if (powernv_led->led_type == -1) {
0214 dev_warn(dev, "%s: No support for led type : %s\n",
0215 __func__, led_type_desc);
0216 return -EINVAL;
0217 }
0218
0219
0220 powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
0221 powernv_led->loc_code,
0222 led_type_desc);
0223 if (!powernv_led->cdev.name)
0224 return -ENOMEM;
0225
0226 powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
0227 powernv_led->cdev.brightness_get = powernv_brightness_get;
0228 powernv_led->cdev.brightness = LED_OFF;
0229 powernv_led->cdev.max_brightness = LED_FULL;
0230
0231
0232 rc = devm_led_classdev_register(dev, &powernv_led->cdev);
0233 if (rc) {
0234 dev_err(dev, "%s: Classdev registration failed for %s\n",
0235 __func__, powernv_led->cdev.name);
0236 }
0237
0238 return rc;
0239 }
0240
0241
0242 static int powernv_led_classdev(struct platform_device *pdev,
0243 struct device_node *led_node,
0244 struct powernv_led_common *powernv_led_common)
0245 {
0246 const char *cur = NULL;
0247 int rc = -1;
0248 struct property *p;
0249 struct device_node *np;
0250 struct powernv_led_data *powernv_led;
0251 struct device *dev = &pdev->dev;
0252
0253 for_each_available_child_of_node(led_node, np) {
0254 p = of_find_property(np, "led-types", NULL);
0255
0256 while ((cur = of_prop_next_string(p, cur)) != NULL) {
0257 powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
0258 GFP_KERNEL);
0259 if (!powernv_led) {
0260 of_node_put(np);
0261 return -ENOMEM;
0262 }
0263
0264 powernv_led->common = powernv_led_common;
0265 powernv_led->loc_code = (char *)np->name;
0266
0267 rc = powernv_led_create(dev, powernv_led, cur);
0268 if (rc) {
0269 of_node_put(np);
0270 return rc;
0271 }
0272 }
0273 }
0274
0275 return rc;
0276 }
0277
0278
0279 static int powernv_led_probe(struct platform_device *pdev)
0280 {
0281 struct device_node *led_node;
0282 struct powernv_led_common *powernv_led_common;
0283 struct device *dev = &pdev->dev;
0284 int rc;
0285
0286 led_node = of_find_node_by_path("/ibm,opal/leds");
0287 if (!led_node) {
0288 dev_err(dev, "%s: LED parent device node not found\n",
0289 __func__);
0290 return -EINVAL;
0291 }
0292
0293 powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common),
0294 GFP_KERNEL);
0295 if (!powernv_led_common) {
0296 rc = -ENOMEM;
0297 goto out;
0298 }
0299
0300 mutex_init(&powernv_led_common->lock);
0301 powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
0302
0303 platform_set_drvdata(pdev, powernv_led_common);
0304
0305 rc = powernv_led_classdev(pdev, led_node, powernv_led_common);
0306 out:
0307 of_node_put(led_node);
0308 return rc;
0309 }
0310
0311
0312 static int powernv_led_remove(struct platform_device *pdev)
0313 {
0314 struct powernv_led_common *powernv_led_common;
0315
0316
0317 powernv_led_common = platform_get_drvdata(pdev);
0318 powernv_led_common->led_disabled = true;
0319
0320
0321 mutex_destroy(&powernv_led_common->lock);
0322
0323 dev_info(&pdev->dev, "PowerNV led module unregistered\n");
0324 return 0;
0325 }
0326
0327
0328 static const struct of_device_id powernv_led_match[] = {
0329 {
0330 .compatible = "ibm,opal-v3-led",
0331 },
0332 {},
0333 };
0334 MODULE_DEVICE_TABLE(of, powernv_led_match);
0335
0336 static struct platform_driver powernv_led_driver = {
0337 .probe = powernv_led_probe,
0338 .remove = powernv_led_remove,
0339 .driver = {
0340 .name = "powernv-led-driver",
0341 .of_match_table = powernv_led_match,
0342 },
0343 };
0344
0345 module_platform_driver(powernv_led_driver);
0346
0347 MODULE_LICENSE("GPL v2");
0348 MODULE_DESCRIPTION("PowerNV LED driver");
0349 MODULE_AUTHOR("Vasant Hegde <hegdevasant@linux.vnet.ibm.com>");