/* usb2514 - small utility to set configuration of USB2514 hub chip * (C) 2013-2014 by sysmocom - s.f.m.c. GmbH, Author: Harald Welte * All Rights Reserved * * This program 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; either version 2 of the License, or * (at your option) any later version. * * 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; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include /* #include */ #include "i2c-dev.h" #define USB2514_SLAVE_ADDR 0x2C #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 */ static const uint8_t usb2514_default[256] = { 0x24, 0x04, 0x14, 0x25, 0xB3, 0x0B, 0x9B, 0x20, /* 0x00 */ 0x02, 0x00, 0x00, 0x00, 0x01, 0x32, 0x01, 0x32, /* 0x08 */ 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0xf8 */ }; /* Default configuration as per data sheet */ static uint8_t usb2514_odu[256] = { 0x24, 0x04, 0x14, 0x25, 0xB3, 0x0B, 0x9B, 0x20, /* 0x00 */ 0x02, 0x00, 0x00, 0x00, 0x01, 0x32, 0x01, 0x32, /* 0x08 */ 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0 */ #if 1 /* swap modem + AIS ports (1,2,3) */ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0xf8 */ #else 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0xf8 */ #endif }; static int g_fd; static unsigned long get_support(void) { int rc; unsigned long funcs; rc = ioctl(g_fd, I2C_FUNCS, funcs); printf("supported functions: 0x%lx\n", funcs); return funcs; } static int write_regs(const uint8_t *regs) { unsigned int i; int rc; for (i = 0; i < 256; i+= 32) { printf("Writing block of %u bytes at index %u\n", 32, i); rc = i2c_smbus_write_block_data(g_fd, i, 32, regs+i); if (rc < 0) fprintf(stderr, "Error writing registers at " "offset %u: %d\n", i, rc); } return rc; } /* attempt to obtain the board version from sysfs */ static int get_board_version(void) { FILE *f; unsigned int ver; f = fopen(BOARD_VER_PATH, "r"); if (!f) return -1; if (fscanf(f, "%u", &ver) != 1) { fclose(f); return -2; } fclose(f); return ver; } /* attempt to reset the hub via sysfs */ static int reset_hub(void) { FILE *f; int invert_logic = 0; f = fopen(RESET_PATH, "w"); if (!f) { f = fopen(RESET_PATH_OLD, "w"); if (!f) return -1; invert_logic = 1; } if (invert_logic) fputs("0", f); else fputs("1", f); usleep(10000); rewind(f); if (invert_logic) fputs("1", f); else fputs("0", f); fclose(f); return 0; } int main(int argc, char **argv) { int rc; int board_version; int adapter_nr; long slave_addr = USB2514_SLAVE_ADDR; char filename[PATH_MAX]; 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); 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 */ } rc = ioctl(g_fd, I2C_SLAVE, slave_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"); } rc = write_regs(usb2514_odu); if (rc < 0) { fprintf(stderr, "Error writing default regs: %d\n", rc); exit(1); } exit(0); }