From 89435bf483dcaf5086c846f81523b808cda7142a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 1 Nov 2015 15:20:24 +0100 Subject: [PATCH] usb2514: make it more modular/configurable --- recipes-bsp/sob-odu/files/usb2514.c | 307 +++++++++++++++++++++++----- 1 file changed, 255 insertions(+), 52 deletions(-) diff --git a/recipes-bsp/sob-odu/files/usb2514.c b/recipes-bsp/sob-odu/files/usb2514.c index 2669a20..5366e01 100644 --- a/recipes-bsp/sob-odu/files/usb2514.c +++ b/recipes-bsp/sob-odu/files/usb2514.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,11 +31,94 @@ /* #include */ #include "i2c-dev.h" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define USB2514_SLAVE_ADDR 0x2C +enum compare_op { + EQUAL, + NOT_EQUAL, + LESS_THAN_OR_EQUAL, + GREATER_THAN_OR_EQUAL, +}; + +struct usb2514_board { + const char *name; + + unsigned int i2c_bus; + uint8_t i2c_addr; + + const char *board_version_file; + unsigned int board_version; + enum compare_op board_version_op; + + uint8_t ports_swap; + + const char *reset_gpio_path; + int reset_low_active; +}; + +struct board_group { + /* new /sys/firmware/devicetree/base/model */ + const char *device_tree_name; + /* old pre-device tree kernels, "Hardware :" in /proc/cpuinfo */ + const char *proc_name; + + const struct usb2514_board *boards; + unsigned int num_boards; +}; + +static const struct usb2514_board odu_boards[] = { + { + .name = "sob-odu v1", + .i2c_bus = 0, + .i2c_addr = 0x2C, + .board_version_file = "/sys/devices/platform/sob-odu.0/board_version", + .board_version = 1, + .board_version_op = EQUAL, + .ports_swap = 0x00, /* ports are still swapped in hardware */ + .reset_gpio_path = "/sys/devices/platform/sob-odu.0/gpio_hub_reset/value", + .reset_low_active = 1, + }, { + .name = "sob-odu v2", + .i2c_bus = 0, + .i2c_addr = 0x2C, + .board_version_file = "/sys/devices/platform/sob-odu.0/board_version", + .board_version = 2, + .board_version_op = EQUAL, + .ports_swap = 0x0E, /* swap DN1, DN2, DN3 */ + .reset_gpio_path = "/sys/devices/platform/sob-odu.0/gpio_hub_reset/value", + .reset_low_active = 0, + }, { + .name = "sob-odu v2", + .i2c_bus = 0, + .i2c_addr = 0x2C, + .board_version_file = "/sys/devices/platform/sob-odu.0/board_version", + .board_version = 0, /* EEPROM Empty ?!? */ + .board_version_op = EQUAL, + .ports_swap = 0x0E, /* swap DN1, DN2, DN3 */ + .reset_gpio_path = "/sys/devices/platform/sob-odu.0/gpio_hub_reset/value", + .reset_low_active = 0, + }, { + .name = "sob-odu v3+", + .i2c_bus = 0, + .i2c_addr = 0x2C, + .board_version_file = "/sys/devices/platform/sob-odu.0/board_version", + .board_version = 3, + .board_version_op = GREATER_THAN_OR_EQUAL, + .ports_swap = 0x0C, /* swap only DN2 and DN3 */ + .reset_gpio_path = "/sys/devices/platform/sob-odu.0/gpio_hub_reset/value", + .reset_low_active = 0, + }, +}; + +static const struct board_group boards[] = { + { + .proc_name = "sob-odu", + .device_tree_name = "sysmocom ODU", + .boards = odu_boards, + .num_boards = ARRAY_SIZE(odu_boards), + }, +}; -#define BOARD_VER_PATH "/sys/devices/platform/sob-odu.0/board_version" -#define RESET_PATH "/sys/devices/platform/sob-odu.0/gpio_hub_reset/value" #define RESET_PATH_OLD "/sys/class/gpio/gpio62/value" /* Default configuration as per data sheet */ @@ -119,7 +203,7 @@ static int g_fd; static unsigned long get_support(void) { int rc; - unsigned long funcs; + unsigned long funcs = 0; rc = ioctl(g_fd, I2C_FUNCS, funcs); @@ -128,7 +212,6 @@ static unsigned long get_support(void) return funcs; } - static int write_regs(const uint8_t *regs) { unsigned int i; @@ -145,12 +228,12 @@ static int write_regs(const uint8_t *regs) } /* attempt to obtain the board version from sysfs */ -static int get_board_version(void) +static int get_board_version(const char *ver_file) { FILE *f; unsigned int ver; - f = fopen(BOARD_VER_PATH, "r"); + f = fopen(ver_file, "r"); if (!f) return -1; @@ -164,20 +247,143 @@ static int get_board_version(void) return ver; } -/* attempt to reset the hub via sysfs */ -static int reset_hub(void) +static int board_ver_matches(const struct usb2514_board *board, + unsigned int version) +{ + switch (board->board_version_op) { + case EQUAL: + return (version == board->board_version); + case NOT_EQUAL: + return (version != board->board_version); + case LESS_THAN_OR_EQUAL: + return (version <= board->board_version); + case GREATER_THAN_OR_EQUAL: + return (version >= board->board_version); + default: + return 0; + } +} + +static char *get_proc_name(void) +{ + FILE *f = fopen("/proc/cpuinfo", "r"); + char linebuf[256]; + + while (fgets(linebuf, sizeof(linebuf), f)) { + /* strip LF at the end of line */ + char *lf = strrchr(linebuf, '\n'); + if (lf) + *lf = '\0'; + + if (strncmp(linebuf, "Hardware", 8) && + strncmp(linebuf, "machine", 7)) + continue; + + /* search for the colon */ + char *colon = strchr(linebuf, ':'); + if (!colon) + continue; + colon++; + + /* strip any leading whitespace */ + while (*colon == ' ' || *colon == '\t') + colon++; + + fclose(f); + return strdup(colon); + } + + fclose(f); + return NULL; +} + +static char *get_dt_name(void) { FILE *f; - int invert_logic = 0; + char *name = NULL; + char linebuf[256]; - f = fopen(RESET_PATH, "w"); - if (!f) { - f = fopen(RESET_PATH_OLD, "w"); - if (!f) - return -1; - invert_logic = 1; + f = fopen("/sys/firmware/devicetree/base/model", "r"); + if (!f) + return NULL; + + if (!fgets(linebuf, sizeof(linebuf), f)) { + fclose(f); + return NULL; } + fclose(f); + + return strdup(linebuf); +} + + +static const struct board_group *find_matching_board_group() +{ + int i; + char *proc_name, *dt_name; + + proc_name = get_proc_name(); + dt_name = get_dt_name(); + + for (i = 0; i < ARRAY_SIZE(boards); i++) { + const struct board_group *bgrp = &boards[i]; + + if (dt_name && bgrp->device_tree_name && + !strcmp(dt_name, bgrp->device_tree_name)) { + free(proc_name); + free(dt_name); + return bgrp; + } + + if (proc_name && bgrp->proc_name && + !strcmp(proc_name, bgrp->proc_name)) { + free(proc_name); + free(dt_name); + return bgrp; + } + } + + free(proc_name); + free(dt_name); + + return NULL; +} + + +static const struct usb2514_board * +find_matching_board(const struct board_group *bgrp) +{ + int i; + + for (i = 0; i < bgrp->num_boards; i++) { + const struct usb2514_board *board = &bgrp->boards[i]; + int ver; + + if (board->board_version_file) { + /* get board version and compare */ + ver = get_board_version(board->board_version_file); + if (ver < 0) + continue; + if (!board_ver_matches(board, ver)) + continue; + } + + return board; + } + + return NULL; +} + +/* attempt to reset the hub via sysfs */ +static int reset_hub(const char *reset_path, int invert_logic) +{ + FILE *f; + + f = fopen(reset_path, "w"); + if (!f) + return -1; + if (invert_logic) fputs("0", f); else @@ -198,56 +404,53 @@ static int reset_hub(void) int main(int argc, char **argv) { int rc; - int board_version; - int adapter_nr; - long slave_addr = USB2514_SLAVE_ADDR; char filename[PATH_MAX]; + const struct board_group *bgrp; + const struct usb2514_board *board; - if (argc < 2) { - fprintf(stderr, "You have to specify I2C bus number\n"); - exit(2); - } - - adapter_nr = atoi(argv[1]); - snprintf(filename, sizeof(filename)-1, "/dev/i2c-%d", adapter_nr); - rc = open(filename, O_RDWR); - if (rc < 0) { - fprintf(stderr, "Error opening the device: %d\n", rc); + bgrp = find_matching_board_group(); + if (!bgrp) { + fprintf(stderr, "Cannot find matching board group for this system\n"); exit(1); } + printf("Found matching board group %s(%s)\n", bgrp->proc_name, bgrp->device_tree_name); + board = find_matching_board(bgrp); + if (!board) { + fprintf(stderr, "Cannot find matching config for this system\n"); + exit(1); + } + printf("Found matching board %s\n", board->name); + + /* open the I2C bus device */ + + snprintf(filename, sizeof(filename)-1, "/dev/i2c-%d", board->i2c_bus); + rc = open(filename, O_RDWR); + if (rc < 0) { + fprintf(stderr, "Error opening the device %s: %d\n", filename, rc); + exit(1); + } g_fd = rc; - get_support(); - board_version = get_board_version(); - if (board_version >= 3) { - /* on board version 3 and later we don't need to swap - * USB downlink port 1 */ - printf("Detected board >= v3, not swapping DN1\n"); - usb2514_odu[0xFA] = 0x0C; - } else if (board_version == 1) { - /* ports are still swapped in hardware */ - printf("Detected board v1, not swapping any ports\n"); - usb2514_odu[0xFA] = 0x00; - } else if (board_version == 2) { - printf("Detected board v2, swapping DN1, DN2 and DN3\n"); - /* default */ - } else { - printf("Assuming board v2, swapping DN1, DN2 and DN3\n"); - /* default */ - } + /* set the slave address */ - rc = ioctl(g_fd, I2C_SLAVE, slave_addr); + rc = ioctl(g_fd, I2C_SLAVE, board->i2c_addr); if (rc < 0) { fprintf(stderr, "Error setting slave addr: %d\n", rc); exit(1); } - /* First reset the USB hub before loading data into it */ - if (reset_hub() < 0) { - fprintf(stderr, "Couldn't reset the USB hub!\n"); - } + if (board->reset_gpio_path) { + /* First reset the USB hub before loading data into it */ + if (reset_hub(board->reset_gpio_path, board->reset_low_active) < 0) { + fprintf(stderr, "Couldn't reset the USB hub!\n"); + } + } else + fprintf(stderr, "board config doesn't indicate USB hub reset GPIO\n"); + + /* patch the port inversion byte into the array */ + usb2514_odu[0xFA] = board->ports_swap; rc = write_regs(usb2514_odu); if (rc < 0) {