blob: 3c71b755bdf5bb587bcc6b0d71fbff90b65faedf [file] [log] [blame]
#!/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)