Index: src/platform/utils/gpio_setup.py |
diff --git a/src/platform/utils/gpio_setup.py b/src/platform/utils/gpio_setup.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..3c71b755bdf5bb587bcc6b0d71fbff90b65faedf |
--- /dev/null |
+++ b/src/platform/utils/gpio_setup.py |
@@ -0,0 +1,230 @@ |
+#!/usr/bin/python |
+ |
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""A script to create symlinks to platform specific GPIO pins. |
+ |
+This script creates a set of symlinks pointing at the sys fs files returning |
+the appropriate GPIO pin values. Each symlink is named to represent the actual |
+GPIO pin function. |
+ |
+The location of the symlinks generated by this script can be specified using |
+the --symlink_root command line option. By default /home/gpio directory is |
+used. The symlink directory must exist before this script is run. |
+ |
+The GPIO pins' values are available through a GPIO device present in sys fs. |
+The device is identified by its PCI bus address. The default PCI address of |
+the GPIO device (set to 0:0:1f.0), can be changed using the --pci_address |
+command line option. |
+ |
+The platform specific bit usage of the GPIO device is derived from the ACPI, |
+also using files found in a fixed location in sys fs. The default location of |
+/sys/bus/platform/devices/chromeos_acpi could be changed using the --acpi_root |
+command line option. |
+ |
+Each GPIO pin is represented through ACPI as a subdirectory with several files |
+in it. A typical name of the GPIO pin file looks as follows: |
+ |
+<acpi_root>/GPIO.<instance>/GPIO.[0-3] |
+ |
+where <instance> is a zero based number assigned to this GPIO pin by the BIOS. |
+ |
+In particular, file GPIO.0 represents encoded pin signal type (from which the |
+symlink name is derived), and file GPIO.2 represents the actual zero based |
+GPIO pin number within this GPIO device range. |
+ |
+This script reads the ACPI provided mapping, enables the appropriate GPIO pins |
+and creates symlinks mapping these GPIOs' values. |
+""" |
+ |
+__author__ = 'The Chromium OS Authors' |
+ |
+import glob |
+import os |
+import optparse |
+import sys |
+ |
+GPIO_ROOT = '/sys/class/gpio' |
+GPIO_DEVICE_ROOT = GPIO_ROOT + '/gpiochip' |
+GPIO_ENABLE_FILE = '/sys/class/gpio/' + 'export' |
+ |
+# Can be changed using --pci_address command line option. |
+GPIO_DEVICE_PCI_ADDRESS = '0000:00:1f.0' |
+ |
+# Can be changed using --acpi_root command line option. |
+ACPI_ROOT = '/sys/bus/platform/devices/chromeos_acpi' |
+GPIO_SIGNAL_TYPE_EXTENSION = '0' |
+GPIO_PIN_NUMBER_EXTENSION = '2' |
+ |
+# can be changed using --symlink_root command line option. |
+DEFAULT_SYMLINK_ROOT = '/home/gpio' |
+ |
+# This dictionary maps GPIO signal types codes into their actual names. |
+GPIO_SIGNAL_TYPES = { |
+ 1: 'recovery_button', |
+ 2: 'developer_switch', |
+ 3: 'write_protect' |
+ } |
+ |
+# Debug header signal type codes are offset by 0x100, the tuple below |
+# represents the range of valid codes for the debug header. The range starts |
+# at 0x100 and is 0x100 wide. |
+GPIO_DEBUG_HEADER_RANGE = (0x100, 0x100) |
+ |
+# This is used to prepare the option parser: each element is a tuple of |
+# strings, including the option name and the option default value. |
+option_list = (('symlink_root', DEFAULT_SYMLINK_ROOT), |
+ ('pci_address', GPIO_DEVICE_PCI_ADDRESS), |
+ ('acpi_root', ACPI_ROOT)) |
+ |
+# This becomes the option object after the command line is parsed. |
+cmd_line_options = None |
+ |
+class GpioSetupError(Exception): |
+ pass |
+ |
+ |
+class GpioChip(object): |
+ """Represent GPIO chip available through sys fs. |
+ |
+ Attributes: |
+ pci_address: a string, PCI address of this GPIO device |
+ base: a number, base global GPIO number of this device (mapped to pin zero |
+ in the device range) |
+ capacity: a number, shows the number of GPIO pins of this device. |
+ description: a multiline string description of this device, initialized |
+ after the device is attached. Can be used to dump device |
+ information. |
+ """ |
+ |
+ def __init__(self, pci_address): |
+ self.pci_address = pci_address |
+ self.base = 0 |
+ self.capacity = 0 |
+ self.description = 'not attached' |
+ |
+ def Attach(self): |
+ for f in glob.glob(GPIO_DEVICE_ROOT + '*/label'): |
+ label = open(f).read().strip() |
+ if label == self.pci_address: |
+ break |
+ else: |
+ raise GpioSetupError( |
+ 'could not find GPIO PCI device %s' % self.pci_address) |
+ directory = os.path.dirname(f) |
+ self.base = int(open(directory + '/base').read()) |
+ self.capacity = int(open(directory + '/ngpio').read()) |
+ self.description = '\n'.join(['GPIO device at PCI address %s' % |
+ self.pci_address, |
+ 'Base gpio pin %d' % self.base, |
+ 'Capacity %d' % self.capacity]) |
+ |
+ def EnablePin(self, pin): |
+ """Enable a certain GPIO pin. |
+ |
+ To enable the pin one needs to write its global GPIO number into |
+ /sys/class/gpio/export, if this pin has not been enabled yet. |
+ |
+ Inputs: |
+ pin: a number, zero based pin number within this device's range. |
+ """ |
+ |
+ if pin >= self.capacity: |
+ raise GpioSetupError('pin %d exceeds capacity of %d' % ( |
+ pin, self.capacity)) |
+ global_gpio_number = self.base + pin |
+ if not os.path.exists('%s/gpio%d' % (GPIO_ROOT, global_gpio_number)): |
+ open(GPIO_ENABLE_FILE, 'w').write('%d' % (global_gpio_number)) |
+ |
+ def __str__(self): |
+ return self.description |
+ |
+ |
+def ParseAcpiMappings(): |
+ """Scan ACPI information about GPIO and generate a mapping. |
+ |
+ Returns: a list of tuples, each tuple consisting of a string representing |
+ the GPIO pin name and a number, representing the GPIO pin within |
+ the GPIO device space. |
+ """ |
+ acpi_gpio_mapping = [] |
+ for d in glob.glob('%s/GPIO.[0-9]*' % cmd_line_options.acpi_root): |
+ signal_type = int(open('%s/GPIO.%s' % ( |
+ d, GPIO_SIGNAL_TYPE_EXTENSION)).read()) |
+ |
+ pin_number = int(open('%s/GPIO.%s' % ( |
+ d, GPIO_PIN_NUMBER_EXTENSION)).read()) |
+ |
+ if signal_type in GPIO_SIGNAL_TYPES: |
+ acpi_gpio_mapping.append((GPIO_SIGNAL_TYPES[signal_type], pin_number)) |
+ continue |
+ |
+ # This is not a specific signal, could be a debug header pin. |
+ debug_header = signal_type - GPIO_DEBUG_HEADER_RANGE[0] |
+ if debug_header >= 0 and debug_header < GPIO_DEBUG_HEADER_RANGE[1]: |
+ acpi_gpio_mapping.append(('debug_header_%d' % debug_header, pin_number)) |
+ continue |
+ |
+ # Unrecognized mapping, could happen if BIOS version is ahead of this |
+ # script. |
+ print 'unknown signal type encoding %d in %d' % (signal_type, d) |
+ |
+ if not acpi_gpio_mapping: |
+ raise GpioSetupError('no gpio mapping found. Is ACPI driver installed?') |
+ |
+ return acpi_gpio_mapping |
+ |
+ |
+def CreateGpioSymlinks(mappings, gpio, symlink_root): |
+ if not os.path.exists(symlink_root): |
+ raise GpioSetupError('%s does not exist' % symlink_root) |
+ |
+ if not os.path.isdir(symlink_root): |
+ raise GpioSetupError('%s is not a directory' % symlink_root) |
+ |
+ if not os.access(symlink_root, os.W_OK): |
+ raise GpioSetupError('%s is not writable' % symlink_root) |
+ |
+ try: |
+ os.chdir(symlink_root) |
+ except OSError: |
+ raise GpioSetupError('failed to change directory to %s' % symlink_root) |
+ |
+ for (symlink, pin) in mappings: |
+ gpio.EnablePin(pin) |
+ source_file = '%s/gpio%d/value' % (GPIO_ROOT, pin + gpio.base) |
+ if not os.path.exists(symlink): |
+ os.symlink(source_file, symlink) |
+ continue |
+ if not os.path.islink(symlink): |
+ raise GpioSetupError( |
+ '%s exists but is not a symlink' % os.path.abspath(symlink)) |
+ if os.readlink(symlink) != source_file: |
+ raise GpioSetupError( |
+ '%s points to a wrong file' % os.path.abspath(symlink)) |
+ |
+ |
+def ProcessOptions(): |
+ global cmd_line_options |
+ parser = optparse.OptionParser() |
+ for (name, default_value) in option_list: |
+ parser.add_option('--' + name, dest=name, default=default_value) |
+ (cmd_line_options, _) = parser.parse_args() |
+ |
+ |
+def main(): |
+ ProcessOptions() |
+ gpioc = GpioChip(cmd_line_options.pci_address) |
+ gpioc.Attach() |
+ CreateGpioSymlinks(ParseAcpiMappings(), gpioc, cmd_line_options.symlink_root) |
+ |
+ |
+if __name__ == '__main__': |
+ try: |
+ main() |
+ except GpioSetupError, e: |
+ print >> sys.stderr, '%s: %s' % (sys.argv[0].split('/')[-1], e) |
+ sys.exit(1) |
+ sys.exit(0) |