27#include "../switchtec_priv.h"
37#include <sys/sysmacros.h>
47#include <linux/i2c-dev.h>
50 struct switchtec_dev dev;
56#define CMD_GET_CAP 0xE0
57#define CMD_GAS_WRITE 0xEA
58#define CMD_GET_WRITE_STATUS 0xE2
59#define CMD_GAS_WRITE_WITH_STATUS 0xE8
60#define CMD_GAS_READ 0xE9
62#define MAX_RETRY_COUNT 100
63#define MAX_STATUS_GET_RETRY 50
64#define PEC_BYTE_COUNT 1
65#define TWI_ENHANCED_MODE 0x80
66#define GAS_TWI_MRPC_ERR 0x20
67#define DATA_TAIL_BYTE_COUNT 2
69#define to_switchtec_i2c(d) \
70 ((struct switchtec_i2c *) \
71 ((char *)d - offsetof(struct switchtec_i2c, dev)))
73static uint8_t get_tag(
struct switchtec_i2c *idev)
82static uint8_t i2c_msg_pec(
struct i2c_msg *msg, uint8_t byte_count,
83 uint8_t oldchksum,
bool init)
86 uint8_t addr = (msg->addr << 1) | msg->flags;
87 uint8_t pec = crc8(&addr, 1, oldchksum, init);
88 return crc8(msg->buf, byte_count, pec,
false);
91static int dev_to_sysfs_path(
struct switchtec_i2c *idev,
const char *suffix,
92 char *buf,
size_t buflen)
97 ret = fstat(idev->fd, &stat);
101 snprintf(buf, buflen,
102 "/sys/dev/char/%d:%d/%s",
103 major(stat.st_rdev), minor(stat.st_rdev), suffix);
108static int check_i2c_device_supported(
struct switchtec_i2c *idev)
113 ret = ioctl(idev->fd, I2C_FUNCS, &funcs);
117 if (!(funcs & I2C_FUNC_I2C)) {
125static int check_i2c_device(
struct switchtec_i2c *idev)
128 char syspath[PATH_MAX];
130 ret = dev_to_sysfs_path(idev,
"device/i2c-dev", syspath,
135 ret = access(syspath, F_OK);
139 return check_i2c_device_supported(idev);
142static int i2c_set_addr(
struct switchtec_i2c *idev,
int i2c_addr)
144 idev->i2c_addr = i2c_addr;
146 return ioctl(idev->fd, I2C_SLAVE, i2c_addr);
149static int i2c_set_timeout(
struct switchtec_i2c *idev,
int time)
151 return ioctl(idev->fd, I2C_TIMEOUT, time);
155#define __force __attribute__((force))
160static void i2c_close(
struct switchtec_dev *dev)
162 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
165 munmap((
void __force *)dev->gas_map, dev->gas_map_size);
171static int map_gas(
struct switchtec_dev *dev)
174 dev->gas_map_size = 4 << 20;
185 addr = mmap(NULL, dev->gas_map_size, PROT_NONE,
186 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
187 if (addr == MAP_FAILED)
190 dev->gas_map = (
gasptr_t __force)addr;
197static gasptr_t i2c_gas_map(
struct switchtec_dev *dev,
int writeable,
201 *map_size = dev->gas_map_size;
206static uint8_t i2c_gas_cap_get(
struct switchtec_dev *dev)
209 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
211 struct i2c_msg msgs[2];
212 struct i2c_rdwr_ioctl_data rwdata = {
217 uint8_t command_code = CMD_GET_CAP;
219 uint8_t msg_0_pec, pec;
220 uint8_t retry_count = 0;
222 msgs[0].addr = msgs[1].addr = idev->i2c_addr;
225 msgs[0].buf = &command_code;
227 msgs[1].flags = I2C_M_RD;
229 msgs[1].buf = rx_buf;
232 ret = ioctl(idev->fd, I2C_RDWR, &rwdata);
236 msg_0_pec = i2c_msg_pec(&msgs[0], msgs[0].len, 0,
true);
237 pec = i2c_msg_pec(&msgs[1], msgs[1].len - PEC_BYTE_COUNT,
239 if (rx_buf[1] == pec)
243 }
while (retry_count < MAX_RETRY_COUNT);
246 if (retry_count == MAX_RETRY_COUNT)
249 return (rx_buf[0] & TWI_ENHANCED_MODE);
259#define I2C_MAX_WRITE 24
264#define I2C_MAX_READ 24
266static uint8_t i2c_gas_data_write(
struct switchtec_dev *dev,
void __gas *dest,
267 const void *src,
size_t n, uint8_t tag)
270 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
273 struct i2c_rdwr_ioctl_data wdata = {
279 uint8_t command_code;
284 } __attribute__((packed)) *i2c_data;
286 uint32_t gas_addr = (uint32_t)(dest - (
void __gas *)dev->gas_map);
287 assert(n <= I2C_MAX_WRITE);
290 i2c_data = malloc(
sizeof(*i2c_data) + n + PEC_BYTE_COUNT);
294 i2c_data->command_code = CMD_GAS_WRITE;
295 i2c_data->byte_count = (
sizeof(i2c_data->tag)
296 +
sizeof(i2c_data->offset)
300 gas_addr = htobe32(gas_addr);
301 i2c_data->offset = gas_addr;
302 memcpy(&i2c_data->data, src, n);
303 msg.addr = idev->i2c_addr;
305 msg.len =
sizeof(*i2c_data) + n + PEC_BYTE_COUNT;
306 msg.buf = (uint8_t *)i2c_data;
308 i2c_data->data[n] = i2c_msg_pec(&msg, msg.len - PEC_BYTE_COUNT, 0,
311 ret = ioctl(idev->fd, I2C_RDWR, &wdata);
323static uint8_t i2c_gas_write_status_get(
struct switchtec_dev *dev,
327 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
328 struct i2c_msg msgs[2];
329 struct i2c_rdwr_ioctl_data rwdata = {
334 uint8_t command_code = CMD_GET_WRITE_STATUS;
337 uint8_t msg_0_pec, pec;
338 uint8_t retry_count = 0;
340 msgs[0].addr = msgs[1].addr = idev->i2c_addr;
343 msgs[0].buf = &command_code;
345 msgs[1].flags = I2C_M_RD;
347 msgs[1].buf = rx_buf;
350 ret = ioctl(idev->fd, I2C_RDWR, &rwdata);
358 msg_0_pec = i2c_msg_pec(&msgs[0], msgs[0].len, 0,
true);
359 pec = i2c_msg_pec(&msgs[1], msgs[1].len - PEC_BYTE_COUNT,
361 if (rx_buf[0] == tag && rx_buf[2] == pec &&
362 (rx_buf[1] == 0 || rx_buf[1] == GAS_TWI_MRPC_ERR))
368 }
while (retry_count < MAX_STATUS_GET_RETRY);
373static void i2c_gas_write(
struct switchtec_dev *dev,
void __gas *dest,
374 const void *src,
size_t n)
376 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
379 uint8_t retry_count = 0;
383 i2c_gas_data_write(dev, dest, src, n, tag);
384 status = i2c_gas_write_status_get(dev, tag);
385 if (status == 0 || status == GAS_TWI_MRPC_ERR)
391 }
while (retry_count < MAX_RETRY_COUNT);
393 if (retry_count == MAX_RETRY_COUNT)
397static void i2c_gas_write_no_retry(
struct switchtec_dev *dev,
void __gas *dest,
398 const void *src,
size_t n)
400 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
403 uint8_t retry_count = 0;
406 i2c_gas_data_write(dev, dest, src, n, tag);
408 status = i2c_gas_write_status_get(dev, tag);
409 if (status == 0 || status == GAS_TWI_MRPC_ERR)
415 }
while (retry_count < MAX_RETRY_COUNT);
420static void i2c_memcpy_to_gas(
struct switchtec_dev *dev,
void __gas *dest,
421 const void *src,
size_t n)
426 cnt = n > I2C_MAX_WRITE ? I2C_MAX_WRITE : n;
427 i2c_gas_write(dev, dest, src, cnt);
435static uint8_t i2c_gas_data_read(
struct switchtec_dev *dev,
void *dest,
436 const void __gas *src,
size_t n)
439 int pec_index, status_index;
440 uint8_t msg_0_pec, pec;
441 uint8_t retry_count = 0;
443 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
444 uint32_t gas_addr = (uint32_t)(src - (
void __gas *)dev->gas_map);
447 struct i2c_msg msgs[2];
448 struct i2c_rdwr_ioctl_data rwdata = {
454 uint8_t command_code;
458 } __attribute__((packed)) *read_command;
463 uint8_t data_and_tail[];
466 read_command = malloc(
sizeof(*read_command));
469 read_response = malloc(
sizeof(*read_response) + n \
470 + DATA_TAIL_BYTE_COUNT);
471 if (!read_response) {
476 msgs[0].addr = msgs[1].addr = idev->i2c_addr;
478 msgs[0].len =
sizeof(*read_command);
480 read_command->command_code = CMD_GAS_READ;
481 read_command->byte_count =
sizeof(read_command->offset) \
482 +
sizeof(read_command->data_length);
483 gas_addr = htobe32(gas_addr);
484 read_command->offset = gas_addr;
485 read_command->data_length = n;
486 msgs[0].buf = (uint8_t *)read_command;
488 msgs[1].flags = I2C_M_RD;
489 msgs[1].len =
sizeof(read_response->byte_count) + n + \
490 DATA_TAIL_BYTE_COUNT;
491 msgs[1].buf = (uint8_t *)read_response;
494 ret = ioctl(idev->fd, I2C_RDWR, &rwdata);
498 msg_0_pec = i2c_msg_pec(&msgs[0], msgs[0].len, 0,
true);
499 pec = i2c_msg_pec(&msgs[1], msgs[1].len - PEC_BYTE_COUNT, \
501 pec_index = msgs[1].len -
sizeof(read_response->byte_count) \
503 if (read_response->data_and_tail[ pec_index ] == pec)
507 }
while (retry_count < MAX_RETRY_COUNT);
509 if (retry_count == MAX_RETRY_COUNT)
512 memcpy(dest, read_response->data_and_tail, n);
513 status_index = msgs[1].len -
sizeof(read_response->byte_count) \
514 - DATA_TAIL_BYTE_COUNT;
515 status = read_response->data_and_tail[ status_index ];
527static void i2c_gas_read(
struct switchtec_dev *dev,
void *dest,
528 const void __gas *src,
size_t n)
531 uint8_t retry_count = 0;
534 status = i2c_gas_data_read(dev, dest, src, n);
535 if (status == 0 || status == GAS_TWI_MRPC_ERR)
538 }
while (retry_count < MAX_RETRY_COUNT);
540 if (retry_count == MAX_RETRY_COUNT)
544static void i2c_memcpy_from_gas(
struct switchtec_dev *dev,
void *dest,
545 const void __gas *src,
size_t n)
550 cnt = n > I2C_MAX_READ ? I2C_MAX_READ : n;
551 i2c_gas_read(dev, dest, src, cnt);
559static ssize_t i2c_write_from_gas(
struct switchtec_dev *dev,
int fd,
560 const void __gas *src,
size_t n)
569 i2c_memcpy_from_gas(dev, buf, src, n);
571 ret = write(fd, buf, n);
579static inline uint8_t le8toh(uint8_t x) {
return x; }
581#define create_gas_read(type, suffix) \
582 static type i2c_gas_read ## suffix(struct switchtec_dev *dev, \
586 i2c_memcpy_from_gas(dev, &ret, addr, sizeof(ret)); \
587 return le##suffix##toh(ret); \
590create_gas_read(uint8_t, 8);
591create_gas_read(uint16_t, 16);
592create_gas_read(uint32_t, 32);
593create_gas_read(uint64_t, 64);
595static void i2c_gas_write8(
struct switchtec_dev *dev, uint8_t val,
598 i2c_gas_write(dev, addr, &val,
sizeof(uint8_t));
601static void i2c_gas_write16(
struct switchtec_dev *dev, uint16_t val,
602 uint16_t __gas *addr)
605 i2c_gas_write(dev, addr, &val,
sizeof(uint16_t));
608static void i2c_gas_write32(
struct switchtec_dev *dev, uint32_t val,
609 uint32_t __gas *addr)
612 i2c_gas_write(dev, addr, &val,
sizeof(uint32_t));
615static void i2c_gas_write32_no_retry(
struct switchtec_dev *dev, uint32_t val,
616 uint32_t __gas *addr)
619 i2c_gas_write_no_retry(dev, addr, &val,
sizeof(uint32_t));
622static void i2c_gas_write64(
struct switchtec_dev *dev, uint64_t val,
623 uint64_t __gas *addr)
626 i2c_gas_write(dev, addr, &val,
sizeof(uint64_t));
629static const struct switchtec_ops i2c_ops = {
631 .gas_map = i2c_gas_map,
634 .get_device_id = gasop_get_device_id,
635 .get_fw_version = gasop_get_fw_version,
636 .get_device_version = gasop_get_device_version,
637 .pff_to_port = gasop_pff_to_port,
638 .port_to_pff = gasop_port_to_pff,
639 .flash_part = gasop_flash_part,
640 .event_summary = gasop_event_summary,
641 .event_ctl = gasop_event_ctl,
642 .event_wait_for = gasop_event_wait_for,
644 .gas_read8 = i2c_gas_read8,
645 .gas_read16 = i2c_gas_read16,
646 .gas_read32 = i2c_gas_read32,
647 .gas_read64 = i2c_gas_read64,
648 .gas_write8 = i2c_gas_write8,
649 .gas_write16 = i2c_gas_write16,
650 .gas_write32 = i2c_gas_write32,
651 .gas_write32_no_retry = i2c_gas_write32_no_retry,
652 .gas_write64 = i2c_gas_write64,
653 .memcpy_to_gas = i2c_memcpy_to_gas,
654 .memcpy_from_gas = i2c_memcpy_from_gas,
655 .write_from_gas = i2c_write_from_gas,
660 struct switchtec_i2c *idev;
662 idev = malloc(
sizeof(*idev));
666 idev->fd = open(path, O_RDWR | O_CLOEXEC);
670 if (check_i2c_device(idev))
673 if (i2c_set_addr(idev, i2c_addr))
676 if (i2c_set_timeout(idev, 10))
679 if (i2c_gas_cap_get(&idev->dev) != TWI_ENHANCED_MODE)
682 if (map_gas(&idev->dev))
685 idev->dev.ops = &i2c_ops;
687 gasop_set_partition_info(&idev->dev);
698struct switchtec_dev *switchtec_open_i2c_by_adapter(
int adapter,
int i2c_addr)
702 sprintf(path,
"/dev/i2c-%d", adapter);
struct switchtec_dev * switchtec_open_i2c(const char *path, int i2c_addr)
Open a switchtec device behind an I2C device.
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.