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);
292 i2c_data->command_code = CMD_GAS_WRITE;
293 i2c_data->byte_count = (
sizeof(i2c_data->tag)
294 +
sizeof(i2c_data->offset)
298 gas_addr = htobe32(gas_addr);
299 i2c_data->offset = gas_addr;
300 memcpy(&i2c_data->data, src, n);
301 msg.addr = idev->i2c_addr;
303 msg.len =
sizeof(*i2c_data) + n + PEC_BYTE_COUNT;
304 msg.buf = (uint8_t *)i2c_data;
306 i2c_data->data[n] = i2c_msg_pec(&msg, msg.len - PEC_BYTE_COUNT, 0,
309 ret = ioctl(idev->fd, I2C_RDWR, &wdata);
321static uint8_t i2c_gas_write_status_get(
struct switchtec_dev *dev,
325 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
326 struct i2c_msg msgs[2];
327 struct i2c_rdwr_ioctl_data rwdata = {
332 uint8_t command_code = CMD_GET_WRITE_STATUS;
335 uint8_t msg_0_pec, pec;
336 uint8_t retry_count = 0;
338 msgs[0].addr = msgs[1].addr = idev->i2c_addr;
341 msgs[0].buf = &command_code;
343 msgs[1].flags = I2C_M_RD;
345 msgs[1].buf = rx_buf;
348 ret = ioctl(idev->fd, I2C_RDWR, &rwdata);
356 msg_0_pec = i2c_msg_pec(&msgs[0], msgs[0].len, 0,
true);
357 pec = i2c_msg_pec(&msgs[1], msgs[1].len - PEC_BYTE_COUNT,
359 if (rx_buf[0] == tag && rx_buf[2] == pec &&
360 (rx_buf[1] == 0 || rx_buf[1] == GAS_TWI_MRPC_ERR))
366 }
while(retry_count < MAX_STATUS_GET_RETRY);
371static void i2c_gas_write(
struct switchtec_dev *dev,
void __gas *dest,
372 const void *src,
size_t n)
374 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
377 uint8_t retry_count = 0;
381 i2c_gas_data_write(dev, dest, src, n, tag);
382 status = i2c_gas_write_status_get(dev, tag);
383 if (status == 0 || status == GAS_TWI_MRPC_ERR)
389 }
while (retry_count < MAX_RETRY_COUNT);
391 if (retry_count == MAX_RETRY_COUNT)
395static void i2c_gas_write_no_retry(
struct switchtec_dev *dev,
void __gas *dest,
396 const void *src,
size_t n)
398 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
403 i2c_gas_data_write(dev, dest, src, n, tag);
404 status = i2c_gas_write_status_get(dev, tag);
405 if (status == 0 || status == GAS_TWI_MRPC_ERR)
411static void i2c_memcpy_to_gas(
struct switchtec_dev *dev,
void __gas *dest,
412 const void *src,
size_t n)
417 cnt = n > I2C_MAX_WRITE ? I2C_MAX_WRITE : n;
418 i2c_gas_write(dev, dest, src, cnt);
426static uint8_t i2c_gas_data_read(
struct switchtec_dev *dev,
void *dest,
427 const void __gas *src,
size_t n)
430 int pec_index, status_index;
431 uint8_t msg_0_pec, pec;
432 uint8_t retry_count = 0;
434 struct switchtec_i2c *idev = to_switchtec_i2c(dev);
435 uint32_t gas_addr = (uint32_t)(src - (
void __gas *)dev->gas_map);
438 struct i2c_msg msgs[2];
439 struct i2c_rdwr_ioctl_data rwdata = {
445 uint8_t command_code;
449 } __attribute__((packed)) *read_command;
454 uint8_t data_and_tail[];
457 read_command = malloc(
sizeof(*read_command));
458 read_response = malloc(
sizeof(*read_response) + n \
459 + DATA_TAIL_BYTE_COUNT);
461 msgs[0].addr = msgs[1].addr = idev->i2c_addr;
463 msgs[0].len =
sizeof(*read_command);
465 read_command->command_code = CMD_GAS_READ;
466 read_command->byte_count =
sizeof(read_command->offset) \
467 +
sizeof(read_command->data_length);
468 gas_addr = htobe32(gas_addr);
469 read_command->offset = gas_addr;
470 read_command->data_length = n;
471 msgs[0].buf = (uint8_t *)read_command;
473 msgs[1].flags = I2C_M_RD;
474 msgs[1].len =
sizeof(read_response->byte_count) + n + \
475 DATA_TAIL_BYTE_COUNT;
476 msgs[1].buf = (uint8_t *)read_response;
479 ret = ioctl(idev->fd, I2C_RDWR, &rwdata);
483 msg_0_pec = i2c_msg_pec(&msgs[0], msgs[0].len, 0,
true);
484 pec = i2c_msg_pec(&msgs[1], msgs[1].len - PEC_BYTE_COUNT, \
486 pec_index = msgs[1].len -
sizeof(read_response->byte_count) \
488 if (read_response->data_and_tail[ pec_index ] == pec)
492 }
while(retry_count < MAX_RETRY_COUNT);
494 if (retry_count == MAX_RETRY_COUNT)
497 memcpy(dest, read_response->data_and_tail, n);
498 status_index = msgs[1].len -
sizeof(read_response->byte_count) \
499 - DATA_TAIL_BYTE_COUNT;
500 status = read_response->data_and_tail[ status_index ];
512static void i2c_gas_read(
struct switchtec_dev *dev,
void *dest,
513 const void __gas *src,
size_t n)
516 uint8_t retry_count = 0;
519 status = i2c_gas_data_read(dev, dest, src, n);
520 if (status == 0 || status == GAS_TWI_MRPC_ERR)
523 }
while(retry_count < MAX_RETRY_COUNT);
525 if (retry_count == MAX_RETRY_COUNT)
529static void i2c_memcpy_from_gas(
struct switchtec_dev *dev,
void *dest,
530 const void __gas *src,
size_t n)
535 cnt = n > I2C_MAX_READ ? I2C_MAX_READ : n;
536 i2c_gas_read(dev, dest, src, cnt);
544static ssize_t i2c_write_from_gas(
struct switchtec_dev *dev,
int fd,
545 const void __gas *src,
size_t n)
552 i2c_memcpy_from_gas(dev, buf, src, n);
554 ret = write(fd, buf, n);
562static inline uint8_t le8toh(uint8_t x) {
return x; }
564#define create_gas_read(type, suffix) \
565 static type i2c_gas_read ## suffix(struct switchtec_dev *dev, \
569 i2c_memcpy_from_gas(dev, &ret, addr, sizeof(ret)); \
570 return le##suffix##toh(ret); \
573create_gas_read(uint8_t, 8);
574create_gas_read(uint16_t, 16);
575create_gas_read(uint32_t, 32);
576create_gas_read(uint64_t, 64);
578static void i2c_gas_write8(
struct switchtec_dev *dev, uint8_t val,
581 i2c_gas_write(dev, addr, &val,
sizeof(uint8_t));
584static void i2c_gas_write16(
struct switchtec_dev *dev, uint16_t val,
585 uint16_t __gas *addr)
588 i2c_gas_write(dev, addr, &val,
sizeof(uint16_t));
591static void i2c_gas_write32(
struct switchtec_dev *dev, uint32_t val,
592 uint32_t __gas *addr)
595 i2c_gas_write(dev, addr, &val,
sizeof(uint32_t));
598static void i2c_gas_write32_no_retry(
struct switchtec_dev *dev, uint32_t val,
599 uint32_t __gas *addr)
602 i2c_gas_write_no_retry(dev, addr, &val,
sizeof(uint32_t));
605static void i2c_gas_write64(
struct switchtec_dev *dev, uint64_t val,
606 uint64_t __gas *addr)
609 i2c_gas_write(dev, addr, &val,
sizeof(uint64_t));
612static const struct switchtec_ops i2c_ops = {
614 .gas_map = i2c_gas_map,
617 .get_device_id = gasop_get_device_id,
618 .get_fw_version = gasop_get_fw_version,
619 .pff_to_port = gasop_pff_to_port,
620 .port_to_pff = gasop_port_to_pff,
621 .flash_part = gasop_flash_part,
622 .event_summary = gasop_event_summary,
623 .event_ctl = gasop_event_ctl,
624 .event_wait_for = gasop_event_wait_for,
626 .gas_read8 = i2c_gas_read8,
627 .gas_read16 = i2c_gas_read16,
628 .gas_read32 = i2c_gas_read32,
629 .gas_read64 = i2c_gas_read64,
630 .gas_write8 = i2c_gas_write8,
631 .gas_write16 = i2c_gas_write16,
632 .gas_write32 = i2c_gas_write32,
633 .gas_write32_no_retry = i2c_gas_write32_no_retry,
634 .gas_write64 = i2c_gas_write64,
635 .memcpy_to_gas = i2c_memcpy_to_gas,
636 .memcpy_from_gas = i2c_memcpy_from_gas,
637 .write_from_gas = i2c_write_from_gas,
642 struct switchtec_i2c *idev;
644 idev = malloc(
sizeof(*idev));
648 idev->fd = open(path, O_RDWR | O_CLOEXEC);
652 if (check_i2c_device(idev))
655 if (i2c_set_addr(idev, i2c_addr))
658 if (i2c_set_timeout(idev, 10))
661 if (i2c_gas_cap_get(&idev->dev) != TWI_ENHANCED_MODE)
664 if (map_gas(&idev->dev))
667 idev->dev.ops = &i2c_ops;
669 gasop_set_partition_info(&idev->dev);
680struct switchtec_dev *switchtec_open_i2c_by_adapter(
int adapter,
int i2c_addr)
684 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.