/* * i2c_sfc.c * CERN 2012 Manohar Vanga */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> #include <pio.h> #include <trace.h> #include "i2c.h" #include "i2c_sfp.h" #include "i2c_bitbang.h" #include "i2c_fpga_reg.h" #include "libshw_i2c.h" #define ARRAY_SIZE(a) \ (sizeof(a) / sizeof(*(a))) /* The rest of the buses are normal */ #define WR_SFP2_BUS 2 #define WR_SFP3_BUS 3 #define WR_SFP4_BUS 4 #define WR_SFP5_BUS 5 #define WR_SFP6_BUS 6 #define WR_SFP7_BUS 7 #define WR_SFP8_BUS 8 #define WR_SFP9_BUS 9 #define WR_SFP10_BUS 10 #define WR_SFP11_BUS 11 #define WR_SFP12_BUS 12 #define WR_SFP13_BUS 13 #define WR_SFP14_BUS 14 #define WR_SFP15_BUS 15 #define WR_SFP16_BUS 16 #define WR_SFP17_BUS 17 #define SFP_LED_SYNCED_MASK(t) ((t) ? (1 << 6) : (1 << 0)) #define SFP_LED_LINK_MASK(t) ((t) ? (1 << 4) : (1 << 1)) #define SFP_LED_WRMODE_MASK(t) ((t) ? (1 << 5) : (1 << 3)) #define SFP_TX_DISABLE_MASK(t) ((t) ? (1 << 7) : (1 << 2)) /* * We need these tables because the schematics are messed up * The first one is for figuring out the masks in the pca9548's * The second table is for the connections of the pca9554's */ uint32_t bus_masks[] = { [WR_SFP2_BUS] = 8, [WR_SFP3_BUS] = 9, [WR_SFP4_BUS] = 10, [WR_SFP5_BUS] = 11, [WR_SFP6_BUS] = 12, [WR_SFP7_BUS] = 13, [WR_SFP8_BUS] = 14, [WR_SFP9_BUS] = 15, [WR_SFP10_BUS] = 0, [WR_SFP11_BUS] = 1, [WR_SFP12_BUS] = 2, [WR_SFP13_BUS] = 3, [WR_SFP14_BUS] = 4, [WR_SFP15_BUS] = 5, [WR_SFP16_BUS] = 6, [WR_SFP17_BUS] = 7, }; uint32_t pca9554_masks[] = { [WR_SFP2_BUS] = 14, [WR_SFP3_BUS] = 15, [WR_SFP4_BUS] = 12, [WR_SFP5_BUS] = 13, [WR_SFP6_BUS] = 10, [WR_SFP7_BUS] = 11, [WR_SFP8_BUS] = 8, [WR_SFP9_BUS] = 9, [WR_SFP10_BUS] = 6, [WR_SFP11_BUS] = 7, [WR_SFP12_BUS] = 4, [WR_SFP13_BUS] = 5, [WR_SFP14_BUS] = 2, [WR_SFP15_BUS] = 3, [WR_SFP16_BUS] = 0, [WR_SFP17_BUS] = 1, }; /* The two FPGA i2c masters */ i2c_fpga_reg_t fpga_bus0_reg = { .base_address = FPGA_I2C_ADDRESS, .if_num = FPGA_I2C0_IFNUM, .prescaler = 500, }; i2c_fpga_reg_t fpga_bus1_reg = { .base_address = FPGA_I2C_ADDRESS, .if_num = FPGA_I2C1_IFNUM, .prescaler = 500, }; /* The Bit-Banged I2C bus connected to the PCA9548A Multiplexers. WORKS */ pio_pin_t wr_mux_scl = { .port = PIOB, .pin = 25, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; pio_pin_t wr_mux_sda = { .port = PIOB, .pin = 27, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; struct i2c_bitbang wr_mux_bus_reg = { .scl = &wr_mux_scl, .sda = &wr_mux_sda, }; /* The Bit-Banged I2C bus connected to the SFP 0 (Link 0). WORKS */ pio_pin_t wr_link0_sda = { .port = PIOB, .pin = 23, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; pio_pin_t wr_link0_scl = { .port = PIOB, .pin = 26, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; struct i2c_bitbang wr_link0_reg = { .scl = &wr_link0_scl, .sda = &wr_link0_sda, }; /* The Bit-Banged I2C bus connected to the SFP 1 (Link 1). WORKS */ pio_pin_t wr_link1_sda = { .port = PIOB, .pin = 22, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; pio_pin_t wr_link1_scl = { .port = PIOB, .pin = 21, .mode = PIO_MODE_GPIO, .dir = PIO_OUT_0, }; struct i2c_bitbang wr_link1_reg = { .scl = &wr_link1_scl, .sda = &wr_link1_sda, }; struct i2c_bus i2c_buses[] = { { .name = "fpga_bus0", .type = I2C_BUS_TYPE_FPGA_REG, .type_specific = &fpga_bus0_reg, }, { .name = "fpga_bus1", .type = I2C_BUS_TYPE_FPGA_REG, .type_specific = &fpga_bus1_reg, }, { .name = "wr_mux_bus", .type = I2C_TYPE_BITBANG, .type_specific = &wr_mux_bus_reg, }, { .name = "wr_sfp0_link0", .type = I2C_TYPE_BITBANG, .type_specific = &wr_link0_reg, }, { .name = "wr_sfp0_link1", .type = I2C_TYPE_BITBANG, .type_specific = &wr_link1_reg, }, }; int shw_sfp_buses_init(void) { int i; TRACE(TRACE_INFO, "Initializing SFP I2C busses..."); for (i = 0; i < ARRAY_SIZE(i2c_buses); i++) { if (i2c_init_bus(&i2c_buses[i]) < 0) { printf("init failed: %s\n", i2c_buses[i].name); return -1; } // printf("init: success: %s\n", i2c_buses[i].name); } return 0; } int shw_sfp_bus_scan(int num, uint8_t *dev_map) { int i; int detect; if (num < 0 || num >= ARRAY_SIZE(i2c_buses)) return -1; if (i2c_buses[num].err) return -1; detect = i2c_scan(&i2c_buses[num], dev_map); printf("\ni2c_bus: %s: %d devices\n", i2c_buses[num].name, detect); for (i = 0; i < 128; i++) if (dev_map[i/8] & (1 << (i%8))) printf("device at: 0x%02X\n", i); return detect; } int shw_sfp_header_verify_base(struct shw_sfp_header *head) { int i; uint32_t sum = 0; for (i = 0; i < 63; i++) sum += ((uint8_t *)head)[i]; sum &= 0xff; return (sum == head->cc_base) ? 0 : -1; } int shw_sfp_header_verify_ext(struct shw_sfp_header *head) { int i; uint32_t sum = 0; for (i = 64; i < 95; i++) sum += ((uint8_t *)head)[i]; sum &= 0xff; return (sum == head->cc_ext) ? 0 : -1; } int shw_sfp_header_verify(struct shw_sfp_header *head) { return (!shw_sfp_header_verify_base(head) && !shw_sfp_header_verify_ext(head)) ? 0 : -1; } void shw_sfp_print_header(struct shw_sfp_header *head) { int i; printf("Identifier: %02X\n", head->id); printf("Extended Identifier: %02X\n", head->ext_id); printf("Connector: %02X\n", head->connector); printf("Connector: %02X\n", head->connector); printf("Tranciever: %016llX\n", ((uint64_t *)head->transciever)[0]); printf("Encoding: %02x\n", head->encoding); printf("Nominal Bit Rate: %d Megabits/s\n", head->br_nom*100); printf("Length (9m): %dkm\n", head->length1); printf("Length (9m): %dm\n", head->length2*100); printf("Length (50m): %dm\n", head->length3*10); printf("Length (62.5m): %dm\n", head->length4*10); printf("Length (copper): %dm\n", head->length5); printf("Vendor Name: "); for (i = 0; i < 16; i++) printf("%c", head->vendor_name[i]); printf("\n"); printf("Company ID: %02X%02X%02X\n", head->vendor_oui[0], head->vendor_oui[1], head->vendor_oui[2]); printf("Vendor Part Number: "); for (i = 0; i < 16; i++) printf("%c", head->vendor_pn[i]); printf("\n"); printf("Vendor Revision: "); for (i = 0; i < 4; i++) printf("%c", head->vendor_rev[i]); printf("\n"); printf("Options: %04X\n", ((uint16_t *)head->options)[0]); printf("Bitrate (MAX): %02X\n", head->br_max); printf("Bitrate (MIN): %02X\n", head->br_min); printf("Vendor Serial: "); for (i = 0; i < 16; i++) printf("%c", head->vendor_serial[i]); printf("\n"); uint8_t *date = (uint8_t *)&head->date_code; printf("Date Code: "); for (i = 0; i < 8; i++) printf("%c", head->date_code[i]); printf("\n"); } void shw_sfp_header_dump(struct shw_sfp_header *head) { int i; uint8_t *dump = (uint8_t *)head; printf("Header Dump:"); for (i = 0; i < sizeof(struct shw_sfp_header); i++) { if (i % 8 == 0) printf("\n"); printf("%02X ", dump[i]); } printf("\n"); } /* Get the SFP ID from the SFP number (0 to 17) */ inline int shw_sfp_id(int num) { if (num > 17 || num < 0) return -1; return num; } int32_t shw_sfp_read(int num, uint32_t addr, int off, int len, uint8_t *buf) { int id; uint8_t byte1, byte2; struct i2c_bus *bus; id = shw_sfp_id(num); if (id < 0) return -1; bus = &i2c_buses[WR_MUX_BUS]; if (id == 0 || id == 1) bus = &i2c_buses[WR_SFP0_BUS + id]; if (id > 1) { /* Set the mask in the PCA9548 */ byte1 = (1 << bus_masks[id]) & 0xff; byte2 = ((1 << bus_masks[id]) >> 8) & 0xff; i2c_transfer(bus, 0x70, 1, 0, &byte1); i2c_transfer(bus, 0x71, 1, 0, &byte2); } /* Send the offset we want to read from */ if (off >= 0) i2c_transfer(bus, addr, 1, 0, (uint8_t *)&off); /* Do the read */ return i2c_transfer(bus, addr, 0, len, buf); } int32_t shw_sfp_write(int num, uint32_t addr, int off, int len, uint8_t *buf) { int id; uint8_t byte1, byte2; struct i2c_bus *bus; id = shw_sfp_id(num); if (id < 0) return -1; bus = &i2c_buses[WR_MUX_BUS]; if (id == 0 || id == 1) bus = &i2c_buses[WR_SFP0_BUS + id]; if (id != 0 && id != 1) { /* Set the mask in the PCA9548 */ byte1 = (1 << bus_masks[id]) & 0xff; byte2 = ((1 << bus_masks[id]) >> 8) & 0xff; i2c_transfer(bus, 0x70, 1, 0, &byte1); i2c_transfer(bus, 0x71, 1, 0, &byte2); } /* Send the offset we want to write to if requested */ if (off >= 0) i2c_transfer(bus, addr, 1, 0, (uint8_t *)&off); /* Do the read */ return i2c_transfer(bus, addr, len, 0, buf); } uint32_t shw_sfp_module_scan(void) { int i; int ret; uint32_t mask = 0; uint8_t test; for (i = 0; i < 18; i++) { ret = shw_sfp_read(i, 0x50, 0x0, sizeof(test), &test); if (ret == I2C_DEV_NOT_FOUND) continue; mask |= (1 << i); } return mask; } void shw_sfp_gpio_init(void) { int i; uint8_t addr = 0x20; uint8_t conf_output[] = {0x3, 0x0}; uint8_t set_output[] = {0x1, 0x0}; struct i2c_bus *bus = &i2c_buses[WR_FPGA_BUS0]; /* configure the pins as outputs */ i2c_transfer(bus, addr, 2, 0, conf_output); i2c_transfer(bus, addr, 2, 0, set_output); bus = &i2c_buses[WR_FPGA_BUS1]; for (i = 0; i < 8; i++) { i2c_transfer(bus, addr + i, 2, 0, conf_output); i2c_transfer(bus, addr + i, 2, 0, set_output); } for(i=0; i<18;i++) { shw_sfp_set_led_synced(i, 1); shw_udelay(7000); shw_sfp_set_led_link(i, 1); shw_udelay(7000); } for(i=0; i<18;i++) { shw_sfp_set_led_synced(i, 0); shw_udelay(7000); shw_sfp_set_led_link(i, 0); shw_udelay(7000); } } void shw_sfp_gpio_set(int num, uint8_t state) { int id; int top; struct i2c_bus *bus; uint8_t addr = 0x20; uint8_t send[2]; uint8_t out = 0; uint8_t curr; id = shw_sfp_id(num); bus = &i2c_buses[WR_FPGA_BUS1]; if (id < 2) bus = &i2c_buses[WR_FPGA_BUS0]; if (id > 1) { addr += pca9554_masks[id]/2; top = pca9554_masks[id]%2; } else { top = id%2; } send[0] = 0x1; /* Read current state of pins */ i2c_transfer(bus, addr, 1, 0, send); i2c_transfer(bus, addr, 0, 1, &curr); //TRACE(TRACE_INFO,"%d: 0x%x 0x%x s=%d, send=%d,%d c=%d, \n",num,bus,addr,state,send[0],send[1],&curr); if (top) curr &= 0xf; else curr &= 0xf0; if (state & SFP_LED_LINK) curr |= SFP_LED_LINK_MASK(top); if (state & SFP_LED_WRMODE) curr |= SFP_LED_WRMODE_MASK(top); if (state & SFP_LED_SYNCED) curr |= SFP_LED_SYNCED_MASK(top); if (state & SFP_TX_DISABLE) curr |= SFP_TX_DISABLE_MASK(top); send[1] = curr; i2c_transfer(bus, addr, 2, 0, send); //TRACE(TRACE_INFO,"%d: 0x%x 0x%x s=%d, send=%d,%d c=%d, \n",num,bus,addr,state,send[0],send[1],curr); } uint8_t shw_sfp_gpio_get(int num) { int id; int top; struct i2c_bus *bus; uint8_t addr = 0x20; uint8_t send[2]; uint8_t out = 0; uint8_t curr; id = shw_sfp_id(num); bus = &i2c_buses[WR_FPGA_BUS1]; if (id < 2) bus = &i2c_buses[WR_FPGA_BUS0]; if (id > 1) { addr += pca9554_masks[id]/2; top = pca9554_masks[id]%2; } else { top = id%2; } send[0] = 0x1; /* Read current state of pins */ i2c_transfer(bus, addr, 1, 0, send); i2c_transfer(bus, addr, 0, 1, &curr); if (curr & SFP_LED_LINK_MASK(top)) out |= SFP_LED_LINK; if (curr & SFP_LED_WRMODE_MASK(top)) out |= SFP_LED_WRMODE; if (curr & SFP_LED_SYNCED_MASK(top)) out |= SFP_LED_SYNCED; if (curr & SFP_TX_DISABLE_MASK(top)) out |= SFP_TX_DISABLE; return out; } int shw_sfp_read_header(int num, struct shw_sfp_header *head) { int ret; if (shw_sfp_id(num) < 0) return -1; ret = shw_sfp_module_scan(); if (!(ret & (1 << num))) return -1; ret = shw_sfp_read(num, I2C_SFP_ADDRESS, 0x0, sizeof(struct shw_sfp_header), (uint8_t *)head); if (ret == I2C_DEV_NOT_FOUND) return -ENODEV; return 0; } int shw_sfp_read_verify_header(int num, struct shw_sfp_header *head) { int ret; ret = shw_sfp_read_header(num, head); if (ret < 0) return ret; return shw_sfp_header_verify(head); } static struct shw_sfp_caldata *shw_sfp_cal_list = NULL; int shw_sfp_read_db(char *filename) { lua_State *L = luaL_newstate(); struct shw_sfp_caldata *sfp; luaL_openlibs(L); if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) { printf("cannot run configuration file: %s", lua_tostring(L, -1)); return -1; } lua_getglobal(L, "sfpdb"); if (!lua_istable(L, -1)) { printf("`sfpdb' should be a table\n"); return -1; } lua_pushnil(L); while (lua_next(L, -2)) { if(!lua_istable(L, -1)) { lua_pop( L, 1 ); continue; } int i = 0; const char *sfp_pn = 0; const char *sfp_vs = 0; int vals[2]; double alpha; lua_pushnil(L); while (lua_next(L, -2)) { const char *key = lua_tostring(L, -2); if (strcmp(key, "part_num") == 0) sfp_pn = lua_tostring(L, -1); else if (strcmp(key, "part_serial") == 0) sfp_vs = lua_tostring(L, -1); else if (strcmp(key, "alpha") == 0) alpha = lua_tonumber(L, -1); else if (strcmp(key, "delta_tx") == 0) vals[0] = lua_tointeger(L, -1); else if (strcmp(key, "delta_rx") == 0) vals[1] = lua_tointeger(L, -1); lua_pop(L, 1); } lua_pop(L, 1); sfp = malloc(sizeof(struct shw_sfp_caldata)); strncpy(sfp->part_num, sfp_pn, sizeof(sfp->part_num)); if (!sfp_vs || strcmp(sfp_vs, "") == 0) { sfp->vendor_serial[0] = 0; sfp->flags |= SFP_FLAG_CLASS_DATA; } else { strcpy(sfp->vendor_serial, sfp_vs); sfp->flags |= SFP_FLAG_DEVICE_DATA; } sfp->alpha = alpha; sfp->delta_tx = vals[0]; sfp->delta_rx = vals[1]; sfp->next = shw_sfp_cal_list; shw_sfp_cal_list = sfp; } lua_pop( L, 1 ); lua_close(L); return 0; } struct shw_sfp_caldata *shw_sfp_get_cal_data(int num) { uint32_t ret; struct shw_sfp_header head; struct shw_sfp_caldata *t; struct shw_sfp_caldata *other = NULL; ret = shw_sfp_module_scan(); if (!(ret & (1 << num))) { printf("sfp not inserted into slot: %d\n", num); return NULL; } if (shw_sfp_read_header(num, &head) < 0) { printf("failed to read sfp header for slot %d\n", num); return NULL; } char *pn = (char *)head.vendor_pn; char *vs = (char *)head.vendor_serial; int i; for (i = 0; i < 16; i++) { if (pn[i] == 0x20) pn[i] = 0; if (vs[i] == 0x20) vs[i] = 0; } t = shw_sfp_cal_list; /* In the first pass, look for serial number */ while (t) { // printf("search1 %s %s\n", t->part_num, t->vendor_serial); if (strncmp(pn, t->part_num, 16) == 0 && strncmp(t->vendor_serial, "", 16) == 0) other = t; else if (strncmp(pn, t->part_num, 16) == 0 && strncmp(vs, t->vendor_serial, 16) == 0) return t; t = t->next; } if (other) return other; return NULL; }