> -------- Original Message -------- > Subject: Hid over I2C and ACPI interaction > Date: Wed, 4 Jul 2012 15:46:35 +0200 > From: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxx> > To: Jean Delvare <khali@xxxxxxxxxxxx>, Ben Dooks <ben-linux@xxxxxxxxx>, Wolfram > Sang <w.sang@xxxxxxxxxxxxxx>, Len Brown <lenb@xxxxxxxxxx>, > <linux-acpi@xxxxxxxxxxxxxxx>, <linux-i2c@xxxxxxxxxxxxxxx>, > <linux-kernel@xxxxxxxxxxxxxxx> > CC: Jiri Kosina <jkosina@xxxxxxx>, Stéphane Chatty <chatty@xxxxxxx>, JJ Ding > <jj_ding@xxxxxxxxxx> > > Hi Guys, > > I'm the co-author and the maintainer of the hid-multitouch driver. To > support even more devices, I started the implementation of the HID > over I2C protocol specification which is introduced by Win8. I'm quite > comfortable with the hid and the I2C part, but I'm blocked with the > interaction with the ACPI for the pnp part. > > I wanted to have your advice/help on this problem. I've add in the > recipients list the maintainers of i2c and ACPI, sorry for the noise > if you don't feel concerned about this. > > So, let's go deeper in the problem ;-) > Microsoft's spec asks the OEM to fill the ACPI DSDT to provide the > following scope in the ASL layout: > > >>>>>>>>> begin of ASL > Scope (\_SB) { > //-------------------- > // General Purpose I/O, ports 0...127 > //-------------------- > > Device(HIDI2C_DEVICE1) { > Name(_ADR,0) > Name (_HID, "MSFT1234”) > Name (_CID, "PNP0C50") > Name (_UID, 3) > > Method(_CRS, 0x0, NotSerialized) > { > Name (RBUF, ResourceTemplate () > { > // Address 0x07 on I2C-X (OEM selects this address) > //IHV SPECIFIC I2C3 = I2C Controller; TGD0 = GPIO Controller; > I2CSerialBus (0x07, ControllerInitiated, > 100000,AddressingMode7Bit, "\\_SB.I2C3",,,,) > GpioInt(Level, ActiveLow, Exclusive, PullUp, 0, "\\_SB. TGD0", > 0 , ResourceConsumer, , ) {40} > }) > Return(RBUF) > } > > Method(_DSM, 0x4, NotSerialized) > { > // BreakPoint > Store ("Method _DSM begin", Debug) > > // DSM UUID > switch(ToBuffer(Arg0)) > { > // ACPI DSM UUID for HIDI2C > case(ToUUID("3CDFF6F7-4267-4555-AD05-B30A3D8938DE")) > { > // DSM function which returns the HID Descriptor > Address (skipped) > } > > default > { > // No other GUIDs supported > Return(Buffer(One) { 0x00 }) > } > } > } > } > <<<<<<<<< end of ASL > yep, this is an ACPI enumerated I2C controller. > Summary: > - a HID over I2C device has to present the Compatibility ID "PNP0C50" > - in the _CRS block, the address, the adapter and the gpioInt are > defined (or referenced) > - it presents a Device Specific Method (_DSM) which returns the HID > Descriptor register address. This register is our entry point for > retrieving the information about our hid device (so it's mandatory to > obtain it). > > Where am I: > - I've written a first layer on top of i2c that retrieves the hid > register (currently the address 0x0001 is hardcoded), then get the > report desccriptors and the input events, and forward all this stuff > to the hid layer. > - It's working with a custom emulated HID over i2c touchpad, while > waiting for the one a manufacturer should send to me. > - The detection and the addition to the adapter is done by adding the > address in the lists and the name through the i2c "->detect" callback > (which is not very good, because I don't have the interrupt line > there). > - I've written a first acpi implementation that rely on the > DEVICE_ACPI_HANDLE macro to get the ACPI handle of the device (if > available). > - I'm not able to do some tests with the ACPI, as I don't know how to > implement this DSDT on my computer (I'm missing the I2C part), and the > manufacturer returned the mainboard with the right DSDT to the OEM. > > My questions: > - will the current acpi implementation handle I2C devices? you still need to write your own device driver for the device. > - it seems to me that the .archdata field is left blank during the i2c > device initialization in all paths I've seen. Is that true? > - who puts the name int the struct i2c_board_info? (for hot-plugged > i2c devices). > > - finally, what is the best way of handling ACPI for those I2C devices: > 1) everything is fine, I should have the ACPI handle in .archdata. > 2) someone has to implement the handling of I2C in the pnpACPI layer > (by adding I2CSerialBus handling and creating there the i2c slave). > 3) I should create an acpi driver which handles "PNP0C50" and which > creates the i2c slaves. > exactly. As this I2C controller uses the GPIO interrupt, we need an ACPI GPIO controller driver for interrupts first. I already have such a patch in hand, but have not release it for some reason. Second, you need to write your own PNP I2C controller driver, to enumerate the I2C controller via ACPI, AND enumerate the I2C slave devices under this controller to I2C bus. I also have a similar driver for SPI controller and SD/MMC controller. Third, you need a I2C slave device driver to handle the I2C slave device in I2C bus. here is a BKM I wrote, hope it helps. And also any comments are welcome. :) >From 0a0fa4ff7b4b06c6560de94a78b15c6adfd86e34 Mon Sep 17 00:00:00 2001 From: Zhang Rui <rui.zhang@xxxxxxxxx> Date: Mon, 26 Dec 2011 10:42:04 +0800 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-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html