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 | const char *hid; |
22 | }; |
23 | |
24 | static void pwr_mlxbf_reboot_work(struct work_struct *work) |
25 | { |
26 | acpi_bus_generate_netlink_event("button/reboot.*" , "Reboot Button" , 0x80, 1); |
27 | } |
28 | |
29 | static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) |
30 | { |
31 | const char *rst_pwr_hid = "MLNXBF24" ; |
32 | const char *shutdown_hid = "MLNXBF29" ; |
33 | struct pwr_mlxbf *priv = ptr; |
34 | |
35 | if (!strncmp(priv->hid, rst_pwr_hid, 8)) |
36 | schedule_work(work: &priv->reboot_work); |
37 | |
38 | if (!strncmp(priv->hid, shutdown_hid, 8)) |
39 | orderly_poweroff(force: true); |
40 | |
41 | return IRQ_HANDLED; |
42 | } |
43 | |
44 | static int pwr_mlxbf_probe(struct platform_device *pdev) |
45 | { |
46 | struct device *dev = &pdev->dev; |
47 | struct acpi_device *adev; |
48 | struct pwr_mlxbf *priv; |
49 | const char *hid; |
50 | int irq, err; |
51 | |
52 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
53 | if (!priv) |
54 | return -ENOMEM; |
55 | |
56 | adev = ACPI_COMPANION(dev); |
57 | if (!adev) |
58 | return -ENXIO; |
59 | |
60 | hid = acpi_device_hid(device: adev); |
61 | priv->hid = hid; |
62 | |
63 | irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), index: 0); |
64 | if (irq < 0) |
65 | return dev_err_probe(dev, err: irq, fmt: "Error getting %s irq.\n" , priv->hid); |
66 | |
67 | err = devm_work_autocancel(dev, w: &priv->reboot_work, worker: pwr_mlxbf_reboot_work); |
68 | if (err) |
69 | return err; |
70 | |
71 | err = devm_request_irq(dev, irq, handler: pwr_mlxbf_irq, irqflags: 0, devname: hid, dev_id: priv); |
72 | if (err) |
73 | dev_err(dev, "Failed request of %s irq\n" , priv->hid); |
74 | |
75 | return err; |
76 | } |
77 | |
78 | static const struct acpi_device_id __maybe_unused pwr_mlxbf_acpi_match[] = { |
79 | { "MLNXBF24" , 0 }, |
80 | { "MLNXBF29" , 0 }, |
81 | {}, |
82 | }; |
83 | MODULE_DEVICE_TABLE(acpi, pwr_mlxbf_acpi_match); |
84 | |
85 | static struct platform_driver pwr_mlxbf_driver = { |
86 | .driver = { |
87 | .name = "pwr_mlxbf" , |
88 | .acpi_match_table = pwr_mlxbf_acpi_match, |
89 | }, |
90 | .probe = pwr_mlxbf_probe, |
91 | }; |
92 | |
93 | module_platform_driver(pwr_mlxbf_driver); |
94 | |
95 | MODULE_DESCRIPTION("Mellanox BlueField power driver" ); |
96 | MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>" ); |
97 | MODULE_LICENSE("Dual BSD/GPL" ); |
98 | |