blob: 3dd0e80426657dbaa53e0d7310379c1d73b38224 [file] [log] [blame]
/* Copyright 2024 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/platform.h"
#include "rts5453.h"
#include "smbus_usermode.h"
#include "um_ppm_chardev.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Some extra functionality that's used by this binary that's not necessarily
* for UCSI (such as getting info about the PD controller, firmware update,
* etc).
*/
struct extra_driver_ops {
int (*get_info)(struct ucsi_pd_driver *driver);
int (*do_firmware_update)(struct ucsi_pd_driver *driver,
const char *filepath, int dry_run);
// Establish communication with SMBUS transport LPMs.
struct ucsi_pd_driver *(*smbus_lpm_open)(
struct smbus_driver *smbus, struct pd_driver_config *config);
};
struct extra_driver_ops rts5453_ops = {
.get_info = rts5453_get_info,
.do_firmware_update = rts5453_do_firmware_update,
.smbus_lpm_open = rts5453_open,
};
// Set up the um_ppm device to start communicating with kernel.
int cdev_prepare_um_ppm(char *um_test_devpath, struct ucsi_pd_driver *pd,
struct smbus_driver *smbus,
struct pd_driver_config *config)
{
// Open the kernel um_ppm chardev to establish the PPM communication.
struct um_ppm_cdev *cdev =
um_ppm_cdev_open(um_test_devpath, pd, smbus, config);
if (!cdev) {
ELOG("Failed to initialize PPM chardev. Exit early!");
return -1;
}
// TODO - Register sigterm handler so we know when to exit.
// Mainloop with chardev handling.
um_ppm_cdev_mainloop(cdev);
return 0;
}
static const char *usage_str =
("um_ppm [options]\n"
"\n"
"Userspace PPM implementation and helper functions. Use this to\n"
"develop against and test new devkits.\n"
"\n"
"General options:\n"
"\t-p PD driver config to use. Valid values: ['rts5453']\n"
"\t-b I2C Bus number (/dev/i2c-N) (required)\n"
"\t-g /dev/gpiochip[N] (required)\n"
"\t-l Gpio line for LPM alert (required)\n"
"\t-v Enable verbose logs\n"
"Actions (exclusive):\n"
"\t-f <path> Do firmware update with file at this path\n"
"\t-k <dev path> Attach ucsi_um_kernel driver via this chardev path\n"
"\t-d Demo commands\n");
void usage()
{
printf(usage_str);
}
int main(int argc, char *argv[])
{
int opt;
int fwupdate = 0;
char *fwupdate_file = NULL;
int demo = 0;
int i2c_bus = -1;
int i2c_chip_address = -1;
int gpio_chip = -1;
int gpio_line = -1;
int attach_to_kernel = 0;
char *ucsi_um_kernel_dev = NULL;
char *driver_config_in = NULL;
struct pd_driver_config driver_config;
struct extra_driver_ops *ops;
while ((opt = getopt(argc, argv, ":f:k:dvb:p:g:l:")) != -1) {
switch (opt) {
case 'b':
i2c_bus = strtol(optarg, NULL, 10);
break;
case 'p':
driver_config_in = strdup(optarg);
break;
case 'g':
gpio_chip = strtol(optarg, NULL, 10);
break;
case 'l':
gpio_line = strtol(optarg, NULL, 10);
break;
case 'f':
fwupdate = 1;
fwupdate_file = strdup(optarg);
break;
case 'k':
attach_to_kernel = 1;
ucsi_um_kernel_dev = strdup(optarg);
break;
case 'd':
demo = 1;
break;
case 'v':
platform_set_debug(true);
break;
case ':':
printf("Missing arg for %c\n", optopt);
usage();
return -1;
case '?':
default:
printf("Unknown option: %c\n", optopt);
usage();
return -1;
}
}
struct smbus_driver *smbus = NULL;
struct ucsi_pd_driver *pd_driver = NULL;
if (i2c_bus == -1) {
ELOG("Invalid bus (%d) parameter", i2c_bus);
return -1;
}
if (!driver_config_in) {
ELOG("No driver config given. Defaulting to rts5453.");
driver_config_in = "rts5453";
}
// Get driver config
if (strcmp(driver_config_in, "rts5453") == 0) {
driver_config = rts5453_get_driver_config();
ops = &rts5453_ops;
// Use port-0 for smbus addressing
i2c_chip_address = driver_config.port_address_map[0];
} else {
ELOG("Unsupported PD driver config: %s", driver_config_in);
return -1;
}
// Open usermode smbus.
smbus = smbus_um_open(i2c_bus, i2c_chip_address, gpio_chip, gpio_line);
if (!smbus) {
ELOG("Failed to open smbus");
goto handle_error;
}
// Open PD driver
pd_driver = ops->smbus_lpm_open(smbus, &driver_config);
if (!pd_driver) {
ELOG("Failed to open PD driver.");
goto handle_error;
}
DLOG("RTS5453 is initialized. Now taking desired action...");
if (demo) {
return ops->get_info(pd_driver);
} else if (fwupdate && fwupdate_file) {
return ops->do_firmware_update(pd_driver, fwupdate_file,
/*dry_run=*/0);
} else if (attach_to_kernel) {
return cdev_prepare_um_ppm(ucsi_um_kernel_dev, pd_driver, smbus,
&driver_config);
}
return 0;
handle_error:
if (smbus) {
smbus->cleanup(smbus);
free(smbus);
}
if (pd_driver) {
pd_driver->cleanup(pd_driver);
free(pd_driver);
}
return -1;
}