0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/bitops.h>
0009 #include <linux/device.h>
0010 #include <linux/hwmon.h>
0011 #include <linux/hwmon-sysfs.h>
0012 #include <linux/i2c.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/module.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_data/mlxreg.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/spinlock.h>
0019 #include <linux/string_helpers.h>
0020 #include <linux/regmap.h>
0021 #include <linux/workqueue.h>
0022
0023
0024 #define MLXREG_HOTPLUG_EVENT_OFF 1
0025 #define MLXREG_HOTPLUG_MASK_OFF 2
0026 #define MLXREG_HOTPLUG_AGGR_MASK_OFF 1
0027
0028
0029 #define MLXREG_HOTPLUG_GOOD_HEALTH_MASK 0x02
0030
0031 #define MLXREG_HOTPLUG_ATTRS_MAX 128
0032 #define MLXREG_HOTPLUG_NOT_ASSERT 3
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054 struct mlxreg_hotplug_priv_data {
0055 int irq;
0056 struct device *dev;
0057 struct platform_device *pdev;
0058 struct mlxreg_hotplug_platform_data *plat;
0059 struct regmap *regmap;
0060 struct delayed_work dwork_irq;
0061 spinlock_t lock;
0062 struct device *hwmon;
0063 struct attribute *mlxreg_hotplug_attr[MLXREG_HOTPLUG_ATTRS_MAX + 1];
0064 struct sensor_device_attribute_2
0065 mlxreg_hotplug_dev_attr[MLXREG_HOTPLUG_ATTRS_MAX];
0066 struct attribute_group group;
0067 const struct attribute_group *groups[2];
0068 u32 cell;
0069 u32 mask;
0070 u32 aggr_cache;
0071 bool after_probe;
0072 u8 not_asserted;
0073 };
0074
0075
0076 static char *mlxreg_hotplug_udev_envp[] = { NULL, NULL };
0077
0078 static int
0079 mlxreg_hotplug_udev_event_send(struct kobject *kobj,
0080 struct mlxreg_core_data *data, bool action)
0081 {
0082 char event_str[MLXREG_CORE_LABEL_MAX_SIZE + 2];
0083 char label[MLXREG_CORE_LABEL_MAX_SIZE] = { 0 };
0084
0085 mlxreg_hotplug_udev_envp[0] = event_str;
0086 string_upper(label, data->label);
0087 snprintf(event_str, MLXREG_CORE_LABEL_MAX_SIZE, "%s=%d", label, !!action);
0088
0089 return kobject_uevent_env(kobj, KOBJ_CHANGE, mlxreg_hotplug_udev_envp);
0090 }
0091
0092 static void
0093 mlxreg_hotplug_pdata_export(void *pdata, void *regmap)
0094 {
0095 struct mlxreg_core_hotplug_platform_data *dev_pdata = pdata;
0096
0097
0098 dev_pdata->regmap = regmap;
0099 }
0100
0101 static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
0102 struct mlxreg_core_data *data,
0103 enum mlxreg_hotplug_kind kind)
0104 {
0105 struct i2c_board_info *brdinfo = data->hpdev.brdinfo;
0106 struct mlxreg_core_hotplug_platform_data *pdata;
0107 struct i2c_client *client;
0108
0109
0110 mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, true);
0111
0112
0113
0114
0115
0116 if (data->hpdev.nr < 0)
0117 return 0;
0118
0119 pdata = dev_get_platdata(&priv->pdev->dev);
0120 switch (data->hpdev.action) {
0121 case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION:
0122 data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr +
0123 pdata->shift_nr);
0124 if (!data->hpdev.adapter) {
0125 dev_err(priv->dev, "Failed to get adapter for bus %d\n",
0126 data->hpdev.nr + pdata->shift_nr);
0127 return -EFAULT;
0128 }
0129
0130
0131 if (brdinfo->platform_data)
0132 mlxreg_hotplug_pdata_export(brdinfo->platform_data, pdata->regmap);
0133
0134 client = i2c_new_client_device(data->hpdev.adapter,
0135 brdinfo);
0136 if (IS_ERR(client)) {
0137 dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
0138 brdinfo->type, data->hpdev.nr +
0139 pdata->shift_nr, brdinfo->addr);
0140
0141 i2c_put_adapter(data->hpdev.adapter);
0142 data->hpdev.adapter = NULL;
0143 return PTR_ERR(client);
0144 }
0145
0146 data->hpdev.client = client;
0147 break;
0148 case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION:
0149
0150 if (data->hpdev.brdinfo && data->hpdev.brdinfo->platform_data)
0151 mlxreg_hotplug_pdata_export(data->hpdev.brdinfo->platform_data,
0152 pdata->regmap);
0153
0154 data->notifier = data->hpdev.notifier;
0155 data->hpdev.pdev = platform_device_register_resndata(&priv->pdev->dev,
0156 brdinfo->type,
0157 data->hpdev.nr,
0158 NULL, 0, data,
0159 sizeof(*data));
0160 if (IS_ERR(data->hpdev.pdev))
0161 return PTR_ERR(data->hpdev.pdev);
0162
0163 break;
0164 default:
0165 break;
0166 }
0167
0168 if (data->hpdev.notifier && data->hpdev.notifier->user_handler)
0169 return data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 1);
0170
0171 return 0;
0172 }
0173
0174 static void
0175 mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_priv_data *priv,
0176 struct mlxreg_core_data *data,
0177 enum mlxreg_hotplug_kind kind)
0178 {
0179
0180 mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, false);
0181 if (data->hpdev.notifier && data->hpdev.notifier->user_handler)
0182 data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 0);
0183
0184 switch (data->hpdev.action) {
0185 case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION:
0186 if (data->hpdev.client) {
0187 i2c_unregister_device(data->hpdev.client);
0188 data->hpdev.client = NULL;
0189 }
0190
0191 if (data->hpdev.adapter) {
0192 i2c_put_adapter(data->hpdev.adapter);
0193 data->hpdev.adapter = NULL;
0194 }
0195 break;
0196 case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION:
0197 if (data->hpdev.pdev)
0198 platform_device_unregister(data->hpdev.pdev);
0199 break;
0200 default:
0201 break;
0202 }
0203 }
0204
0205 static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
0206 struct device_attribute *attr,
0207 char *buf)
0208 {
0209 struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(dev);
0210 struct mlxreg_core_hotplug_platform_data *pdata;
0211 int index = to_sensor_dev_attr_2(attr)->index;
0212 int nr = to_sensor_dev_attr_2(attr)->nr;
0213 struct mlxreg_core_item *item;
0214 struct mlxreg_core_data *data;
0215 u32 regval;
0216 int ret;
0217
0218 pdata = dev_get_platdata(&priv->pdev->dev);
0219 item = pdata->items + nr;
0220 data = item->data + index;
0221
0222 ret = regmap_read(priv->regmap, data->reg, ®val);
0223 if (ret)
0224 return ret;
0225
0226 if (item->health) {
0227 regval &= data->mask;
0228 } else {
0229
0230 if (item->inversed)
0231 regval = !(regval & data->mask);
0232 else
0233 regval = !!(regval & data->mask);
0234 }
0235
0236 return sprintf(buf, "%u\n", regval);
0237 }
0238
0239 #define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
0240 #define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i]
0241
0242 static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
0243 {
0244 struct mlxreg_core_hotplug_platform_data *pdata;
0245 struct mlxreg_core_item *item;
0246 struct mlxreg_core_data *data;
0247 unsigned long mask;
0248 u32 regval;
0249 int num_attrs = 0, id = 0, i, j, k, ret;
0250
0251 pdata = dev_get_platdata(&priv->pdev->dev);
0252 item = pdata->items;
0253
0254
0255 for (i = 0; i < pdata->counter; i++, item++) {
0256 if (item->capability) {
0257
0258
0259
0260
0261
0262 ret = regmap_read(priv->regmap, item->capability,
0263 ®val);
0264 if (ret)
0265 return ret;
0266
0267 item->mask = GENMASK((regval & item->mask) - 1, 0);
0268 }
0269
0270 data = item->data;
0271
0272
0273 mask = item->mask;
0274 k = 0;
0275 for_each_set_bit(j, &mask, item->count) {
0276 if (data->capability) {
0277
0278
0279
0280
0281 ret = regmap_read(priv->regmap,
0282 data->capability, ®val);
0283 if (ret)
0284 return ret;
0285 if (!(regval & data->bit)) {
0286 data++;
0287 continue;
0288 }
0289 }
0290 PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr;
0291 PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev,
0292 GFP_KERNEL,
0293 data->label);
0294
0295 if (!PRIV_ATTR(id)->name) {
0296 dev_err(priv->dev, "Memory allocation failed for attr %d.\n",
0297 id);
0298 return -ENOMEM;
0299 }
0300
0301 PRIV_DEV_ATTR(id).dev_attr.attr.name =
0302 PRIV_ATTR(id)->name;
0303 PRIV_DEV_ATTR(id).dev_attr.attr.mode = 0444;
0304 PRIV_DEV_ATTR(id).dev_attr.show =
0305 mlxreg_hotplug_attr_show;
0306 PRIV_DEV_ATTR(id).nr = i;
0307 PRIV_DEV_ATTR(id).index = k;
0308 sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr);
0309 data++;
0310 id++;
0311 k++;
0312 }
0313 num_attrs += k;
0314 }
0315
0316 priv->group.attrs = devm_kcalloc(&priv->pdev->dev,
0317 num_attrs,
0318 sizeof(struct attribute *),
0319 GFP_KERNEL);
0320 if (!priv->group.attrs)
0321 return -ENOMEM;
0322
0323 priv->group.attrs = priv->mlxreg_hotplug_attr;
0324 priv->groups[0] = &priv->group;
0325 priv->groups[1] = NULL;
0326
0327 return 0;
0328 }
0329
0330 static void
0331 mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
0332 struct mlxreg_core_item *item)
0333 {
0334 struct mlxreg_core_data *data;
0335 unsigned long asserted;
0336 u32 regval, bit;
0337 int ret;
0338
0339
0340
0341
0342
0343
0344
0345
0346 if (unlikely(!item)) {
0347 dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n",
0348 item->reg, item->mask);
0349
0350 return;
0351 }
0352
0353
0354 ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
0355 0);
0356 if (ret)
0357 goto out;
0358
0359
0360 ret = regmap_read(priv->regmap, item->reg, ®val);
0361 if (ret)
0362 goto out;
0363
0364
0365 regval &= item->mask;
0366 asserted = item->cache ^ regval;
0367 item->cache = regval;
0368
0369 for_each_set_bit(bit, &asserted, 8) {
0370 data = item->data + bit;
0371 if (regval & BIT(bit)) {
0372 if (item->inversed)
0373 mlxreg_hotplug_device_destroy(priv, data, item->kind);
0374 else
0375 mlxreg_hotplug_device_create(priv, data, item->kind);
0376 } else {
0377 if (item->inversed)
0378 mlxreg_hotplug_device_create(priv, data, item->kind);
0379 else
0380 mlxreg_hotplug_device_destroy(priv, data, item->kind);
0381 }
0382 }
0383
0384
0385 ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF,
0386 0);
0387 if (ret)
0388 goto out;
0389
0390
0391 ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
0392 item->mask);
0393
0394 out:
0395 if (ret)
0396 dev_err(priv->dev, "Failed to complete workqueue.\n");
0397 }
0398
0399 static void
0400 mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
0401 struct mlxreg_core_item *item)
0402 {
0403 struct mlxreg_core_data *data = item->data;
0404 u32 regval;
0405 int i, ret = 0;
0406
0407 for (i = 0; i < item->count; i++, data++) {
0408
0409 ret = regmap_write(priv->regmap, data->reg +
0410 MLXREG_HOTPLUG_MASK_OFF, 0);
0411 if (ret)
0412 goto out;
0413
0414
0415 ret = regmap_read(priv->regmap, data->reg, ®val);
0416 if (ret)
0417 goto out;
0418
0419 regval &= data->mask;
0420
0421 if (item->cache == regval)
0422 goto ack_event;
0423
0424
0425
0426
0427
0428
0429
0430
0431 if (regval == MLXREG_HOTPLUG_GOOD_HEALTH_MASK) {
0432 if (!data->attached) {
0433
0434
0435
0436
0437 mlxreg_hotplug_device_create(priv, data, item->kind);
0438 data->attached = true;
0439 }
0440 } else {
0441 if (data->attached) {
0442
0443
0444
0445
0446
0447 mlxreg_hotplug_device_destroy(priv, data, item->kind);
0448 data->attached = false;
0449 data->health_cntr = 0;
0450 }
0451 }
0452 item->cache = regval;
0453 ack_event:
0454
0455 ret = regmap_write(priv->regmap, data->reg +
0456 MLXREG_HOTPLUG_EVENT_OFF, 0);
0457 if (ret)
0458 goto out;
0459
0460
0461 ret = regmap_write(priv->regmap, data->reg +
0462 MLXREG_HOTPLUG_MASK_OFF, data->mask);
0463 if (ret)
0464 goto out;
0465 }
0466
0467 out:
0468 if (ret)
0469 dev_err(priv->dev, "Failed to complete workqueue.\n");
0470 }
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499 static void mlxreg_hotplug_work_handler(struct work_struct *work)
0500 {
0501 struct mlxreg_core_hotplug_platform_data *pdata;
0502 struct mlxreg_hotplug_priv_data *priv;
0503 struct mlxreg_core_item *item;
0504 u32 regval, aggr_asserted;
0505 unsigned long flags;
0506 int i, ret;
0507
0508 priv = container_of(work, struct mlxreg_hotplug_priv_data,
0509 dwork_irq.work);
0510 pdata = dev_get_platdata(&priv->pdev->dev);
0511 item = pdata->items;
0512
0513
0514 ret = regmap_write(priv->regmap, pdata->cell +
0515 MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
0516 if (ret < 0)
0517 goto out;
0518
0519
0520 ret = regmap_read(priv->regmap, pdata->cell, ®val);
0521 if (ret)
0522 goto out;
0523
0524 regval &= pdata->mask;
0525 aggr_asserted = priv->aggr_cache ^ regval;
0526 priv->aggr_cache = regval;
0527
0528
0529
0530
0531
0532
0533 if (priv->not_asserted == MLXREG_HOTPLUG_NOT_ASSERT) {
0534 priv->not_asserted = 0;
0535 aggr_asserted = pdata->mask;
0536 }
0537 if (!aggr_asserted)
0538 goto unmask_event;
0539
0540
0541 for (i = 0; i < pdata->counter; i++, item++) {
0542 if (aggr_asserted & item->aggr_mask) {
0543 if (item->health)
0544 mlxreg_hotplug_health_work_helper(priv, item);
0545 else
0546 mlxreg_hotplug_work_helper(priv, item);
0547 }
0548 }
0549
0550 spin_lock_irqsave(&priv->lock, flags);
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561 cancel_delayed_work(&priv->dwork_irq);
0562 schedule_delayed_work(&priv->dwork_irq, 0);
0563
0564 spin_unlock_irqrestore(&priv->lock, flags);
0565
0566 return;
0567
0568 unmask_event:
0569 priv->not_asserted++;
0570
0571 ret = regmap_write(priv->regmap, pdata->cell +
0572 MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
0573
0574 out:
0575 if (ret)
0576 dev_err(priv->dev, "Failed to complete workqueue.\n");
0577 }
0578
0579 static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv)
0580 {
0581 struct mlxreg_core_hotplug_platform_data *pdata;
0582 struct mlxreg_core_item *item;
0583 struct mlxreg_core_data *data;
0584 u32 regval;
0585 int i, j, ret;
0586
0587 pdata = dev_get_platdata(&priv->pdev->dev);
0588 item = pdata->items;
0589
0590 for (i = 0; i < pdata->counter; i++, item++) {
0591
0592 ret = regmap_write(priv->regmap, item->reg +
0593 MLXREG_HOTPLUG_EVENT_OFF, 0);
0594 if (ret)
0595 goto out;
0596
0597
0598
0599
0600
0601 data = item->data;
0602 for (j = 0; j < item->count; j++, data++) {
0603
0604 if (data->capability) {
0605
0606 ret = regmap_read(priv->regmap,
0607 data->capability, ®val);
0608 if (ret)
0609 goto out;
0610
0611 if (!(regval & data->bit))
0612 item->mask &= ~BIT(j);
0613 }
0614 }
0615
0616
0617 if (item->inversed) {
0618 item->cache = item->mask;
0619 ret = regmap_write(priv->regmap, item->reg +
0620 MLXREG_HOTPLUG_MASK_OFF,
0621 item->mask);
0622 if (ret)
0623 goto out;
0624 }
0625 }
0626
0627
0628 ret = regmap_write(priv->regmap, pdata->cell +
0629 MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
0630 if (ret)
0631 goto out;
0632
0633
0634 if (pdata->cell_low) {
0635 ret = regmap_write(priv->regmap, pdata->cell_low +
0636 MLXREG_HOTPLUG_AGGR_MASK_OFF,
0637 pdata->mask_low);
0638 if (ret)
0639 goto out;
0640 }
0641
0642
0643 mlxreg_hotplug_work_handler(&priv->dwork_irq.work);
0644
0645 out:
0646 if (ret)
0647 dev_err(priv->dev, "Failed to set interrupts.\n");
0648 enable_irq(priv->irq);
0649 return ret;
0650 }
0651
0652 static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv)
0653 {
0654 struct mlxreg_core_hotplug_platform_data *pdata;
0655 struct mlxreg_core_item *item;
0656 struct mlxreg_core_data *data;
0657 int count, i, j;
0658
0659 pdata = dev_get_platdata(&priv->pdev->dev);
0660 item = pdata->items;
0661 disable_irq(priv->irq);
0662 cancel_delayed_work_sync(&priv->dwork_irq);
0663
0664
0665 if (pdata->cell_low)
0666 regmap_write(priv->regmap, pdata->cell_low +
0667 MLXREG_HOTPLUG_AGGR_MASK_OFF, 0);
0668
0669
0670 regmap_write(priv->regmap, pdata->cell + MLXREG_HOTPLUG_AGGR_MASK_OFF,
0671 0);
0672
0673
0674 for (i = 0; i < pdata->counter; i++, item++) {
0675 data = item->data;
0676
0677 regmap_write(priv->regmap, data->reg + MLXREG_HOTPLUG_MASK_OFF,
0678 0);
0679
0680 regmap_write(priv->regmap, data->reg +
0681 MLXREG_HOTPLUG_EVENT_OFF, 0);
0682
0683
0684 count = item->count;
0685 for (j = 0; j < count; j++, data++)
0686 mlxreg_hotplug_device_destroy(priv, data, item->kind);
0687 }
0688 }
0689
0690 static irqreturn_t mlxreg_hotplug_irq_handler(int irq, void *dev)
0691 {
0692 struct mlxreg_hotplug_priv_data *priv;
0693
0694 priv = (struct mlxreg_hotplug_priv_data *)dev;
0695
0696
0697 schedule_delayed_work(&priv->dwork_irq, 0);
0698
0699 return IRQ_HANDLED;
0700 }
0701
0702 static int mlxreg_hotplug_probe(struct platform_device *pdev)
0703 {
0704 struct mlxreg_core_hotplug_platform_data *pdata;
0705 struct mlxreg_hotplug_priv_data *priv;
0706 struct i2c_adapter *deferred_adap;
0707 int err;
0708
0709 pdata = dev_get_platdata(&pdev->dev);
0710 if (!pdata) {
0711 dev_err(&pdev->dev, "Failed to get platform data.\n");
0712 return -EINVAL;
0713 }
0714
0715
0716 deferred_adap = i2c_get_adapter(pdata->deferred_nr);
0717 if (!deferred_adap)
0718 return -EPROBE_DEFER;
0719 i2c_put_adapter(deferred_adap);
0720
0721 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0722 if (!priv)
0723 return -ENOMEM;
0724
0725 if (pdata->irq) {
0726 priv->irq = pdata->irq;
0727 } else {
0728 priv->irq = platform_get_irq(pdev, 0);
0729 if (priv->irq < 0)
0730 return priv->irq;
0731 }
0732
0733 priv->regmap = pdata->regmap;
0734 priv->dev = pdev->dev.parent;
0735 priv->pdev = pdev;
0736
0737 err = devm_request_irq(&pdev->dev, priv->irq,
0738 mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING
0739 | IRQF_SHARED, "mlxreg-hotplug", priv);
0740 if (err) {
0741 dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
0742 return err;
0743 }
0744
0745 disable_irq(priv->irq);
0746 spin_lock_init(&priv->lock);
0747 INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler);
0748 dev_set_drvdata(&pdev->dev, priv);
0749
0750 err = mlxreg_hotplug_attr_init(priv);
0751 if (err) {
0752 dev_err(&pdev->dev, "Failed to allocate attributes: %d\n",
0753 err);
0754 return err;
0755 }
0756
0757 priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
0758 "mlxreg_hotplug", priv, priv->groups);
0759 if (IS_ERR(priv->hwmon)) {
0760 dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
0761 PTR_ERR(priv->hwmon));
0762 return PTR_ERR(priv->hwmon);
0763 }
0764
0765
0766 mlxreg_hotplug_set_irq(priv);
0767 priv->after_probe = true;
0768
0769 return 0;
0770 }
0771
0772 static int mlxreg_hotplug_remove(struct platform_device *pdev)
0773 {
0774 struct mlxreg_hotplug_priv_data *priv = dev_get_drvdata(&pdev->dev);
0775
0776
0777 mlxreg_hotplug_unset_irq(priv);
0778 devm_free_irq(&pdev->dev, priv->irq, priv);
0779
0780 return 0;
0781 }
0782
0783 static struct platform_driver mlxreg_hotplug_driver = {
0784 .driver = {
0785 .name = "mlxreg-hotplug",
0786 },
0787 .probe = mlxreg_hotplug_probe,
0788 .remove = mlxreg_hotplug_remove,
0789 };
0790
0791 module_platform_driver(mlxreg_hotplug_driver);
0792
0793 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
0794 MODULE_DESCRIPTION("Mellanox regmap hotplug platform driver");
0795 MODULE_LICENSE("Dual BSD/GPL");
0796 MODULE_ALIAS("platform:mlxreg-hotplug");