1 // SPDX-License-Identifier: GPL-2.0+
3 * Mellanox hotplug driver
5 * Copyright (C) 2016-2020 Mellanox Technologies
8 #include <linux/bitops.h>
9 #include <linux/device.h>
10 #include <linux/hwmon.h>
11 #include <linux/hwmon-sysfs.h>
12 #include <linux/i2c.h>
13 #include <linux/interrupt.h>
14 #include <linux/module.h>
15 #include <linux/platform_data/mlxreg.h>
16 #include <linux/platform_device.h>
17 #include <linux/spinlock.h>
18 #include <linux/string_helpers.h>
19 #include <linux/regmap.h>
20 #include <linux/workqueue.h>
22 /* Offset of event and mask registers from status register. */
23 #define MLXREG_HOTPLUG_EVENT_OFF 1
24 #define MLXREG_HOTPLUG_MASK_OFF 2
25 #define MLXREG_HOTPLUG_AGGR_MASK_OFF 1
27 /* ASIC good health mask. */
28 #define MLXREG_HOTPLUG_GOOD_HEALTH_MASK 0x02
30 #define MLXREG_HOTPLUG_ATTRS_MAX 128
31 #define MLXREG_HOTPLUG_NOT_ASSERT 3
34 * struct mlxreg_hotplug_priv_data - platform private data:
35 * @irq: platform device interrupt number;
37 * @pdev: platform device;
38 * @plat: platform data;
39 * @regmap: register map handle;
40 * @dwork_irq: delayed work template;
42 * @hwmon: hwmon device;
43 * @mlxreg_hotplug_attr: sysfs attributes array;
44 * @mlxreg_hotplug_dev_attr: sysfs sensor device attribute array;
45 * @group: sysfs attribute group;
46 * @groups: list of sysfs attribute group for hwmon registration;
47 * @cell: location of top aggregation interrupt register;
48 * @mask: top aggregation interrupt common mask;
49 * @aggr_cache: last value of aggregation register status;
50 * @after_probe: flag indication probing completion;
51 * @not_asserted: number of entries in workqueue with no signal assertion;
53 struct mlxreg_hotplug_priv_data
{
56 struct platform_device
*pdev
;
57 struct mlxreg_hotplug_platform_data
*plat
;
58 struct regmap
*regmap
;
59 struct delayed_work dwork_irq
;
60 spinlock_t lock
; /* sync with interrupt */
62 struct attribute
*mlxreg_hotplug_attr
[MLXREG_HOTPLUG_ATTRS_MAX
+ 1];
63 struct sensor_device_attribute_2
64 mlxreg_hotplug_dev_attr
[MLXREG_HOTPLUG_ATTRS_MAX
];
65 struct attribute_group group
;
66 const struct attribute_group
*groups
[2];
74 /* Environment variables array for udev. */
75 static char *mlxreg_hotplug_udev_envp
[] = { NULL
, NULL
};
78 mlxreg_hotplug_udev_event_send(struct kobject
*kobj
,
79 struct mlxreg_core_data
*data
, bool action
)
81 char event_str
[MLXREG_CORE_LABEL_MAX_SIZE
+ 2];
82 char label
[MLXREG_CORE_LABEL_MAX_SIZE
] = { 0 };
84 mlxreg_hotplug_udev_envp
[0] = event_str
;
85 string_upper(label
, data
->label
);
86 snprintf(event_str
, MLXREG_CORE_LABEL_MAX_SIZE
, "%s=%d", label
, !!action
);
88 return kobject_uevent_env(kobj
, KOBJ_CHANGE
, mlxreg_hotplug_udev_envp
);
92 mlxreg_hotplug_pdata_export(void *pdata
, void *regmap
)
94 struct mlxreg_core_hotplug_platform_data
*dev_pdata
= pdata
;
96 /* Export regmap to underlying device. */
97 dev_pdata
->regmap
= regmap
;
100 static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data
*priv
,
101 struct mlxreg_core_data
*data
,
102 enum mlxreg_hotplug_kind kind
)
104 struct i2c_board_info
*brdinfo
= data
->hpdev
.brdinfo
;
105 struct mlxreg_core_hotplug_platform_data
*pdata
;
106 struct i2c_client
*client
;
108 /* Notify user by sending hwmon uevent. */
109 mlxreg_hotplug_udev_event_send(&priv
->hwmon
->kobj
, data
, true);
112 * Return if adapter number is negative. It could be in case hotplug
113 * event is not associated with hotplug device.
115 if (data
->hpdev
.nr
< 0 && data
->hpdev
.action
!= MLXREG_HOTPLUG_DEVICE_NO_ACTION
)
118 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
119 switch (data
->hpdev
.action
) {
120 case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION
:
121 data
->hpdev
.adapter
= i2c_get_adapter(data
->hpdev
.nr
+
123 if (!data
->hpdev
.adapter
) {
124 dev_err(priv
->dev
, "Failed to get adapter for bus %d\n",
125 data
->hpdev
.nr
+ pdata
->shift_nr
);
129 /* Export platform data to underlying device. */
130 if (brdinfo
->platform_data
)
131 mlxreg_hotplug_pdata_export(brdinfo
->platform_data
, pdata
->regmap
);
133 client
= i2c_new_client_device(data
->hpdev
.adapter
,
135 if (IS_ERR(client
)) {
136 dev_err(priv
->dev
, "Failed to create client %s at bus %d at addr 0x%02x\n",
137 brdinfo
->type
, data
->hpdev
.nr
+
138 pdata
->shift_nr
, brdinfo
->addr
);
140 i2c_put_adapter(data
->hpdev
.adapter
);
141 data
->hpdev
.adapter
= NULL
;
142 return PTR_ERR(client
);
145 data
->hpdev
.client
= client
;
147 case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION
:
148 /* Export platform data to underlying device. */
149 if (data
->hpdev
.brdinfo
&& data
->hpdev
.brdinfo
->platform_data
)
150 mlxreg_hotplug_pdata_export(data
->hpdev
.brdinfo
->platform_data
,
152 /* Pass parent hotplug device handle to underlying device. */
153 data
->notifier
= data
->hpdev
.notifier
;
154 data
->hpdev
.pdev
= platform_device_register_resndata(&priv
->pdev
->dev
,
159 if (IS_ERR(data
->hpdev
.pdev
))
160 return PTR_ERR(data
->hpdev
.pdev
);
167 if (data
->hpdev
.notifier
&& data
->hpdev
.notifier
->user_handler
)
168 return data
->hpdev
.notifier
->user_handler(data
->hpdev
.notifier
->handle
, kind
, 1);
174 mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_priv_data
*priv
,
175 struct mlxreg_core_data
*data
,
176 enum mlxreg_hotplug_kind kind
)
178 /* Notify user by sending hwmon uevent. */
179 mlxreg_hotplug_udev_event_send(&priv
->hwmon
->kobj
, data
, false);
180 if (data
->hpdev
.notifier
&& data
->hpdev
.notifier
->user_handler
)
181 data
->hpdev
.notifier
->user_handler(data
->hpdev
.notifier
->handle
, kind
, 0);
183 switch (data
->hpdev
.action
) {
184 case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION
:
185 if (data
->hpdev
.client
) {
186 i2c_unregister_device(data
->hpdev
.client
);
187 data
->hpdev
.client
= NULL
;
190 if (data
->hpdev
.adapter
) {
191 i2c_put_adapter(data
->hpdev
.adapter
);
192 data
->hpdev
.adapter
= NULL
;
195 case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION
:
196 if (data
->hpdev
.pdev
)
197 platform_device_unregister(data
->hpdev
.pdev
);
204 static ssize_t
mlxreg_hotplug_attr_show(struct device
*dev
,
205 struct device_attribute
*attr
,
208 struct mlxreg_hotplug_priv_data
*priv
= dev_get_drvdata(dev
);
209 struct mlxreg_core_hotplug_platform_data
*pdata
;
210 int index
= to_sensor_dev_attr_2(attr
)->index
;
211 int nr
= to_sensor_dev_attr_2(attr
)->nr
;
212 struct mlxreg_core_item
*item
;
213 struct mlxreg_core_data
*data
;
217 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
218 item
= pdata
->items
+ nr
;
219 data
= item
->data
+ index
;
221 ret
= regmap_read(priv
->regmap
, data
->reg
, ®val
);
226 regval
&= data
->mask
;
228 /* Bit = 0 : functional if item->inversed is true. */
230 regval
= !(regval
& data
->mask
);
232 regval
= !!(regval
& data
->mask
);
235 return sprintf(buf
, "%u\n", regval
);
238 #define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
239 #define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i]
241 static int mlxreg_hotplug_item_label_index_get(u32 mask
, u32 bit
)
245 for (i
= 0, j
= -1; i
<= bit
; i
++) {
252 static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data
*priv
)
254 struct mlxreg_core_hotplug_platform_data
*pdata
;
255 struct mlxreg_core_item
*item
;
256 struct mlxreg_core_data
*data
;
259 int num_attrs
= 0, id
= 0, i
, j
, k
, count
, ret
;
261 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
264 /* Go over all kinds of items - psu, pwr, fan. */
265 for (i
= 0; i
< pdata
->counter
; i
++, item
++) {
266 if (item
->capability
) {
268 * Read group capability register to get actual number
269 * of interrupt capable components and set group mask
272 ret
= regmap_read(priv
->regmap
, item
->capability
,
277 item
->mask
= GENMASK((regval
& item
->mask
) - 1, 0);
282 /* Go over all unmasked units within item. */
285 count
= item
->ind
? item
->ind
: item
->count
;
286 for_each_set_bit(j
, &mask
, count
) {
287 if (data
->capability
) {
289 * Read capability register and skip non
290 * relevant attributes.
292 ret
= regmap_read(priv
->regmap
,
293 data
->capability
, ®val
);
297 if (!(regval
& data
->bit
)) {
303 PRIV_ATTR(id
) = &PRIV_DEV_ATTR(id
).dev_attr
.attr
;
304 PRIV_ATTR(id
)->name
= devm_kasprintf(&priv
->pdev
->dev
,
307 if (!PRIV_ATTR(id
)->name
) {
308 dev_err(priv
->dev
, "Memory allocation failed for attr %d.\n",
313 PRIV_DEV_ATTR(id
).dev_attr
.attr
.name
=
315 PRIV_DEV_ATTR(id
).dev_attr
.attr
.mode
= 0444;
316 PRIV_DEV_ATTR(id
).dev_attr
.show
=
317 mlxreg_hotplug_attr_show
;
318 PRIV_DEV_ATTR(id
).nr
= i
;
319 PRIV_DEV_ATTR(id
).index
= k
;
320 sysfs_attr_init(&PRIV_DEV_ATTR(id
).dev_attr
.attr
);
328 priv
->group
.attrs
= devm_kcalloc(&priv
->pdev
->dev
,
330 sizeof(struct attribute
*),
332 if (!priv
->group
.attrs
)
335 priv
->group
.attrs
= priv
->mlxreg_hotplug_attr
;
336 priv
->groups
[0] = &priv
->group
;
337 priv
->groups
[1] = NULL
;
343 mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data
*priv
,
344 struct mlxreg_core_item
*item
)
346 struct mlxreg_core_data
*data
;
347 unsigned long asserted
;
352 ret
= regmap_write(priv
->regmap
, item
->reg
+ MLXREG_HOTPLUG_MASK_OFF
,
358 ret
= regmap_read(priv
->regmap
, item
->reg
, ®val
);
362 /* Set asserted bits and save last status. */
363 regval
&= item
->mask
;
364 asserted
= item
->cache
^ regval
;
365 item
->cache
= regval
;
366 for_each_set_bit(bit
, &asserted
, 8) {
369 pos
= mlxreg_hotplug_item_label_index_get(item
->mask
, bit
);
373 data
= item
->data
+ pos
;
374 if (regval
& BIT(bit
)) {
376 mlxreg_hotplug_device_destroy(priv
, data
, item
->kind
);
378 mlxreg_hotplug_device_create(priv
, data
, item
->kind
);
381 mlxreg_hotplug_device_create(priv
, data
, item
->kind
);
383 mlxreg_hotplug_device_destroy(priv
, data
, item
->kind
);
387 /* Acknowledge event. */
388 ret
= regmap_write(priv
->regmap
, item
->reg
+ MLXREG_HOTPLUG_EVENT_OFF
,
394 ret
= regmap_write(priv
->regmap
, item
->reg
+ MLXREG_HOTPLUG_MASK_OFF
,
399 dev_err(priv
->dev
, "Failed to complete workqueue.\n");
403 mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data
*priv
,
404 struct mlxreg_core_item
*item
)
406 struct mlxreg_core_data
*data
= item
->data
;
410 for (i
= 0; i
< item
->count
; i
++, data
++) {
412 ret
= regmap_write(priv
->regmap
, data
->reg
+
413 MLXREG_HOTPLUG_MASK_OFF
, 0);
418 ret
= regmap_read(priv
->regmap
, data
->reg
, ®val
);
422 regval
&= data
->mask
;
424 if (item
->cache
== regval
)
428 * ASIC health indication is provided through two bits. Bits
429 * value 0x2 indicates that ASIC reached the good health, value
430 * 0x0 indicates ASIC the bad health or dormant state and value
431 * 0x3 indicates the booting state. During ASIC reset it should
432 * pass the following states: dormant -> booting -> good.
434 if (regval
== MLXREG_HOTPLUG_GOOD_HEALTH_MASK
) {
435 if (!data
->attached
) {
437 * ASIC is in steady state. Connect associated
438 * device, if configured.
440 mlxreg_hotplug_device_create(priv
, data
, item
->kind
);
441 data
->attached
= true;
444 if (data
->attached
) {
446 * ASIC health is failed after ASIC has been
447 * in steady state. Disconnect associated
448 * device, if it has been connected.
450 mlxreg_hotplug_device_destroy(priv
, data
, item
->kind
);
451 data
->attached
= false;
452 data
->health_cntr
= 0;
455 item
->cache
= regval
;
457 /* Acknowledge event. */
458 ret
= regmap_write(priv
->regmap
, data
->reg
+
459 MLXREG_HOTPLUG_EVENT_OFF
, 0);
464 ret
= regmap_write(priv
->regmap
, data
->reg
+
465 MLXREG_HOTPLUG_MASK_OFF
, data
->mask
);
472 dev_err(priv
->dev
, "Failed to complete workqueue.\n");
476 * mlxreg_hotplug_work_handler - performs traversing of device interrupt
477 * registers according to the below hierarchy schema:
479 * Aggregation registers (status/mask)
480 * PSU registers: *---*
481 * *-----------------* | |
482 * |status/event/mask|-----> | * |
483 * *-----------------* | |
484 * Power registers: | |
485 * *-----------------* | |
486 * |status/event/mask|-----> | * |
487 * *-----------------* | |
488 * FAN registers: | |--> CPU
489 * *-----------------* | |
490 * |status/event/mask|-----> | * |
491 * *-----------------* | |
492 * ASIC registers: | |
493 * *-----------------* | |
494 * |status/event/mask|-----> | * |
495 * *-----------------* | |
498 * In case some system changed are detected: FAN in/out, PSU in/out, power
499 * cable attached/detached, ASIC health good/bad, relevant device is created
502 static void mlxreg_hotplug_work_handler(struct work_struct
*work
)
504 struct mlxreg_core_hotplug_platform_data
*pdata
;
505 struct mlxreg_hotplug_priv_data
*priv
;
506 struct mlxreg_core_item
*item
;
507 u32 regval
, aggr_asserted
;
511 priv
= container_of(work
, struct mlxreg_hotplug_priv_data
,
513 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
516 /* Mask aggregation event. */
517 ret
= regmap_write(priv
->regmap
, pdata
->cell
+
518 MLXREG_HOTPLUG_AGGR_MASK_OFF
, 0);
522 /* Read aggregation status. */
523 ret
= regmap_read(priv
->regmap
, pdata
->cell
, ®val
);
527 regval
&= pdata
->mask
;
528 aggr_asserted
= priv
->aggr_cache
^ regval
;
529 priv
->aggr_cache
= regval
;
532 * Handler is invoked, but no assertion is detected at top aggregation
533 * status level. Set aggr_asserted to mask value to allow handler extra
534 * run over all relevant signals to recover any missed signal.
536 if (priv
->not_asserted
== MLXREG_HOTPLUG_NOT_ASSERT
) {
537 priv
->not_asserted
= 0;
538 aggr_asserted
= pdata
->mask
;
543 /* Handle topology and health configuration changes. */
544 for (i
= 0; i
< pdata
->counter
; i
++, item
++) {
545 if (aggr_asserted
& item
->aggr_mask
) {
547 mlxreg_hotplug_health_work_helper(priv
, item
);
549 mlxreg_hotplug_work_helper(priv
, item
);
553 spin_lock_irqsave(&priv
->lock
, flags
);
556 * It is possible, that some signals have been inserted, while
557 * interrupt has been masked by mlxreg_hotplug_work_handler. In this
558 * case such signals will be missed. In order to handle these signals
559 * delayed work is canceled and work task re-scheduled for immediate
560 * execution. It allows to handle missed signals, if any. In other case
561 * work handler just validates that no new signals have been received
564 cancel_delayed_work(&priv
->dwork_irq
);
565 schedule_delayed_work(&priv
->dwork_irq
, 0);
567 spin_unlock_irqrestore(&priv
->lock
, flags
);
572 priv
->not_asserted
++;
573 /* Unmask aggregation event (no need acknowledge). */
574 ret
= regmap_write(priv
->regmap
, pdata
->cell
+
575 MLXREG_HOTPLUG_AGGR_MASK_OFF
, pdata
->mask
);
579 dev_err(priv
->dev
, "Failed to complete workqueue.\n");
582 static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data
*priv
)
584 struct mlxreg_core_hotplug_platform_data
*pdata
;
585 struct mlxreg_core_item
*item
;
586 struct mlxreg_core_data
*data
;
590 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
593 for (i
= 0; i
< pdata
->counter
; i
++, item
++) {
594 /* Clear group presense event. */
595 ret
= regmap_write(priv
->regmap
, item
->reg
+
596 MLXREG_HOTPLUG_EVENT_OFF
, 0);
601 * Verify if hardware configuration requires to disable
602 * interrupt capability for some of components.
605 for (j
= 0; j
< item
->count
; j
++, data
++) {
606 /* Verify if the attribute has capability register. */
607 if (data
->capability
) {
608 /* Read capability register. */
609 ret
= regmap_read(priv
->regmap
,
610 data
->capability
, ®val
);
614 if (!(regval
& data
->bit
))
615 item
->mask
&= ~BIT(j
);
619 /* Set group initial status as mask and unmask group event. */
620 if (item
->inversed
) {
621 item
->cache
= item
->mask
;
622 ret
= regmap_write(priv
->regmap
, item
->reg
+
623 MLXREG_HOTPLUG_MASK_OFF
,
630 /* Keep aggregation initial status as zero and unmask events. */
631 ret
= regmap_write(priv
->regmap
, pdata
->cell
+
632 MLXREG_HOTPLUG_AGGR_MASK_OFF
, pdata
->mask
);
636 /* Keep low aggregation initial status as zero and unmask events. */
637 if (pdata
->cell_low
) {
638 ret
= regmap_write(priv
->regmap
, pdata
->cell_low
+
639 MLXREG_HOTPLUG_AGGR_MASK_OFF
,
645 /* Invoke work handler for initializing hot plug devices setting. */
646 mlxreg_hotplug_work_handler(&priv
->dwork_irq
.work
);
650 dev_err(priv
->dev
, "Failed to set interrupts.\n");
651 enable_irq(priv
->irq
);
655 static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data
*priv
)
657 struct mlxreg_core_hotplug_platform_data
*pdata
;
658 struct mlxreg_core_item
*item
;
659 struct mlxreg_core_data
*data
;
662 pdata
= dev_get_platdata(&priv
->pdev
->dev
);
664 disable_irq(priv
->irq
);
665 cancel_delayed_work_sync(&priv
->dwork_irq
);
667 /* Mask low aggregation event, if defined. */
669 regmap_write(priv
->regmap
, pdata
->cell_low
+
670 MLXREG_HOTPLUG_AGGR_MASK_OFF
, 0);
672 /* Mask aggregation event. */
673 regmap_write(priv
->regmap
, pdata
->cell
+ MLXREG_HOTPLUG_AGGR_MASK_OFF
,
676 /* Clear topology configurations. */
677 for (i
= 0; i
< pdata
->counter
; i
++, item
++) {
679 /* Mask group presense event. */
680 regmap_write(priv
->regmap
, data
->reg
+ MLXREG_HOTPLUG_MASK_OFF
,
682 /* Clear group presense event. */
683 regmap_write(priv
->regmap
, data
->reg
+
684 MLXREG_HOTPLUG_EVENT_OFF
, 0);
686 /* Remove all the attached devices in group. */
688 for (j
= 0; j
< count
; j
++, data
++)
689 mlxreg_hotplug_device_destroy(priv
, data
, item
->kind
);
693 static irqreturn_t
mlxreg_hotplug_irq_handler(int irq
, void *dev
)
695 struct mlxreg_hotplug_priv_data
*priv
;
697 priv
= (struct mlxreg_hotplug_priv_data
*)dev
;
699 /* Schedule work task for immediate execution.*/
700 schedule_delayed_work(&priv
->dwork_irq
, 0);
705 static int mlxreg_hotplug_probe(struct platform_device
*pdev
)
707 struct mlxreg_core_hotplug_platform_data
*pdata
;
708 struct mlxreg_hotplug_priv_data
*priv
;
709 struct i2c_adapter
*deferred_adap
;
712 pdata
= dev_get_platdata(&pdev
->dev
);
714 dev_err(&pdev
->dev
, "Failed to get platform data.\n");
718 /* Defer probing if the necessary adapter is not configured yet. */
719 deferred_adap
= i2c_get_adapter(pdata
->deferred_nr
);
721 return -EPROBE_DEFER
;
722 i2c_put_adapter(deferred_adap
);
724 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
729 priv
->irq
= pdata
->irq
;
731 priv
->irq
= platform_get_irq(pdev
, 0);
736 priv
->regmap
= pdata
->regmap
;
737 priv
->dev
= pdev
->dev
.parent
;
740 err
= devm_request_irq(&pdev
->dev
, priv
->irq
,
741 mlxreg_hotplug_irq_handler
, IRQF_TRIGGER_FALLING
742 | IRQF_SHARED
, "mlxreg-hotplug", priv
);
744 dev_err(&pdev
->dev
, "Failed to request irq: %d\n", err
);
748 disable_irq(priv
->irq
);
749 spin_lock_init(&priv
->lock
);
750 INIT_DELAYED_WORK(&priv
->dwork_irq
, mlxreg_hotplug_work_handler
);
751 dev_set_drvdata(&pdev
->dev
, priv
);
753 err
= mlxreg_hotplug_attr_init(priv
);
755 dev_err(&pdev
->dev
, "Failed to allocate attributes: %d\n",
760 priv
->hwmon
= devm_hwmon_device_register_with_groups(&pdev
->dev
,
761 "mlxreg_hotplug", priv
, priv
->groups
);
762 if (IS_ERR(priv
->hwmon
)) {
763 dev_err(&pdev
->dev
, "Failed to register hwmon device %ld\n",
764 PTR_ERR(priv
->hwmon
));
765 return PTR_ERR(priv
->hwmon
);
768 /* Perform initial interrupts setup. */
769 mlxreg_hotplug_set_irq(priv
);
770 priv
->after_probe
= true;
775 static void mlxreg_hotplug_remove(struct platform_device
*pdev
)
777 struct mlxreg_hotplug_priv_data
*priv
= dev_get_drvdata(&pdev
->dev
);
779 /* Clean interrupts setup. */
780 mlxreg_hotplug_unset_irq(priv
);
781 devm_free_irq(&pdev
->dev
, priv
->irq
, priv
);
784 static struct platform_driver mlxreg_hotplug_driver
= {
786 .name
= "mlxreg-hotplug",
788 .probe
= mlxreg_hotplug_probe
,
789 .remove
= mlxreg_hotplug_remove
,
792 module_platform_driver(mlxreg_hotplug_driver
);
794 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
795 MODULE_DESCRIPTION("Mellanox regmap hotplug platform driver");
796 MODULE_LICENSE("Dual BSD/GPL");
797 MODULE_ALIAS("platform:mlxreg-hotplug");