0001
0002
0003
0004
0005
0006 #include <linux/bitops.h>
0007 #include <linux/device.h>
0008 #include <linux/io.h>
0009 #include <linux/leds.h>
0010 #include <linux/module.h>
0011 #include <linux/of_device.h>
0012 #include <linux/platform_data/mlxreg.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/regmap.h>
0015
0016
0017 #define MLXREG_LED_OFFSET_BLINK_3HZ 0x01
0018 #define MLXREG_LED_OFFSET_BLINK_6HZ 0x02
0019 #define MLXREG_LED_IS_OFF 0x00
0020 #define MLXREG_LED_RED_SOLID 0x05
0021 #define MLXREG_LED_GREEN_SOLID 0x0D
0022 #define MLXREG_LED_AMBER_SOLID 0x09
0023 #define MLXREG_LED_BLINK_3HZ 167
0024 #define MLXREG_LED_BLINK_6HZ 83
0025 #define MLXREG_LED_CAPABILITY_CLEAR GENMASK(31, 8)
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 struct mlxreg_led_data {
0038 struct mlxreg_core_data *data;
0039 struct led_classdev led_cdev;
0040 u8 base_color;
0041 void *data_parent;
0042 char led_cdev_name[MLXREG_CORE_LABEL_MAX_SIZE];
0043 };
0044
0045 #define cdev_to_priv(c) container_of(c, struct mlxreg_led_data, led_cdev)
0046
0047
0048
0049
0050
0051
0052
0053
0054 struct mlxreg_led_priv_data {
0055 struct platform_device *pdev;
0056 struct mlxreg_core_platform_data *pdata;
0057 struct mutex access_lock;
0058 };
0059
0060 static int
0061 mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset)
0062 {
0063 struct mlxreg_led_priv_data *priv = led_data->data_parent;
0064 struct mlxreg_core_platform_data *led_pdata = priv->pdata;
0065 struct mlxreg_core_data *data = led_data->data;
0066 u32 regval;
0067 u32 nib;
0068 int ret;
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080 mutex_lock(&priv->access_lock);
0081
0082 ret = regmap_read(led_pdata->regmap, data->reg, ®val);
0083 if (ret)
0084 goto access_error;
0085
0086 nib = (ror32(data->mask, data->bit) == 0xf0) ? rol32(vset, data->bit) :
0087 rol32(vset, data->bit + 4);
0088 regval = (regval & data->mask) | nib;
0089
0090 ret = regmap_write(led_pdata->regmap, data->reg, regval);
0091
0092 access_error:
0093 mutex_unlock(&priv->access_lock);
0094
0095 return ret;
0096 }
0097
0098 static enum led_brightness
0099 mlxreg_led_get_hw(struct mlxreg_led_data *led_data)
0100 {
0101 struct mlxreg_led_priv_data *priv = led_data->data_parent;
0102 struct mlxreg_core_platform_data *led_pdata = priv->pdata;
0103 struct mlxreg_core_data *data = led_data->data;
0104 u32 regval;
0105 int err;
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117 err = regmap_read(led_pdata->regmap, data->reg, ®val);
0118 if (err < 0) {
0119 dev_warn(led_data->led_cdev.dev, "Failed to get current brightness, error: %d\n",
0120 err);
0121
0122 return LED_OFF;
0123 }
0124
0125 regval = regval & ~data->mask;
0126 regval = (ror32(data->mask, data->bit) == 0xf0) ? ror32(regval,
0127 data->bit) : ror32(regval, data->bit + 4);
0128 if (regval >= led_data->base_color &&
0129 regval <= (led_data->base_color + MLXREG_LED_OFFSET_BLINK_6HZ))
0130 return LED_FULL;
0131
0132 return LED_OFF;
0133 }
0134
0135 static int
0136 mlxreg_led_brightness_set(struct led_classdev *cled, enum led_brightness value)
0137 {
0138 struct mlxreg_led_data *led_data = cdev_to_priv(cled);
0139
0140 if (value)
0141 return mlxreg_led_store_hw(led_data, led_data->base_color);
0142 else
0143 return mlxreg_led_store_hw(led_data, MLXREG_LED_IS_OFF);
0144 }
0145
0146 static enum led_brightness
0147 mlxreg_led_brightness_get(struct led_classdev *cled)
0148 {
0149 struct mlxreg_led_data *led_data = cdev_to_priv(cled);
0150
0151 return mlxreg_led_get_hw(led_data);
0152 }
0153
0154 static int
0155 mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on,
0156 unsigned long *delay_off)
0157 {
0158 struct mlxreg_led_data *led_data = cdev_to_priv(cled);
0159 int err;
0160
0161
0162
0163
0164
0165
0166 if (!(*delay_on == 0 && *delay_off == 0) &&
0167 !(*delay_on == MLXREG_LED_BLINK_3HZ &&
0168 *delay_off == MLXREG_LED_BLINK_3HZ) &&
0169 !(*delay_on == MLXREG_LED_BLINK_6HZ &&
0170 *delay_off == MLXREG_LED_BLINK_6HZ))
0171 return -EINVAL;
0172
0173 if (*delay_on == MLXREG_LED_BLINK_6HZ)
0174 err = mlxreg_led_store_hw(led_data, led_data->base_color +
0175 MLXREG_LED_OFFSET_BLINK_6HZ);
0176 else if (*delay_on == MLXREG_LED_BLINK_3HZ)
0177 err = mlxreg_led_store_hw(led_data, led_data->base_color +
0178 MLXREG_LED_OFFSET_BLINK_3HZ);
0179 else
0180 err = mlxreg_led_store_hw(led_data, led_data->base_color);
0181
0182 return err;
0183 }
0184
0185 static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
0186 {
0187 struct mlxreg_core_platform_data *led_pdata = priv->pdata;
0188 struct mlxreg_core_data *data = led_pdata->data;
0189 struct mlxreg_led_data *led_data;
0190 struct led_classdev *led_cdev;
0191 enum led_brightness brightness;
0192 u32 regval;
0193 int i;
0194 int err;
0195
0196 for (i = 0; i < led_pdata->counter; i++, data++) {
0197 led_data = devm_kzalloc(&priv->pdev->dev, sizeof(*led_data),
0198 GFP_KERNEL);
0199 if (!led_data)
0200 return -ENOMEM;
0201
0202 if (data->capability) {
0203 err = regmap_read(led_pdata->regmap, data->capability,
0204 ®val);
0205 if (err) {
0206 dev_err(&priv->pdev->dev, "Failed to query capability register\n");
0207 return err;
0208 }
0209 if (!(regval & data->bit))
0210 continue;
0211
0212
0213
0214
0215
0216 data->bit &= MLXREG_LED_CAPABILITY_CLEAR;
0217 }
0218
0219 led_cdev = &led_data->led_cdev;
0220 led_data->data_parent = priv;
0221 if (strstr(data->label, "red") ||
0222 strstr(data->label, "orange")) {
0223 brightness = LED_OFF;
0224 led_data->base_color = MLXREG_LED_RED_SOLID;
0225 } else if (strstr(data->label, "amber")) {
0226 brightness = LED_OFF;
0227 led_data->base_color = MLXREG_LED_AMBER_SOLID;
0228 } else {
0229 brightness = LED_OFF;
0230 led_data->base_color = MLXREG_LED_GREEN_SOLID;
0231 }
0232 snprintf(led_data->led_cdev_name, sizeof(led_data->led_cdev_name),
0233 "mlxreg:%s", data->label);
0234 led_cdev->name = led_data->led_cdev_name;
0235 led_cdev->brightness = brightness;
0236 led_cdev->max_brightness = LED_ON;
0237 led_cdev->brightness_set_blocking =
0238 mlxreg_led_brightness_set;
0239 led_cdev->brightness_get = mlxreg_led_brightness_get;
0240 led_cdev->blink_set = mlxreg_led_blink_set;
0241 led_cdev->flags = LED_CORE_SUSPENDRESUME;
0242 led_data->data = data;
0243 err = devm_led_classdev_register(&priv->pdev->dev, led_cdev);
0244 if (err)
0245 return err;
0246
0247 if (led_cdev->brightness)
0248 mlxreg_led_brightness_set(led_cdev,
0249 led_cdev->brightness);
0250 dev_info(led_cdev->dev, "label: %s, mask: 0x%02x, offset:0x%02x\n",
0251 data->label, data->mask, data->reg);
0252 }
0253
0254 return 0;
0255 }
0256
0257 static int mlxreg_led_probe(struct platform_device *pdev)
0258 {
0259 struct mlxreg_core_platform_data *led_pdata;
0260 struct mlxreg_led_priv_data *priv;
0261
0262 led_pdata = dev_get_platdata(&pdev->dev);
0263 if (!led_pdata) {
0264 dev_err(&pdev->dev, "Failed to get platform data.\n");
0265 return -EINVAL;
0266 }
0267
0268 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0269 if (!priv)
0270 return -ENOMEM;
0271
0272 mutex_init(&priv->access_lock);
0273 priv->pdev = pdev;
0274 priv->pdata = led_pdata;
0275
0276 return mlxreg_led_config(priv);
0277 }
0278
0279 static int mlxreg_led_remove(struct platform_device *pdev)
0280 {
0281 struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev);
0282
0283 mutex_destroy(&priv->access_lock);
0284
0285 return 0;
0286 }
0287
0288 static struct platform_driver mlxreg_led_driver = {
0289 .driver = {
0290 .name = "leds-mlxreg",
0291 },
0292 .probe = mlxreg_led_probe,
0293 .remove = mlxreg_led_remove,
0294 };
0295
0296 module_platform_driver(mlxreg_led_driver);
0297
0298 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
0299 MODULE_DESCRIPTION("Mellanox LED regmap driver");
0300 MODULE_LICENSE("Dual BSD/GPL");
0301 MODULE_ALIAS("platform:leds-mlxreg");