[no subject]

From: Zhang Rui
Date: Sun Dec 25 2011 - 21:42:04 EST


As many SoC IP blocks are not hardware self-enumerable, the
firmware, aka, ACPI tables, is responsible for
enumerating/reserving/assigning system resources to these
devices. This tutorial talks about how to enumerate these
devices via ACPI namespace.

Signed-off-by: Zhang Rui <rui.zhang@xxxxxxxxx>
---
Documentation/acpi/acpi-device-probing.txt | 466
++++++++++++++++++++++++++++
1 file changed, 466 insertions(+)
create mode 100644 Documentation/acpi/acpi-device-probing.txt

diff --git a/Documentation/acpi/acpi-device-probing.txt
b/Documentation/acpi/acpi-device-probing.txt
new file mode 100644
index 0000000..82efbf3
--- /dev/null
+++ b/Documentation/acpi/acpi-device-probing.txt
@@ -0,0 +1,466 @@
+
+HOWTO enumerate devices via ACPI
+
+Copyright (c) 2011-2012 Intel Corporation
+
+Contrast to hardware self-enumerable devices(e.g. USB, PCI) on PC
platform,
+many SoC IP blocks can not be self enumerated.
+We used to introduce platform specific code for these devices.
+But now, with ACPI 5.0, there is no requirement for the hardware to be
+self-discoverable, enumerable or re-locatable, as the firmware is
responsible
+for enumerating/reserving/assigning system resources (such as address
ranges or
+interrupts) to the device.
+
+This document will show how to enumerate and configure a device via
ACPI.
+If you want to get more details about why and when we need this,
+please refer to ACPI spec 5.0 and
+Intel Architecture Platform Compatibility Definition.
+
+Note that although these are ACPI devices, we prefer to use PnP drivers
for them,
+this is because:
+1. all the non-ACPI-predefined Devices are exported as PnP devices as
well
+2. PnP bus is a well designed bus. Probing via PnP layer saves a lot of
work
+ for the device driver, e.g. getting & parsing ACPI resources.
+
+=============================================================================
+1. Understand device definition in ACPI namespace
+ [Case study 1] SD/MMC controller
+2. Driver for a leaf device
+ 2.1 Make a list of supported PnP ids
+ 2.2 Implement .probe/.remove callbacks for the PnP driver
+ 2.3 Fill in the pnp_driver structure
+ 2.4 Register the PnP driver
+3. Driver for a master device on a non-self-enumerable bus
+ [Case Study 2] SPI controller and its slave device
+ 3.1 Probe the master device
+ 3.2 Walk ACPI namesapce to get the child devices of the master
device
+ 3.3 Register these child devices as slave devices
+ 3.4 Write slave device driver
+4. Misc
+=============================================================================
+
+-----------------------------------------------------------------------------
+1. Understand device definition in ACPI namespace
+-----------------------------------------------------------------------------
+
+To enumerate a device in ACPI namespace, we need to find out and
understand
+HOW the device is defined in ACPI namespace first.
+
+[Case study 1 ] SD/MMC Controller
+
+Here is an ASL example code for SD/MMC controller definition in ACPI
namespace.
+
+ Device (EMMC)
+ {
+ Name (_ADR, Zero)
+ /* I use PNPXXXX, an arbitrary string, here, as PnP id is
device specific */
+ Name (_HID, "PNPXXXX")
+ Name (_CID, "PNPXXXX")
+ Name (_UID, 4)
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ Memory32Fixed (ReadWrite,
+ 0xFFA50000, // Address Base
+ 0x00000100, // Address Length
+ )
+ Interrupt (ResourceConsumer, Level, ActiveLow,
Exclusive, ,, )
+ {
+ 0x0000001b,
+ }
+ })
+ Return (RBUF)
+ }
+
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0F)
+ }
+ }
+
+_ADR : the address of this device on its parent bus. Useless in this
case.
+_HID : the PnP id for this device.
+_CID : the compatible PnP id. use this as the PnP id if _HID doesn't
exist.
+_CRS : the system resources currently allocated to this device.
+ the Memory32Fixed part shows an Mem space for the device,
+ and the Interrupt part shows the device interrupt.
+_STA : the current status of the device, e.g. it's
enabled/disabled/removed.
+
+By reading this example ASL code, we should know that there is a SD/MMC
controller
+on this platform, it's mem space base address is 0xFFA50000, length is
0x00000100,
+and the irq for this device is 0x1b.
+
+In Chapter 2, we will use this piece of ASL code as an example to
+show how to probe the SD/MMC controller via ACPI namespace.
+
+-----------------------------------------------------------------------------
+2 Driver for a leaf device
+-----------------------------------------------------------------------------
+
+2.1 Make a list of supported pnp ids.
+
+Use the string in _HID or _CID objects as the PnP ids so that the
device can
+be attached to the driver successfully.
+
+In this case,
+struct pnp_device_id sdhci_pnp_ids[] = {
+ { .id = "PNPXXXX",
+ .driver_data = (unsigned long)&sdhci_mfd_pdata },
+ { },
+};
+
+2.2 Implement the .probe and .remove callback of PnP driver.
+
+If you're not clear about what should be done in the driver, you can
consult
+some similar driver, for example, drivers/mmc/host/sdhci-pci.c shows
how
+to probe a PCI SD/MMC controller, this helps us understand what should
be done
+in the .probe/.remove callback.
+
+By reading the sdhci-pci .probe function, we know that the .probe
callback
+needs to
+a) alloc a sdhci host.
+b) fill the sdhci host structure with necessary resources got from
+ PCI configure space, including irq and mem space for the sdhci host.
+c) register the sdhci host.
+And then, driver/mmc/host/sdhci.c, the SDHCI interface driver will
handle
+everything for us.
+
+So, basically, we need to do the same work in sdhci_pnp_probe callback,
+except that we need to get the information from ACPI namesapce instead.
+
+To get the resources in _CRS, we do not need Linux ACPICA APIs as PnP
layer
+has done this for us already.
+
+pnp_irq() returns the device irq, which equals the "Interrupt" part in
_CRS method.
+pnp_get_resource(, IORESOURCE_MEM, 0) returns the first Mem space base
address
+and length of this device, which equals the "Memory32Fixed" Part of the
_CRS.
+
+the code below shows how to use the PnP APIs to get ACPI resources and
+register a sdhci host in the .probe callback.
+
+static int __devinit
+sdhci_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id
*dev_id)
+{
+...
+ pnp_disable_dev(pdev);
+ ret = pnp_activate_dev(pdev);
+...
+ iomem = pnp_get_resource(pdev, IORESOURCE_MEM, 0);
+...
+ host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pnp_dev));
+...
+ host->irq = pnp_irq(pdev, 0);
+...
+ if (!request_mem_region(iomem->start, resource_size(iomem),
+ mmc_hostname(host->mmc))) {
+...
+ host->ioaddr = ioremap_nocache(iomem->start,
resource_size(iomem));
+...
+ ret = sdhci_add_host(host);
+...
+ pnp_set_drvdata(pdev, sdhci);
+...
+}
+
+Once the .probe callback is done, we just need to release the resources
and
+unregister the host in the .remove callback.
+
+static void sdhci_pnp_remove(struct pnp_dev * pdev)
+{
+ struct sdhci_pnp_dev *sdhci = pnp_get_drvdata(pdev);
+ struct resources *iomem = pnp_get_resource(pdev, IORESOURCE_MEM, 0);
+...
+ sdhci_remove_host(sdhci->host, dead);
+ sdhci_free_host(sdhci->host);
+ iounmap(sdhci->host->ioaddr);
+ release_mem_region(iomem->start, resource_size(iomem));
+ pnp_set_drvdata(pdev, NULL);
+ pnp_disable_dev(pdev);
+}
+
+2.3 Fill in the pnp_driver structure
+
+Next step is to fill in the pnp_driver structure with PnP ids and
+.probe/.remove callbacks finished in section 2.1 and 2.2
+
+static struct pnp_driver sdhci_pnp_driver = {
+ .name = DRIVER_NAME,
+ .id_table = sdhci_pnp_ids,
+ .probe = sdhci_pnp_probe,
+ .remove = __devexit_p(sdhci_pnp_remove),
+};
+
+Note that .name and .id_table cannot be NULL.
+
+2.4 Register the PnP driver
+
+Now we can register this PnP driver to the driver model.
+
+static int __init sdhci_pnp_init(void)
+{
+ return pnp_register_driver(&sdhci_pnp_driver);
+}
+
+module_init(sdhci_pnp_init);
+
+
+-----------------------------------------------------------------------------
+3 Driver for a master device on a non-self-enumerable bus
+-----------------------------------------------------------------------------
+In some cases, enumerating via ACPI brings new requirements in the
driver.
+For example, the driver for a master device on a non-self-enumerable
bus is
+responsible for enumerating the slave devices on this bus as well,
which are
+described as child devices of this master device in ACPI namespace.
+
+Taking SPI bus for example,
+
+-------------------------------------------------------------------
+PNP/ACPI layer
+
+ spi-acpi driver
+ |
+ |-----------------|
+ | |
+ | |
+ V V
+ register itself register its children
+ as a master as slave devices
+ device |
+ | |
+---------|-----------------|---------------------------------------
+ | |
+ | |
+ | |
+ V V
+ -------------- -----------
+ | SPI | | SPI |
+ | master | | slave |
+ -------------- -----------
+ ^
+ |
+ |
+ V
+ -----------------------------
+ | SPI slave driver driver |
+ -----------------------------
+SPI Bus layer
+-------------------------------------------------------------------
+
+The figure above shows the components needed to make a SPI slave device
work
+a) an PNP/ACPI driver to probe the SPI master and its slaves.
+b) a SPI slave device driver for the SPI slave device.
+
+[Case Study 2] SPI controller and its slave device
+
+This piece of ASL code shows the definition of a SPI controller and its
slave device,
+MAX3110, in ACPI namespace.
+
+Device (SPI1) {
+ Name (_ADR, 0)
+ Name (_HID, "PNPYYYY")
+ Name (_CID, "PNPYYYY")
+ Name (_UID, 1)
+
+ Method (_CRS, 0x0, NotSerialized) {
+ Name (RBUF, ResourceTemplate ()
+ {
+ Memory32Fixed (ReadWrite, 0xff128400, 0x00000400)
+ Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive, , , )
{0x09}
+ })
+ Return (RBUF)
+ }
+
+ Method (_STA, 0x0, NotSerialized) {
+ Return(0xf)
+ }
+
+ Device(MAX0)
+ {
+ Name(_HID, "PNPZZZZ") // Max3110 serial port
+ Name(_DDN, "Max3110 serial port")
+ Method(_CRS, 0x0, NotSerialized)
+ {
+ // SpiSerial Bus Connection Descriptor
+ Name(UBUF, ResourceTemplate () {
+ SPISerialBus(
+ 1, // Device selection
+ PolarityHigh, // Device selection polarity
+ ThreeWireMode, // wiremode
+ 8, // databit len
+ ControllerInitiated, // slave mode
+ 1000, // Connection speed
+ ClockPolarityHigh, // Clock polarity
+ ClockPhaseFirst, // clock phase
+ "\\_SB.SPI1", // ResourceSource: SPI bus controller name
+ 0, // ResourceSourceIndex
+ ResourceConsumer, // Resource usage
+ , // DescriptorName: creates name for offset
of resource descriptor
+ ) // Vendor Data
+ // OUT pin, BT_EN pin Core GPIO 74
+ GpioIo(Exclusive, PullDefault, 0, 0, IoRestrictionOutputOnly, "\
\_SB.GPIS", ) {0x4A}
+ })
+
+ Return (UBUF)
+ }
+ }
+}
+
+By reading the ASL code, we can see that
+a) There is a SPI controller on this platform.
+ with IRQ 0x09, and a 0x400 bytes Memory space started from
0xff128400.
+b) a MAX3110 device is connect to a SPI controller.
+ all the information required for probing a SPI slave device is
described
+ in the "SPISerailBus" part of the MAX0._CRS method.
+
+We will talk about how to probe these two devices in this chapter.
+
+3.1 Probe the master device
+
+Please follow the Chapter 2 to probe the SPI master device.
+
+static int __devinit
+dw_spi_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id
*dev_id)
+{
+...
+ dws->paddr = pnp_mem_start(pdev, 0);
+ dws->iolen = pnp_mem_len(pdev, 0);
+ dws->irq = pnp_irq(pdev, 0);
+ dws->parent_dev = &pdev->dev;
+ dws->bus_num = index++;
+ dws->num_cs = 4;
+ dws->regs = ioremap_nocache((unsigned long)dws->paddr,
+ dws->iolen);
+...
+ ret = dw_spi_mid_init(dws);
+...
+ ret = dw_spi_add_host(dws);
+...
+}
+
+3.2 Walk ACPI namespace to probe all its child devices.
+
+As MAX3110 can not be enumerated automatically, we introduce
+dw_spi_pnp_slaves_register() to find the MAX3110 device in ACPI
namespace
+
+static int __devinit dw_spi_pnp_slaves_register(struct dw_spi_pnp*
dwpnp)
+{
+ ...
+ struct acpi_device *adev;
+ adev = dwpnp->pdev->data;
+
+ /*
+ * find spi child devices given in ACPI namespace, one lower level
only
+ */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, adev->handle, 1,
+ spi_slave_register, NULL,
+ spi_slave_info, NULL);
+ ...
+}
+
+3.3 Register its child devices as slave devices
+
+As spi_slave_register is invoked for each SPI1 child device,
+we introduce spi_slave_fill_resourcetry and try to register
+SPI slave devices in spi_slave_register.
+
+acpi_status __init spi_slave_register(acpi_handle spi_slave_handle, u32
level,
+ void* data, void** return_value)
+{
+ ...
+ struct spi_board_info *spi_slave_info;
+ ...
+ status = acpi_walk_resources(spi_slave_handle, METHOD_NAME__CRS,
+ spi_slave_fill_resource, data);
+ ...
+ /* register SPI slave device */
+ ret = spi_register_board_info(spi_slave_info, 1);
+ ...
+}
+
+acpi_status __devinit spi_slave_fill_resource(struct acpi_resource
*resource, void* data)
+{
+ struct spi_board_info *spi_slave_info;
+ struct acpi_resource_spi_serialbus *spi_resource;
+ ...
+ spi_resource = &resource->data.spi_serial_bus;
+ spi_slave_info->chip_select = spi_resource->device_selection;
+ spi_slave_info->max_speed_hz = spi_resource->connection_speed;
+ spi_slave_info->mode = (spi_resource->clock_phase ? SPI_CPHA : 0) |
+ (spi_resource->clock_polarity ? SPI_CPOL : 0) |
+ (spi_resource->device_polarity ? SPI_CS_HIGH : 0) |
+ (spi_resource->wire_mode ? SPI_3WIRE : 0);
+ ...
+}
+
+3.4 Write the slave device driver
+
+After 3.3 is done, the MAX3110 device is an slave device in the SPI
bus,
+but to make it work properly, we still need a SPI slave device driver.
+
+Note that this is a general SPI drivers independent of ACPI.
+
+We will not go into details of the slave device driver here as
+this piece of code is bus/device specific.
+
+-----------------------------------------------------------------------------
+4 Misc
+-----------------------------------------------------------------------------
+
+4.1 Note
+
+As ACPI 5.0 is still in heavily developing, if you are unable to find
out all the
+required information for probing a device in ACPI namespace, it is
possible
+that the ASL code is not well written.
+Please contact Zhang Rui <rui.zhang@xxxxxxxxx> with the acpidump output
of your
+platform attached if you suspect it's an BIOS problem.
+
+4.2 Some important ACPICA APIs for device driver implementation:
+
+-- acpi_status
+ acpi_walk_namespace(acpi_object_type type,
+ acpi_handle start_object,
+ u32 max_depth,
+ acpi_walk_callback pre_order_visit,
+ acpi_walk_callback post_order_visit,
+ void *context, void **return_value);
+Traverse ACPI namespace subtree rooted at start_object, go down
max_depth level
+at most. Call pre_order_visit when the proper node with type is found
the first
+time, call post_order_visit is the node is previously visited. Context
and
+return_value is passed down during the traverse.
+
+And the prototype of acpi_walk_callback:
+typedef
+acpi_status(*acpi_walk_callback) (acpi_handle object,
+ u32 nesting_level,
+ void *context, void **return_value);
+
+-- acpi_status
+ acpi_get_handle(acpi_handle parent,
+ acpi_string pathname, acpi_handle * ret_handle);
+Try to get handle with specified pathname under node parent. Usually
used to
+check whether a particular node is available or not.
+
+-- acpi_status
+ acpi_get_object_info(acpi_handle object,
+ struct acpi_device_info **return_buffer);
+Get acpi_device_info from object handle. Useful for retrieving ACPI
object
+name, type, and status etc.
+
+-- acpi_status
+ acpi_walk_resources(acpi_handle device,
+ char *name,
+ acpi_walk_resource_callback user_function, void *context);
+Traverse resource node specified by name(e.g. METHOD_NAME__CRS) in ACPI
+namespace subtree rooted at device. Call user_function for each entry
in
+acpi_resource list. The list may containe acpi_resource entries with
various
+types. So it is important to handle the interested resource type
properly.
+The acpi_resource with ACPI_RESOURCE_TYPE_END_TAG indicates
end-of-list.
+
+And the prototype of acpi_walk_resource_callback:
+typedef
+acpi_status(*acpi_walk_resource_callback) (struct acpi_resource *
resource,
+ void *context);
+
+More ACPICA external interfaces available in include/acpi/acpixf.h
--
1.7.10



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/