eg25-control: refactor in advance of PinePhonePro having different GPIOs for powering the modem

This commit is contained in:
2024-10-13 00:48:08 +00:00
parent 06ee80bc40
commit cdfa8afcfc

View File

@@ -325,58 +325,81 @@ class GpioPhy:
# GPIO indices ("lines")
# DTR = "Data Terminal Ready", controls sleep/wakeup. high means modem is allowed to sleep when requested. high -> low forces the modem awake. internal pull-up.
# WAKEUP = 6
DTR = 34 # PB2
PWRKEY = 35 # PB3
RESET = 68 # PC4
APREADY = 231 # PH7, a.k.a. "host ready"
DISABLE = 232 # PH8, a.k.a. "enable"; active-low
# these are supplied by the subclass:
DTR = None, None
PWRKEY = None, None
RESET = None, None
APREADY = None, None
DISABLE = None, None
# STATUS line: HIGH means powered off
STATUS = 233 # PH9
STATUS = None, None
def __init__(self):
self.lines = gpiod.request_lines(
"/dev/gpiochip1",
consumer="eg25-control",
config={
GpioPhy.DTR: gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT),
GpioPhy.PWRKEY: gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT),
GpioPhy.RESET: gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT),
GpioPhy.APREADY: gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT),
GpioPhy.DISABLE: gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT),
GpioPhy.STATUS: gpiod.LineSettings(direction=gpiod.line.Direction.INPUT, bias=gpiod.line.Bias.PULL_UP),
},
gpios = dict(
DTR=self.DTR,
PWRKEY=self.PWRKEY,
RESET=self.RESET,
APREADY=self.APREADY,
DISABLE=self.DISABLE,
STATUS=self.STATUS,
)
num_lines = max(chip for (chip, _) in gpios.values())
line_configs = [dict() for _ in range(num_lines)]
line_configs[self.DTR[0]][self.DTR[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT)
line_configs[self.PWRKEY[0]][self.PWRKEY[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT)
line_configs[self.RESET[0]][self.RESET[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT)
line_configs[self.APREADY[0]][self.APREADY[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT)
line_configs[self.DISABLE[0]][self.DISABLE[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.OUTPUT)
line_configs[self.STATUS[0]][self.STATUS[1]] = gpiod.LineSettings(direction=gpiod.line.Direction.INPUT, bias=gpiod.line.Bias.PULL_UP)
self.lines = [None] * num_lines
for line, config in enumerate(line_configs):
if config:
self.lines[line] = gpiod.request_line(
f"/dev/gpiochip{line}",
consumer="eg25-control",
config=config,
)
def set_gpio(self, gpio, value) -> None:
line, index = gpio
self.lines[line].set_value(index, value)
def get_gpio(self, gpio):
line, index = gpio
return self.lines[line].get_value(index)
def power_toggle(self, disable = gpiod.line.Value.INACTIVE) -> None:
# power-on is signalled by toggling the PWRKEY, and the modem interprets that as either a power-up OR a power-down request.
self.dump_debug_info()
if self.lines.get_value(self.STATUS) == disable:
if self.get_gpio(self.STATUS) == disable:
# i wouldn't be surprised if the modem can get "stuck" in some state such that this early return always hits,
# if that's the case add some `--force` flag or verify against `mmcli -m any` report, etc.
logger.info("modem appears physically to already be in the desired state: not changing")
return
self.lines.set_value(self.APREADY, gpiod.line.Value.ACTIVE)
self.lines.set_value(self.DISABLE, gpiod.line.Value.INACTIVE)
self.lines.set_value(self.RESET, gpiod.line.Value.INACTIVE)
self.lines.set_value(self.PWRKEY, gpiod.line.Value.INACTIVE)
self.lines.set_value(self.DTR, gpiod.line.Value.INACTIVE)
self.set_gpio(self.APREADY, gpiod.line.Value.ACTIVE)
self.set_gpio(self.DISABLE, gpiod.line.Value.INACTIVE)
self.set_gpio(self.RESET, gpiod.line.Value.INACTIVE)
self.set_gpio(self.PWRKEY, gpiod.line.Value.INACTIVE)
self.set_gpio(self.DTR, gpiod.line.Value.INACTIVE)
# Megi's modem-power sleeps 50ms, cites datasheet claim to 30ms power-on
time.sleep(0.050)
self.dump_debug_info()
self.lines.set_value(self.PWRKEY, gpiod.line.Value.ACTIVE)
self.set_gpio(self.PWRKEY, gpiod.line.Value.ACTIVE)
# Megi's modem-power sleeps 200ms; eg25-manager sleeps 1.0s; EG25-HW 3.7 says 500ms+ for power-on, 650ms+ for power-off
time.sleep(1.0)
self.lines.set_value(self.PWRKEY, gpiod.line.Value.INACTIVE)
self.set_gpio(self.PWRKEY, gpiod.line.Value.INACTIVE)
# TODO: switch 'status' key to input (megi's modem-power claims it can be multiplexed with other stuff, so shouldn't be actively driven when possible)
# power-up takes 3-5s; power-down takes 12-15s
for i in range(20):
self.dump_debug_info()
if self.lines.get_value(self.STATUS) == disable:
if self.get_gpio(self.STATUS) == disable:
break
else:
logger.info("modem hasn't pulled STATUS: sleeping for 1s")
@@ -391,18 +414,46 @@ class GpioPhy:
self.power_toggle(disable=gpiod.line.Value.ACTIVE)
def dump_debug_info(self) -> None:
vals = self.lines.get_values()
dtr, pwrkey, reset, apready, disable, status = vals
DTR = self.get_gpio(self.DTR)
PWRKEY = self.get_gpio(self.PWRKEY)
RESET = self.get_gpio(self.RESET)
APREADY = self.get_gpio(self.APREADY)
DISABLE = self.get_gpio(self.DISABLE)
STATUS = self.get_gpio(self.STATUS)
logger.debug(
"gpio states:\n"
f" DTR: {dtr}\n"
f" PWRKEY: {pwrkey}\n"
f" RESET: {reset}\n"
f" APREADY: {apready}\n"
f" DISABLE: {disable}\n"
f" STATUS: {status}"
f" DTR: {DTR}\n"
f" PWRKEY: {PWRKEY}\n"
f" RESET: {RESET}\n"
f" APREADY: {APREADY}\n"
f" DISABLE: {DISABLE}\n"
f" STATUS: {STATUS}"
)
class PinePhoneGpioPhy(GpioPhy):
# PLEASE NOTE THAT THIS REQUIRES THE AXP REGULATOR TO BE POWERING VBAT-BB.
# as of 2024-09-19, that's not mainline; it still requires a handful of patches.
# see postmarketOS' or mobian's kernel to cherry-pick the necessary patches.
# GPIO indices ("lines")
# DTR = "Data Terminal Ready", controls sleep/wakeup. high means modem is allowed to sleep when requested. high -> low forces the modem awake. internal pull-up.
# WAKEUP = 6
DTR = 1, 34 # PB2
PWRKEY = 1, 35 # PB3
RESET = 1, 68 # PC4
APREADY = 1, 231 # PH7, a.k.a. "host ready"
DISABLE = 1, 232 # PH8, a.k.a. "enable"; active-low
# STATUS line: HIGH means powered off
STATUS = 1, 233 # PH9
class PinePhoneProGpioPhy(GpioPhy):
DTR = 0, 3 # gpio0, RK_PA3
PWRKEY = 0, 13 # gpio0, RK_PB5
RESET = 3, 8 # gpio3, RK_PB0
APREADY = 0, 12 # gio0, RK_RB4 ('host-ready-gpios')
DISABLE = 0, 8 # gpio0, RK_PB0 (TODO: megi has this marked active HIGH, not LOW)
STATUS = 3, 6 # gpio3, RK_PA6
class Sequencer:
def __init__(self, executor: Executor, modem: str, modem_phy: Phy, state_fs: Filesystem):
self.executor = executor
@@ -720,7 +771,7 @@ def main():
modem_phy = MegiPhy(executor, args.power_endpoint)
elif args.power_on or args.power_off:
# don't initialize the Gpio PHY unless absolutely necessary, since it has to (re-)configure GPIOs just to show debug info
modem_phy = GpioPhy()
modem_phy = PinePhoneGpioPhy()
sequencer = Sequencer(executor, modem=args.modem, modem_phy=modem_phy, state_fs=state_fs)