![Hans de Goede](/assets/img/avatar_default.png)
Many HP AMD based laptops contain an SMB0001 device like this: Device (SMBD) { Name (_HID, "SMB0001") // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0B20, // Range Minimum 0x0B20, // Range Maximum 0x20, // Alignment 0x20, // Length ) IRQ (Level, ActiveLow, Shared, ) {7} }) } The legacy style IRQ resource here causes acpi_dev_get_irqresource() to be called with legacy=true and this message to show in dmesg: ACPI: IRQ 7 override to edge, high This causes issues when later on the AMD0030 GPIO device gets enumerated: Device (GPIO) { Name (_HID, "AMDI0030") // _HID: Hardware ID Name (_CID, "AMDI0030") // _CID: Compatible ID Name (_UID, Zero) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (RBUF, ResourceTemplate () { Interrupt (ResourceConsumer, Level, ActiveLow, Shared, ,, ) { 0x00000007, } Memory32Fixed (ReadWrite, 0xFED81500, // Address Base 0x00000400, // Address Length ) }) Return (RBUF) /* \_SB_.GPIO._CRS.RBUF */ } } Now acpi_dev_get_irqresource() gets called with legacy=false, but because of the earlier override of the trigger-type acpi_register_gsi() returns -EBUSY (because we try to register the same interrupt with a different trigger-type) and we end up setting IORESOURCE_DISABLED in the flags. The setting of IORESOURCE_DISABLED causes platform_get_irq() to call acpi_irq_get() which is not implemented on x86 and returns -EINVAL. resulting in the following in dmesg: amd_gpio AMDI0030:00: Failed to get gpio IRQ: -22 amd_gpio: probe of AMDI0030:00 failed with error -22 The SMB0001 is a "virtual" device in the sense that the only way the OS interacts with it is through calling a couple of methods to do SMBus transfers. As such it is weird that it has IO and IRQ resources at all, because the driver for it is not expected to ever access the hardware directly. The Linux driver for the SMB0001 device directly binds to the acpi_device through the acpi_bus, so we do not need to instantiate a platform_device for this ACPI device. This commit adds the SMB0001 HID to the forbidden_id_list, avoiding the instantiating of a platform_device for it. Not instantiating a platform_device means we will no longer call acpi_dev_get_irqresource() for the legacy IRQ resource fixing the probe of the AMDI0030 device failing. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1644013 BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=198715 BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199523 Reported-by: Lukas Kahnert <openproggerfreak@gmail.com> Tested-by: Marc <suaefar@googlemail.com> Cc: All applicable <stable@vger.kernel.org> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
136 lines
4 KiB
C
136 lines
4 KiB
C
/*
|
|
* ACPI support for platform bus type.
|
|
*
|
|
* Copyright (C) 2012, Intel Corporation
|
|
* Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
* Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "internal.h"
|
|
|
|
ACPI_MODULE_NAME("platform");
|
|
|
|
static const struct acpi_device_id forbidden_id_list[] = {
|
|
{"PNP0000", 0}, /* PIC */
|
|
{"PNP0100", 0}, /* Timer */
|
|
{"PNP0200", 0}, /* AT DMA Controller */
|
|
{"ACPI0009", 0}, /* IOxAPIC */
|
|
{"ACPI000A", 0}, /* IOAPIC */
|
|
{"SMB0001", 0}, /* ACPI SMBUS virtual device */
|
|
{"", 0},
|
|
};
|
|
|
|
static void acpi_platform_fill_resource(struct acpi_device *adev,
|
|
const struct resource *src, struct resource *dest)
|
|
{
|
|
struct device *parent;
|
|
|
|
*dest = *src;
|
|
|
|
/*
|
|
* If the device has parent we need to take its resources into
|
|
* account as well because this device might consume part of those.
|
|
*/
|
|
parent = acpi_get_first_physical_node(adev->parent);
|
|
if (parent && dev_is_pci(parent))
|
|
dest->parent = pci_find_resource(to_pci_dev(parent), dest);
|
|
}
|
|
|
|
/**
|
|
* acpi_create_platform_device - Create platform device for ACPI device node
|
|
* @adev: ACPI device node to create a platform device for.
|
|
* @properties: Optional collection of build-in properties.
|
|
*
|
|
* Check if the given @adev can be represented as a platform device and, if
|
|
* that's the case, create and register a platform device, populate its common
|
|
* resources and returns a pointer to it. Otherwise, return %NULL.
|
|
*
|
|
* Name of the platform device will be the same as @adev's.
|
|
*/
|
|
struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
|
|
struct property_entry *properties)
|
|
{
|
|
struct platform_device *pdev = NULL;
|
|
struct platform_device_info pdevinfo;
|
|
struct resource_entry *rentry;
|
|
struct list_head resource_list;
|
|
struct resource *resources = NULL;
|
|
int count;
|
|
|
|
/* If the ACPI node already has a physical device attached, skip it. */
|
|
if (adev->physical_node_count)
|
|
return NULL;
|
|
|
|
if (!acpi_match_device_ids(adev, forbidden_id_list))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
INIT_LIST_HEAD(&resource_list);
|
|
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
|
if (count < 0) {
|
|
return NULL;
|
|
} else if (count > 0) {
|
|
resources = kcalloc(count, sizeof(struct resource),
|
|
GFP_KERNEL);
|
|
if (!resources) {
|
|
dev_err(&adev->dev, "No memory for resources\n");
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
count = 0;
|
|
list_for_each_entry(rentry, &resource_list, node)
|
|
acpi_platform_fill_resource(adev, rentry->res,
|
|
&resources[count++]);
|
|
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
}
|
|
|
|
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
|
/*
|
|
* If the ACPI node has a parent and that parent has a physical device
|
|
* attached to it, that physical device should be the parent of the
|
|
* platform device we are about to create.
|
|
*/
|
|
pdevinfo.parent = adev->parent ?
|
|
acpi_get_first_physical_node(adev->parent) : NULL;
|
|
pdevinfo.name = dev_name(&adev->dev);
|
|
pdevinfo.id = -1;
|
|
pdevinfo.res = resources;
|
|
pdevinfo.num_res = count;
|
|
pdevinfo.fwnode = acpi_fwnode_handle(adev);
|
|
pdevinfo.properties = properties;
|
|
|
|
if (acpi_dma_supported(adev))
|
|
pdevinfo.dma_mask = DMA_BIT_MASK(32);
|
|
else
|
|
pdevinfo.dma_mask = 0;
|
|
|
|
pdev = platform_device_register_full(&pdevinfo);
|
|
if (IS_ERR(pdev))
|
|
dev_err(&adev->dev, "platform device creation failed: %ld\n",
|
|
PTR_ERR(pdev));
|
|
else {
|
|
set_dev_node(&pdev->dev, acpi_get_node(adev->handle));
|
|
dev_dbg(&adev->dev, "created platform device %s\n",
|
|
dev_name(&pdev->dev));
|
|
}
|
|
|
|
kfree(resources);
|
|
|
|
return pdev;
|
|
}
|
|
EXPORT_SYMBOL_GPL(acpi_create_platform_device);
|