129 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ACPI PCI Hot Plug Extension for Ampere Altra. Allows control of4 * attention LEDs via requests to system firmware.5 *6 * Copyright (C) 2023 Ampere Computing LLC7 */8 9#define pr_fmt(fmt) "acpiphp_ampere_altra: " fmt10 11#include <linux/init.h>12#include <linux/module.h>13#include <linux/pci.h>14#include <linux/pci_hotplug.h>15#include <linux/platform_device.h>16 17#include "acpiphp.h"18 19#define HANDLE_OPEN 0xb020000020#define HANDLE_CLOSE 0xb030000021#define REQUEST 0xf070000022#define LED_CMD 0x0000000423#define LED_ATTENTION 0x0000000224#define LED_SET_ON 0x0000000125#define LED_SET_OFF 0x0000000226#define LED_SET_BLINK 0x0000000327 28static u32 led_service_id[4];29 30static int led_status(u8 status)31{32 switch (status) {33 case 1: return LED_SET_ON;34 case 2: return LED_SET_BLINK;35 default: return LED_SET_OFF;36 }37}38 39static int set_attention_status(struct hotplug_slot *slot, u8 status)40{41 struct arm_smccc_res res;42 struct pci_bus *bus;43 struct pci_dev *root_port;44 unsigned long flags;45 u32 handle;46 int ret = 0;47 48 bus = slot->pci_slot->bus;49 root_port = pcie_find_root_port(bus->self);50 if (!root_port)51 return -ENODEV;52 53 local_irq_save(flags);54 arm_smccc_smc(HANDLE_OPEN, led_service_id[0], led_service_id[1],55 led_service_id[2], led_service_id[3], 0, 0, 0, &res);56 if (res.a0) {57 ret = -ENODEV;58 goto out;59 }60 handle = res.a1 & 0xffff0000;61 62 arm_smccc_smc(REQUEST, LED_CMD, led_status(status), LED_ATTENTION,63 (PCI_SLOT(root_port->devfn) << 4) | (pci_domain_nr(bus) & 0xf),64 0, 0, handle, &res);65 if (res.a0)66 ret = -ENODEV;67 68 arm_smccc_smc(HANDLE_CLOSE, handle, 0, 0, 0, 0, 0, 0, &res);69 70 out:71 local_irq_restore(flags);72 return ret;73}74 75static int get_attention_status(struct hotplug_slot *slot, u8 *status)76{77 return -EINVAL;78}79 80static struct acpiphp_attention_info ampere_altra_attn = {81 .set_attn = set_attention_status,82 .get_attn = get_attention_status,83 .owner = THIS_MODULE,84};85 86static int altra_led_probe(struct platform_device *pdev)87{88 struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);89 int ret;90 91 ret = fwnode_property_read_u32_array(fwnode, "uuid", led_service_id, 4);92 if (ret) {93 dev_err(&pdev->dev, "can't find uuid\n");94 return ret;95 }96 97 ret = acpiphp_register_attention(&ere_altra_attn);98 if (ret) {99 dev_err(&pdev->dev, "can't register driver\n");100 return ret;101 }102 return 0;103}104 105static void altra_led_remove(struct platform_device *pdev)106{107 acpiphp_unregister_attention(&ere_altra_attn);108}109 110static const struct acpi_device_id altra_led_ids[] = {111 { "AMPC0008", 0 },112 { }113};114MODULE_DEVICE_TABLE(acpi, altra_led_ids);115 116static struct platform_driver altra_led_driver = {117 .driver = {118 .name = "ampere-altra-leds",119 .acpi_match_table = altra_led_ids,120 },121 .probe = altra_led_probe,122 .remove_new = altra_led_remove,123};124module_platform_driver(altra_led_driver);125 126MODULE_AUTHOR("D Scott Phillips <scott@os.amperecomputing.com>");127MODULE_DESCRIPTION("ACPI PCI Hot Plug Extension for Ampere Altra");128MODULE_LICENSE("GPL");129