1 | // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause |
2 | |
3 | /* |
4 | * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. |
5 | */ |
6 | |
7 | #include <linux/acpi.h> |
8 | #include <linux/device.h> |
9 | #include <linux/devm-helpers.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/module.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/pm.h> |
16 | #include <linux/reboot.h> |
17 | #include <linux/types.h> |
18 | |
19 | struct pwr_mlxbf { |
20 | struct work_struct reboot_work; |
21 | struct work_struct shutdown_work; |
22 | const char *hid; |
23 | }; |
24 | |
25 | static void pwr_mlxbf_reboot_work(struct work_struct *work) |
26 | { |
27 | acpi_bus_generate_netlink_event("button/reboot.*" , "Reboot Button" , 0x80, 1); |
28 | } |
29 | |
30 | static void pwr_mlxbf_shutdown_work(struct work_struct *work) |
31 | { |
32 | acpi_bus_generate_netlink_event("button/power.*" , "Power Button" , 0x80, 1); |
33 | } |
34 | |
35 | static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) |
36 | { |
37 | const char *rst_pwr_hid = "MLNXBF24" ; |
38 | const char *low_pwr_hid = "MLNXBF29" ; |
39 | struct pwr_mlxbf *priv = ptr; |
40 | |
41 | if (!strncmp(priv->hid, rst_pwr_hid, 8)) |
42 | schedule_work(work: &priv->reboot_work); |
43 | |
44 | if (!strncmp(priv->hid, low_pwr_hid, 8)) |
45 | schedule_work(work: &priv->shutdown_work); |
46 | |
47 | return IRQ_HANDLED; |
48 | } |
49 | |
50 | static int pwr_mlxbf_probe(struct platform_device *pdev) |
51 | { |
52 | struct device *dev = &pdev->dev; |
53 | struct acpi_device *adev; |
54 | struct pwr_mlxbf *priv; |
55 | const char *hid; |
56 | int irq, err; |
57 | |
58 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
59 | if (!priv) |
60 | return -ENOMEM; |
61 | |
62 | adev = ACPI_COMPANION(dev); |
63 | if (!adev) |
64 | return -ENXIO; |
65 | |
66 | hid = acpi_device_hid(device: adev); |
67 | priv->hid = hid; |
68 | |
69 | irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), index: 0); |
70 | if (irq < 0) |
71 | return dev_err_probe(dev, err: irq, fmt: "Error getting %s irq.\n" , priv->hid); |
72 | |
73 | err = devm_work_autocancel(dev, w: &priv->shutdown_work, worker: pwr_mlxbf_shutdown_work); |
74 | if (err) |
75 | return err; |
76 | |
77 | err = devm_work_autocancel(dev, w: &priv->reboot_work, worker: pwr_mlxbf_reboot_work); |
78 | if (err) |
79 | return err; |
80 | |
81 | err = devm_request_irq(dev, irq, handler: pwr_mlxbf_irq, irqflags: 0, devname: hid, dev_id: priv); |
82 | if (err) |
83 | dev_err(dev, "Failed request of %s irq\n" , priv->hid); |
84 | |
85 | return err; |
86 | } |
87 | |
88 | static const struct acpi_device_id __maybe_unused pwr_mlxbf_acpi_match[] = { |
89 | { "MLNXBF24" , 0 }, |
90 | { "MLNXBF29" , 0 }, |
91 | {}, |
92 | }; |
93 | MODULE_DEVICE_TABLE(acpi, pwr_mlxbf_acpi_match); |
94 | |
95 | static struct platform_driver pwr_mlxbf_driver = { |
96 | .driver = { |
97 | .name = "pwr_mlxbf" , |
98 | .acpi_match_table = pwr_mlxbf_acpi_match, |
99 | }, |
100 | .probe = pwr_mlxbf_probe, |
101 | }; |
102 | |
103 | module_platform_driver(pwr_mlxbf_driver); |
104 | |
105 | MODULE_DESCRIPTION("Mellanox BlueField power driver" ); |
106 | MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>" ); |
107 | MODULE_LICENSE("Dual BSD/GPL" ); |
108 | |