# 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 from __future__ import division from __future__ import print_function from __future__ import unicode_literals from soc_gpio import soc_get_register import openbmc_gpio import logging import os import sys 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 if len(self.bits) > 1: raise NotSmartEnoughException('Not able to unsatisfy ' 'multi-bits equal') bit = self.bits[0] reg = soc_get_register(self.register) value = self.value 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.iteritems(): for func in funcs: assert func.name not in self.functions self.functions[func.name] = 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 == 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) 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_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: openbmc_gpio.gpio_export(gpio.gpio, gpio.shadow) if gpio.value == GPIO_INPUT: continue elif gpio.value == GPIO_OUT_HIGH: openbmc_gpio.gpio_set(gpio.gpio, 1) elif gpio.value == GPIO_OUT_LOW: openbmc_gpio.gpio_set(gpio.gpio, 0) else: raise Exception('Invalid value "%s"' % gpio.value)