Commit 2aaa11f2 authored by Quentin Genoud's avatar Quentin Genoud

restore I2C as it was to compile libwr

parent ef4f7357
Pipeline #5320 failed with stage
in 9 seconds
/*
** This work is part of the White Rabbit project
*
* Copyright (C) 2024 CERN (www.cern.ch)
* Author: Quentin Genoud Duvillaret <quentin.genoud@cern.ch>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "i2c.h"
int i2c_init_bus(struct i2c_bus *bus)
{
/* Open I2C controller (i2c-dev) file from /dev */
bus->fd = open(bus->dev_name, O_RDWR);
if(bus->fd < 0)
return bus->fd;
else
return 0;
}
#include "i2c_bitbang.h"
#include "i2c_fpga_reg.h"
#include <libwr/wrs-msg.h>
#include <libwr/util.h>
int32_t i2c_write(struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data)
int i2c_init_bus(struct i2c_bus *bus)
{
int ret;
//pr_info("%s (0x%X): 0x%X 2w:%d %d\n",bus->dev_name,bus,address,to_write,data[0]);
/* Check that the bus structure has been allocated */
if(!bus)
return I2C_NULL_PARAM;
/* Check that the bus is initialized (device open) */
if(bus->fd <= 0)
return I2C_NULL_PARAM;
/* Set the slave address */
ret = ioctl(bus->fd, I2C_SLAVE, address);
if(ret < 0)
return -1;
/* Write the data */
ret = write(bus->fd, data, count);
/* Check that we were able to write data */
if(ret < 0)
return ret;
/* Check that we wrote as much bytes as requested */
if(ret < count)
return -1;
if (bus->type == I2C_TYPE_BITBANG)
ret = i2c_bitbang_init_bus(bus);
else if (bus->type == I2C_BUS_TYPE_FPGA_REG)
ret = i2c_fpga_reg_init_bus(bus);
else
ret = -1;
/* All good, return number of bytes written */
bus->err = ret;
return ret;
}
int32_t i2c_read(struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data)
int32_t i2c_transfer(struct i2c_bus * bus, uint32_t address, uint32_t to_write,
uint32_t to_read, uint8_t * data)
{
int ret;
//pr_info("%s (0x%X): 0x%X 2w:%d %d\n",bus->dev_name,bus,address,to_write,data[0]);
/* Check that the bus structure has been allocated */
if(!bus)
return I2C_NULL_PARAM;
/* Check that the bus is initialized (device open) */
if(bus->fd <= 0)
return I2C_NULL_PARAM;
/* Set the slave address */
ret = ioctl(bus->fd, I2C_SLAVE, address);
if(ret < 0)
return -1;
/* Read the data */
ret = read(bus->fd, data, count);
/* Check that we were able to read data */
if(ret < 0)
return ret;
return bus->transfer(bus, address, to_write, to_read, data);
}
/* Check that we read as much bytes as requested */
if(ret < count)
return -1;
int32_t i2c_write(struct i2c_bus *bus, uint32_t address, uint32_t to_write,
uint8_t * data)
{
//pr_info("%s (0x%X): 0x%X 2w:%d 2r:%d %d\n",bus->name,bus,address,to_write,0,data[0]);
return bus->transfer(bus, address, to_write, 0, data);
}
/* All good, return number of bytes read */
return ret;
int32_t i2c_read(struct i2c_bus * bus, uint32_t address, uint32_t to_read,
uint8_t * data)
{
return bus->transfer(bus, address, 0, to_read, data);
//pr_info("%s (0x%X): 0x%X 2w:%d 2r:%d %d\n",bus->name,bus,address,0,to_read,data[0]);
}
int32_t i2c_scan(struct i2c_bus * bus, uint8_t * data)
{
const uint8_t first_valid_address = 0;
const uint8_t last_valid_address = 0x7f;
uint8_t address;
int32_t found = 0;
int res;
uint8_t reg = 0;
uint32_t offset;
uint32_t bit;
/* Check that the bus structure has been allocated */
if(!bus)
if (!bus)
return I2C_NULL_PARAM;
/* Check that the bus is initialized (device open) */
if(bus->fd <= 0)
return I2C_NULL_PARAM;
// const int devices = 128;
/* 16 bytes * 8 addresses per byte == 128 addresses */
memset((void *)data, 0, 16);
int address;
/* Check all I2C addresses to find any device on the bus */
for (address = first_valid_address; address <= last_valid_address;
address++) {
/* Set current slave address */
res = ioctl(bus->fd, I2C_SLAVE, address);
const int first_valid_address = 0;
const int last_valid_address = 0x7f;
memset((void *)data, 0, 16); //16 bytes * 8 addresses per byte == 128 addresses
/* We want to perform an operation on register @ 0x00 */
res = write(bus->fd, &reg, 1);
int found = 0;
/* Device detected */
if(res > 0)
for (address = first_valid_address; address <= last_valid_address;
address++) {
int res = bus->scan(bus, address);
if (res) //device present
{
offset = address >> 3; //choose proper byte
bit = (1 << (address % 8)); //choose proper bit
int offset = address >> 3; //choose proper byte
int bit = (1 << (address % 8)); //choose proper bit
data[offset] |= bit;
found++;
}
}
//pr_debug("%s (%p): ndev=%d\n", bus->name, bus, found);
pr_debug("%s (%p): ndev=%d\n", bus->name, bus, found);
return found;
}
/*
i2c.h
Q. GENOUD CERN 2024
B.Bielawski CERN 2012
*/
#ifndef I2C_H
......@@ -8,33 +8,40 @@
#include <stdint.h>
#define I2C_OK 0
#define I2C_DEV_NOT_FOUND -1
#define I2C_NO_ACK_RCVD -2
#define I2C_ALLOC_ERROR -0x10
#define I2C_NULL_PARAM -0x11
#define I2C_BUS_MISMATCH -0x12
#define I2C_OK 0
#define I2C_DEV_NOT_FOUND -1
#define I2C_NO_ACK_RCVD -2
#define I2C_ALLOC_ERROR -0x10
#define I2C_NULL_PARAM -0x11
#define I2C_BUS_MISMATCH -0x12
#define I2C_TYPE_BITBANG 0
#define I2C_TYPE_BITBANG 0
#define I2C_BUS_TYPE_FPGA_REG 1
#define I2C_WRITE 0
#define I2C_READ 1
#define I2C_WRITE 0
#define I2C_READ 1
typedef struct i2c_bus {
const char *name;
int fd;
int32_t(*write) (struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data);
int32_t(*read) (struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data);
int32_t(*scan) (struct i2c_bus *bus, uint8_t *data);
int type;
void *type_specific;
int32_t(*transfer) (struct i2c_bus * bus, uint32_t address,
uint32_t to_write, uint32_t to_read,
uint8_t * data);
int32_t(*scan) (struct i2c_bus * bus, uint32_t address);
int err;
} i2c_bus_t;
int i2c_init_bus(struct i2c_bus *bus);
int32_t i2c_write(struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data);
int32_t i2c_read(struct i2c_bus *bus, uint32_t address, uint32_t count, uint8_t *data);
int32_t i2c_transfer(struct i2c_bus *bus, uint32_t address, uint32_t to_write,
uint32_t to_read, uint8_t * data);
int32_t i2c_scan(struct i2c_bus *bus, uint8_t *data);
int32_t i2c_write(struct i2c_bus *bus, uint32_t address, uint32_t to_write,
uint8_t * data);
int32_t i2c_read(struct i2c_bus *bus, uint32_t address, uint32_t to_read,
uint8_t * data);
int32_t i2c_scan(struct i2c_bus *bus, uint8_t * data);
#endif //I2C_H
......@@ -49,67 +49,154 @@
#define SFP_LED_WRMODE_MASK(t) ((t) ? (1 << 5) : (1 << 3))
#define SFP_TX_DISABLE_MASK(t) ((t) ? (1 << 7) : (1 << 2))
/* 20 I2C busses: one for each SFP port */
/* Either 8 or 16 byte pages, so we use the smaller */
#define SFP_PAGE_SIZE 8
/*
* 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,
};
/* I2C pins are set to input to avoid wrong transfers (which can lead to
* e.g. overwrite of SFP's eeprom) on i2c buses if HAL was killed in
* the middle of a transfer (by e.g. system reset). */
/* 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_IN, /* Set as input to avoid toggle after reset */
};
pio_pin_t wr_mux_sda = {
.port = PIOB,
.pin = 27,
.mode = PIO_MODE_GPIO,
.dir = PIO_IN, /* Set as input to avoid toggle after reset */
};
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_IN, /* Set as input to avoid toggle after reset */
};
pio_pin_t wr_link0_scl = {
.port = PIOB,
.pin = 26,
.mode = PIO_MODE_GPIO,
.dir = PIO_IN, /* Set as input to avoid toggle after reset */
};
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_IN, /* Set as input to avoid toggle after reset */
};
pio_pin_t wr_link1_scl = {
.port = PIOB,
.pin = 21,
.mode = PIO_MODE_GPIO,
.dir = PIO_IN, /* Set as input to avoid toggle after reset */
};
struct i2c_bitbang wr_link1_reg = {
.scl = &wr_link1_scl,
.sda = &wr_link1_sda,
};
struct i2c_bus i2c_buses[] = {
{
.name = "/dev/i2c-0",
},
{
.name = "/dev/i2c-1",
},
{
.name = "/dev/i2c-2",
},
{
.name = "/dev/i2c-3",
},
{
.name = "/dev/i2c-4",
},
{
.name = "/dev/i2c-5",
},
{
.name = "/dev/i2c-6",
},
{
.name = "/dev/i2c-7",
},
{
.name = "/dev/i2c-8",
},
{
.name = "/dev/i2c-9",
},
{
.name = "/dev/i2c-10",
},
{
.name = "/dev/i2c-11",
},
{
.name = "/dev/i2c-12",
},
{
.name = "/dev/i2c-13",
.name = "fpga_bus0",
.type = I2C_BUS_TYPE_FPGA_REG,
.type_specific = &fpga_bus0_reg,
},
{
.name = "/dev/i2c-14",
.name = "fpga_bus1",
.type = I2C_BUS_TYPE_FPGA_REG,
.type_specific = &fpga_bus1_reg,
},
{
.name = "/dev/i2c-15",
.name = "wr_mux_bus",
.type = I2C_TYPE_BITBANG,
.type_specific = &wr_mux_bus_reg,
},
{
.name = "/dev/i2c-16",
.name = "wr_sfp0_link0",
.type = I2C_TYPE_BITBANG,
.type_specific = &wr_link0_reg,
},
{
.name = "/dev/i2c-17",
},
{
.name = "/dev/i2c-18",
},
{
.name = "/dev/i2c-19",
.name = "wr_sfp0_link1",
.type = I2C_TYPE_BITBANG,
.type_specific = &wr_link1_reg,
},
};
......@@ -301,10 +388,10 @@ void shw_sfp_dom_dump(struct shw_sfp_dom *dom)
}
/* Get the SFP ID from the SFP number */
/* Get the SFP ID from the SFP number (0 to 17) */
inline int shw_sfp_id(int num)
{
if (num < 0 || num > WR_SFP_PORT_NB-1)
if (num > 17 || num < 0)
return -1;
return num;
}
......@@ -319,14 +406,23 @@ int32_t shw_sfp_read(int num, uint32_t addr, int off, int len, uint8_t * buf)
if (id < 0)
return -1;
bus = &i2c_buses[id];
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_write(bus, addr, 1, (uint8_t*)&off);
i2c_transfer(bus, addr, 1, 0, (uint8_t *) & off);
/* Do the read */
return i2c_read(bus, addr, len, buf);
return i2c_transfer(bus, addr, 0, len, buf);
}
......@@ -349,7 +445,17 @@ int32_t shw_sfp_write(int num, uint32_t addr, int off, int len, uint8_t * buf)
if (id < 0)
return -1;
bus = &i2c_buses[id];
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);
}
/* Write in a paged mode, 1 byte address */
page[0] = (counter + off) & 0xff;
......@@ -362,9 +468,9 @@ int32_t shw_sfp_write(int num, uint32_t addr, int off, int len, uint8_t * buf)
{
pr_debug("Writing %d bytes to EEPROM address %02x\n",
i, page[0]);
ret = i2c_write(bus, addr, i + 1, page);
ret = i2c_transfer(bus, addr, i + 1, 0, page);
if (ret < 0) {
pr_error("i2c_write error code 0x%x\n",
pr_error("i2c_transfer error code 0x%x\n",
ret);
return -1;
}
......@@ -381,7 +487,7 @@ int32_t shw_sfp_write(int num, uint32_t addr, int off, int len, uint8_t * buf)
{
pr_debug("Writing last %d bytes to EEPROM address %02x\n",
i, page[0]);
ret = i2c_write(bus, addr, i + 1, page);
ret = i2c_transfer(bus, addr, i + 1, 0, page);
if (ret < 0) {
pr_error("i2c_transfer error code 0x%x\n", ret);
return -1;
......@@ -397,7 +503,7 @@ uint32_t shw_sfp_module_scan(void)
int ret;
uint32_t mask = 0;
uint8_t test;
for (i = 0; i < WR_SFP_PORT_NB; i++) {
for (i = 0; i < 18; i++) {
ret = shw_sfp_read(i, 0x50, 0x0, sizeof(test), &test);
if (ret == I2C_DEV_NOT_FOUND)
continue;
......@@ -408,8 +514,6 @@ uint32_t shw_sfp_module_scan(void)
void shw_sfp_gpio_init(void)
{
#if 0
int i;
uint8_t addr = 0x20;
uint8_t conf_output[] = { 0x3, 0x0 };
......@@ -438,12 +542,11 @@ void shw_sfp_gpio_init(void)
shw_sfp_set_generic(i, 0, SFP_LED_WRMODE1 | SFP_LED_WRMODE2);
shw_udelay(7000);
}
#endif
}
void shw_sfp_gpio_set(int num, uint8_t state)
{
#if 0
int id;
int top;
struct i2c_bus *bus;
......@@ -487,13 +590,12 @@ void shw_sfp_gpio_set(int num, uint8_t state)
send[1] = curr;
i2c_transfer(bus, addr, 2, 0, send);
#endif
//pr_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)
{
#if 0
int id;
int top;
struct i2c_bus *bus;
......@@ -529,9 +631,6 @@ uint8_t shw_sfp_gpio_get(int num)
out |= SFP_TX_DISABLE;
return out;
#endif
return 0;
}
int shw_sfp_read_header(int num, struct shw_sfp_header *head)
......@@ -549,7 +648,8 @@ int shw_sfp_read_header(int num, struct shw_sfp_header *head)
return -2;
}
ret = shw_sfp_read(num, I2C_SFP_ADDRESS, 0x0,
ret =
shw_sfp_read(num, I2C_SFP_ADDRESS, 0x0,
sizeof(struct shw_sfp_header), (uint8_t *) head);
if (ret == I2C_DEV_NOT_FOUND) {
pr_error("shw_sfp_read_header: I2C_DEV_NOT_FOUND\n");
......@@ -574,7 +674,8 @@ int shw_sfp_read_dom(int num, struct shw_sfp_dom *dom)
return -2;
}
ret = shw_sfp_read(num, I2C_SFP_DOM_ADDRESS, 0x0,
ret =
shw_sfp_read(num, I2C_SFP_DOM_ADDRESS, 0x0,
sizeof(struct shw_sfp_dom), (uint8_t *) dom);
if (ret == I2C_DEV_NOT_FOUND) {
pr_error("shw_sfp_read_header: I2C_DEV_NOT_FOUND\n");
......
......@@ -9,8 +9,15 @@
#define I2C_SFP_ADDRESS 0x50
// From SFF-8472, but right-shifted one bit as I2C addresses are only 7 bits.
#define I2C_SFP_DOM_ADDRESS 0x51
// Number of SFP ports (20 on WRS v4)
#define WR_SFP_PORT_NB 18
/* The two FPGA buses */
#define WR_FPGA_BUS0 0
#define WR_FPGA_BUS1 1
/* The multiplexer bus */
#define WR_MUX_BUS 2
/* Individual buses. 0 and 1 are weird */
#define WR_SFP0_BUS 3
#define WR_SFP1_BUS 4
extern struct i2c_bus i2c_buses[];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment