common/recipes-utils/openbmc-gpio/files/openbmc-gpio-1/openbmc_gpio_table.py (244 lines of code) (raw):
#!/usr/bin/env python3
# Copyright 2015-present Facebook. All rights reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
from __future__ import absolute_import, division, print_function, unicode_literals
import logging
import os
import sys
import openbmc_gpio
import phymemory
from soc_gpio import soc_get_register, soc_get_tolerance_reg
class NotSmartEnoughException(Exception):
"""There are few cases the code cannot make good decision on how to configure
the registers automatically. In such cases, this exception is thrown.
"""
pass
class ConfigUnknownFunction(Exception):
"""Unknown function to configure exception"""
pass
class BitsEqual(object):
def __init__(self, register, bits, value):
self.register = register
self.bits = bits
self.value = value
def __str__(self):
return "%s[%s]==0x%x" % (
str(soc_get_register(self.register)),
self.bits,
self.value,
)
def get_registers(self):
return set([self.register])
def check(self):
return soc_get_register(self.register).bits_value(self.bits) == self.value
def satisfy(self, **kwargs):
if BitsEqual.check(self):
return
reg = soc_get_register(self.register)
value = self.value
for bit in sorted(self.bits):
if value & 0x1 == 0x1:
reg.set_bit(bit, **kwargs)
else:
reg.clear_bit(bit, **kwargs)
value >>= 1
def unsatisfy(self, **kwargs):
if not BitsEqual.check(self):
return
bit = self.bits[0]
reg = soc_get_register(self.register)
value = self.value
for bit in sorted(self.bits):
if value & 0x1 == 0x1:
reg.clear_bit(bit, **kwargs)
else:
reg.set_bit(bit, **kwargs)
class BitsNotEqual(BitsEqual):
def __str__(self):
return "%s[%s]!=0x%x" % (
str(soc_get_register(self.register)),
self.bits,
self.value,
)
def check(self):
return not BitsEqual.check(self)
def satisfy(self, **kwargs):
BitsEqual.unsatisfy(self, **kwargs)
def unsatisfy(self, **kwargs):
BitsEqual.satisfy(self, **kwargs)
class AndOrBase(object):
def __init__(self, left, right):
self.left = left
self.right = right
def get_registers(self):
return self.left.get_registers() | self.right.get_registers()
def check(self):
raise Exception("This method must be implemented in subclass")
class And(AndOrBase):
def __str__(self):
return "AND(%s, %s)" % (str(self.left), str(self.right))
def check(self):
return self.left.check() and self.right.check()
def satisfy(self, **kwargs):
if self.check():
return
self.left.satisfy(**kwargs)
self.right.satisfy(**kwargs)
def unsatisfy(self, **kwargs):
if not self.check():
return
raise NotSmartEnoughException("Not able to unsatisfy an AND condition")
class Or(AndOrBase):
def __str__(self):
return "OR(%s, %s)" % (str(self.left), str(self.right))
def check(self):
return self.left.check() or self.right.check()
def satisfy(self, **kwargs):
if self.check():
return
raise NotSmartEnoughException("Not able to satisfy an OR condition")
def unsatisfy(self, **kwargs):
if not self.check():
return
self.left.unsatisfy(**kwargs)
self.right.unsatisfy(**kwargs)
class Function(object):
def __init__(self, name, condition=None):
self.name = name
self.condition = condition
def __str__(self):
return "Function('%s', %s)" % (self.name, str(self.condition))
class SocGPIOTable(object):
def __init__(self, gpio_table):
self.soc_gpio_table = gpio_table
self.registers = set([]) # all HW registers used for GPIO control
self.functions = {}
self._parse_gpio_table()
self._sync_from_hw()
def _parse_gpio_table(self):
# first get list of registers based on the SoC GPIO table
for pin, funcs in self.soc_gpio_table.items():
for func in funcs:
assert func.name not in self.functions
self.functions[func.name.split("/")[0]] = pin
if func.condition is not None:
self.registers |= func.condition.get_registers()
def _sync_from_hw(self):
# for each register, create an object and read the value from HW
for reg in self.registers:
soc_get_register(reg).read(refresh=True)
def write_to_hw(self):
for reg in self.registers:
soc_get_register(reg).write()
def config_function(self, func_name, write_through=True):
logging.debug('Configure function "%s"' % func_name)
if func_name not in self.functions:
# The function is not multi-function pin
raise ConfigUnknownFunction('Unknown function "%s" ' % func_name)
funcs = self.soc_gpio_table[self.functions[func_name]]
for func in funcs:
cond = func.condition
if func.name.startswith(func_name):
# this is the function we want to configure.
# if the condition is None, we are good to go,
# otherwiset, satisfy the condition
if cond is not None:
cond.satisfy(write_through=write_through)
break
else:
# this is not the funciton we want to configure.
# have to make this condition unsatisfied, so that we can go
# to the next function
assert cond is not None
cond.unsatisfy(write_through=write_through)
def _get_one_pin(self, pin, refresh):
if refresh:
self._sync_from_hw()
funcs = self.soc_gpio_table[pin]
active_func = None
all_funcs = []
for func in funcs:
cond = func.condition
all_funcs.append("%s:%s" % (func.name, str(cond)))
if active_func is None and (cond is None or cond.check()):
active_func = func.name
if active_func is None:
logging.error(
'Pin "%s" has no function set up. '
"All possibile functions are %s." % (pin, ", ".join(all_funcs))
)
return ("", "")
else:
desc = "%s => %s, functions: %s" % (pin, active_func, ", ".join(all_funcs))
return (active_func, desc)
def dump_pin(self, pin, out=sys.stdout, refresh=False):
if pin not in self.soc_gpio_table:
raise Exception('"%s" is not a valid pin' % pin)
_, desc = self._get_one_pin(pin, refresh)
out.write("%s\n" % desc)
def dump_function(self, func_name, out=sys.stdout, refresh=False):
if func_name not in self.functions:
raise Exception('"%s" is not a valid function name' % func_name)
pin = self.functions[func_name]
self.dump_pin(pin, out=out, refresh=refresh)
def dump_functions(self, out=sys.stdout, refresh=False):
if refresh:
self._sync_from_hw()
for pin in self.soc_gpio_table:
self.dump_pin(pin, out=out, refresh=False)
def get_active_functions(self, refresh=False):
if refresh:
self._sync_from_hw()
all = []
for pin in self.soc_gpio_table:
active, _ = self._get_one_pin(pin, False)
all.append(active.split("/")[0])
return all
GPIO_INPUT = "input"
GPIO_OUT_HIGH = "high"
GPIO_OUT_LOW = "low"
class BoardGPIO(object):
def __init__(self, gpio, shadow, value=GPIO_INPUT):
self.gpio = gpio
self.shadow = shadow
self.value = value
def setup_board_tolerance_gpio(
board_tolerance_gpio_table, board_gpioOffsetDic, validate=True
):
gpio_configured = []
regs = []
for gpio in board_tolerance_gpio_table:
if not (gpio.gpio).startswith("GPIO"):
continue
offset = openbmc_gpio.gpio_name_to_offset(gpio.gpio)
group_index = int(offset / 32)
phy_addr = board_gpioOffsetDic[str(group_index)]
bit = offset % 32
# config the devmem and write through to the hw
reg = soc_get_tolerance_reg(phy_addr)
reg.set_bit(bit)
regs.append(reg)
for reg in regs:
reg.write()
# export gpio
for gpio in board_tolerance_gpio_table:
openbmc_gpio.gpio_export(gpio.gpio, gpio.shadow)
def_val = openbmc_gpio.gpio_get_value(gpio.gpio, change_direction=False)
if def_val == 1:
openbmc_gpio.gpio_set_value(gpio.gpio, 1)
else:
openbmc_gpio.gpio_set_value(gpio.gpio, 0)
def setup_board_gpio(soc_gpio_table, board_gpio_table, validate=True):
soc = SocGPIOTable(soc_gpio_table)
gpio_configured = []
for gpio in board_gpio_table:
try:
soc.config_function(gpio.gpio, write_through=False)
gpio_configured.append(gpio.gpio)
except ConfigUnknownFunction as e:
# not multiple-function GPIO pin
pass
except NotSmartEnoughException as e:
logging.error(
'Failed to configure "%s" for "%s": %s'
% (gpio.gpio, gpio.shadow, str(e))
)
soc.write_to_hw()
if validate:
all_functions = set(soc.get_active_functions(refresh=True))
for gpio in gpio_configured:
if gpio not in all_functions:
raise Exception('Failed to configure function "%s"' % gpio)
for gpio in board_gpio_table:
if not (gpio.gpio).startswith("GPIO"):
continue
openbmc_gpio.gpio_export(gpio.gpio, gpio.shadow)
if gpio.value == GPIO_INPUT:
continue
elif gpio.value == GPIO_OUT_HIGH:
openbmc_gpio.gpio_set_value(gpio.gpio, 1)
elif gpio.value == GPIO_OUT_LOW:
openbmc_gpio.gpio_set_value(gpio.gpio, 0)
else:
raise Exception('Invalid value "%s"' % gpio.value)