Драйвер i2c-vortex для ЗОСРВ Нейтрино редакции 2020

This commit is contained in:
commit 4e4298f0b9
11 changed files with 633 additions and 0 deletions

3
Makefile Normal file
View File

@ -0,0 +1,3 @@
LIST=hardware
EARLY_DIRS=i2c
include recurse.mk

55
README.md Normal file
View File

@ -0,0 +1,55 @@
## Общая структура I2C драйвера
```
┌───────────────────────────┐
│ │
│ I2C шина │
│ │
└─────────────▴─────────────┘
┌─────────────┴─────────────┐
│ │
│ I2C-драйвер (i2c-*) │
│ │
└───────────────────────────┘
┌─────────────┴─────────────┐
│ │
│ Клиентское приложение │
│ │
└───────────────────────────┘
```
## Дерево исходных кодов
```
|- i2c/
| `- vortex/ - Исходный код I2C-драйвера для vortex86
| |- Makefile - Правила сборки дерева исходников
| `- common.mk - Параметры сборки драйверов
|
`- Makefile - Правила сборки дерева исходников
```
## Сборка драйвера
- Установить и настроить [комплект разработчика](https://help.kpda.ru/help/topic/ru.kpda.doc.dev_tools_ru/html/devkit/devkit.html) для [ЗОСРВ "Нейтрино" редакции 2020](https://help.kpda.ru/help/index.jsp).
- Выполнить команду:
```
make
```
## Запуск драйвера
```
i2c-* [параметры] &
```

2
i2c/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=CLASS
include recurse.mk

8
i2c/vortex/Makefile Normal file
View File

@ -0,0 +1,8 @@
LIST=CPU
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

15
i2c/vortex/common.mk Normal file
View File

@ -0,0 +1,15 @@
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
NAME = i2c-vortex
INSTALLDIR = sbin
include $(MKFILES_ROOT)/qtargets.mk
LIBS = i2c-master
include $(PROJECT_ROOT)/pinfo.mk

408
i2c/vortex/i2c-vortex.c Normal file
View File

@ -0,0 +1,408 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "proto.h"
struct sigevent int_event;
unsigned long int_n = 0;
uintptr_t pci_io;
void *pci;
int i2c_master_getfuncs(i2c_master_funcs_t *funcs, int tabsize) {
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, init, vortex_i2c_init, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, fini, vortex_i2c_fini, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, send, vortex_i2c_send, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, recv, vortex_i2c_recv, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, set_slave_addr, vortex_i2c_set_slave_addr, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, set_bus_speed, vortex_i2c_set_bus_speed, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, version_info, vortex_i2c_version_info, tabsize);
I2C_ADD_FUNC(i2c_master_funcs_t, funcs, driver_info, vortex_i2c_driver_info, tabsize);
return 0;
}
int verbosity = 0;
int vortex_i2c_driver_info(void *hdl, i2c_driver_info_t *info) {
info->speed_mode = I2C_SPEED_STANDARD | I2C_SPEED_FAST;
info->addr_mode = I2C_ADDRFMT_7BIT | I2C_ADDRFMT_10BIT;
return 0;
}
void *vortex_i2c_init(int argc, char *argv[]) {
vortex_i2c_dev_t *dev = NULL;
if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
perror("ThreadCtl");
return NULL;
}
dev = malloc(sizeof(vortex_i2c_dev_t));
if (!dev) {
return NULL;
}
if (-1 == vortex_i2c_parseopts(dev, argc, argv))
goto fail;
if (pthread_mutex_init (&dev->lock, NULL) == -1) {
perror("Unable to initialize lock");
goto fail;
}
I2C_Set_Base(I2C_DEF_BASE_ADDR);
dev->physbase = I2C_Init(I2C_CHANNEL0);
return dev;
fail:
free(dev);
return NULL;
}
i2c_status_t vortex_i2c_recv(void *hdl, void *buf, unsigned int len, unsigned int stop) {
vortex_i2c_dev_t *dev = hdl;
int recvd = len - 1;
unsigned char *buffer = buf;
int rval = I2C_STATUS_DONE;
uint16_t ba;
v_i2c_slogf(_SLOG_INFO, "[%s]", __FUNCTION__);
if (len <= 0)
return I2C_STATUS_DONE;
pthread_mutex_lock(&dev->lock);
I2C_Set_Base(I2C_DEF_BASE_ADDR);
I2C_Start(I2C_CHANNEL0, dev->slave_addr.addr & 0xFE, I2C_NOTLASTBYTE, DEF_TIMEOUT);
I2C_WriteByte(I2C_CHANNEL0, buffer[0], I2C_NOTLASTBYTE, DEF_TIMEOUT);
do {
I2C_Start(I2C_CHANNEL0, dev->slave_addr.addr | 0x01, I2C_NOTLASTBYTE, DEF_TIMEOUT);
I2C_ReadByte(I2C_CHANNEL0, buffer+recvd, I2C_NOTLASTBYTE, DEF_TIMEOUT);
recvd--;
} while (recvd >= 0 && rval != I2C_STATUS_ERROR && rval != I2C_STATUS_NACK);
v_i2c_slogf(_SLOG_INFO, "[%s] *** STAT = 0x%02x", __FUNCTION__, i2c_get_byte(I2C_DATA_REG));
i2c_set_stop(ba);
pthread_mutex_unlock(&dev->lock);
return rval;
}
int vortex_i2c_parseopts(vortex_i2c_dev_t *dev, int argc, char *argv[]) {
int c;
int prev_optind;
int done = 0;
while (!done) {
prev_optind = optind;
c = getopt(argc, argv, "v");
switch (c) {
case 'v':
verbosity++;
break;
case '?':
if (optopt == '-') {
++optind;
break;
}
return -1;
case -1:
if (prev_optind < optind)
return -1;
if (argv[optind] == NULL) {
done = 1;
break;
}
if (*argv[optind] != '-') {
++optind;
break;
}
return -1;
}
}
return 0;
}
int vortex_i2c_set_slave_addr(void *hdl, unsigned int addr, i2c_addrfmt_t fmt) {
vortex_i2c_dev_t *dev = hdl;
if (fmt != I2C_ADDRFMT_7BIT && fmt != I2C_ADDRFMT_10BIT) {
v_i2c_slogf(_SLOG_INFO, "[%s] Invalid address format", __FUNCTION__, addr);
errno = EINVAL;
return -1;
}
v_i2c_slogf(_SLOG_INFO, "[%s] Setting slave address to 0x%x", __FUNCTION__, addr);
dev->slave_addr.addr = addr;
dev->slave_addr.fmt = fmt;
return 0;
}
int v_i2c_slogf(int level, const char *fmt, ...) {
int status = 0;
if (level <= verbosity) {
va_list arg;
va_start(arg, fmt);
status = vslogf(_SLOG_SETCODE(_SLOGC_CHAR, 0), level, fmt, arg);
if (verbosity > 7) {
status = vfprintf(stderr, fmt, arg);
status += fprintf(stderr, "\n");
}
va_end(arg);
}
return status;
}
i2c_status_t vortex_i2c_send(void *hdl, void *buf, unsigned int len, unsigned int stop) {
vortex_i2c_dev_t *dev = hdl;
unsigned char *buffer = buf;
unsigned int transmitted = 0;
int rval = I2C_STATUS_DONE;
uint16_t timeout;
uint16_t ba;
if (len <= 0)
return I2C_STATUS_DONE;
pthread_mutex_lock(&dev->lock);
I2C_Set_Base(I2C_DEF_BASE_ADDR);
ba = I2C_Init(I2C_CHANNEL0);
do {
v_i2c_slogf(_SLOG_INFO, "[%s] *** Sending data byte (0x%02x) to (0x%x)", __FUNCTION__, buffer[transmitted],
dev->slave_addr.addr);
timeout = I2C_Start(I2C_CHANNEL0, dev->slave_addr.addr & 0xFE, I2C_NOTLASTBYTE, DEF_TIMEOUT);
timeout = I2C_WriteByte(I2C_CHANNEL0, buffer[transmitted], I2C_NOTLASTBYTE, DEF_TIMEOUT);
if(timeout == 0) {
pthread_mutex_unlock(&dev->lock);
return 0;
}
v_i2c_slogf(_SLOG_INFO, "[%s] 0", __FUNCTION__);
} while (++transmitted < len);
i2c_set_stop(ba);
pthread_mutex_unlock(&dev->lock);
return rval;
}
void vortex_i2c_fini(void *hdl) {
vortex_i2c_dev_t *dev = hdl;
pthread_mutex_destroy(&dev->lock);
free(dev);
}
int vortex_i2c_set_bus_speed(void *hdl, unsigned int speed, unsigned int *ospeed) {
vortex_i2c_dev_t *dev = hdl;
if (ospeed)
*ospeed = dev->scl_freq;
if (speed == dev->scl_freq) {
return 0;
}
dev->scl_freq = speed;
return 0;
}
int vortex_i2c_version_info(i2c_libversion_t *version) {
version->major = I2CLIB_VERSION_MAJOR;
version->minor = I2CLIB_VERSION_MINOR;
version->revision = I2CLIB_REVISION;
return 0;
}
void I2C_Set_Base(uint16_t ba) {
uint32_t pwr_dat;
pci_read_config(pci, I2C_BASE_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pwr_dat = (pwr_dat & 0xFFFF0000) | 0x80000000 | (ba & 0xFFF0);
pci_write_config(pci, I2C_BASE_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
}
uint16_t inline i2c_get_base(uint8_t channel) {
uint32_t pwr_dat;
pci_read_config(pci, I2C_BASE_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pwr_dat = (pwr_dat & 0xFFF0) + ((0 << 3) & 0x08);
return pwr_dat;
}
uint16_t I2C_Init(uint8_t channel) {
uint16_t ba;
uint32_t pwr_dat;
int pd;
channel = I2C_CHANNEL0;
vortex_i2c_dev_t *dev;
struct pci_dev_info;
pd = pci_attach(0);
if (-1 == pd) {
perror("pci_attach");
return EXIT_FAILURE;
}
dev->vendor = 0x17f3;
dev->device = 0x6035;
attach_to_device(dev->vendor, dev->device);
I2C_Set_Base(I2C_DEF_BASE_ADDR);
I2C_Power_On(channel);
ba = i2c_get_base(channel);
i2c_reset(ba);
out8(ba + I2C_CLK_CONTROL1, I2C_CLK1_400);
out8(ba + I2C_CLK_CONTROL2, I2C_CLK2_VAL);
out8(ba + I2C_STATUS, I2C_DEF_STATUS);
out8(ba + I2C_CONTROL, I2C_DEF_CONTROL);
pci_read_config(pci, VORTEX_ICNT_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pwr_dat |= 0x00000100;
pci_write_config(pci, VORTEX_ICNT_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
return ba;
}
void I2C_Power_On(uint8_t channel) {
uint32_t pwr_dat;
pci_read_config(pci, I2C_PINMODE_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pwr_dat |= (0x02 << (channel & 0x01));
pci_write_config(pci, I2C_PINMODE_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pci_read_config(pci, I2C_PWR_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
pwr_dat &= (~(0x10000 << (channel & 0x01)));
pci_write_config(pci, I2C_PWR_PCICFG, 1, sizeof(pwr_dat), &pwr_dat);
}
uint16_t I2C_Start(uint8_t channel, uint8_t addr, uint8_t gen_stop, uint16_t timeout) {
uint16_t ba;
ba = i2c_get_base(channel);
i2c_clr_rx(ba);
i2c_clr_tx(ba);
i2c_put_addr(ba, addr);
timeout = i2c_wait_tx(ba, timeout);
if(timeout == 0) {
return 0;
};
i2c_clr_tx(ba);
if(addr & 0x01) i2c_get_byte(ba);
if(gen_stop) {
i2c_set_stop(ba);
}
return timeout;
}
uint16_t I2C_WriteByte(uint8_t channel, uint8_t dat, uint8_t lastbyte, uint16_t timeout) {
uint16_t ba;
ba = i2c_get_base(channel);
i2c_put_byte(ba, dat);
timeout = i2c_wait_tx(ba, timeout);
if(timeout == 0) return 0;
i2c_clr_tx(ba);
if(lastbyte) {
i2c_set_stop(ba);
}
return timeout;
}
uint16_t I2C_ReadByte(uint8_t channel, uint8_t * dat, uint8_t lastbyte, uint16_t timeout) {
uint16_t ba;
ba = i2c_get_base(channel);
timeout = i2c_wait_rx(ba, timeout);
if(timeout == 0) return 0;
if(lastbyte) i2c_set_stop(ba);
*dat = i2c_get_byte(ba);
i2c_clr_rx(ba);
if(lastbyte == 0) i2c_get_byte(ba);
return timeout;
}
void inline i2c_clr_rx(uint16_t ba) {
out8(ba + I2C_STATUS, I2C_RX_RDY);
}
void inline i2c_put_addr(uint16_t ba, uint8_t addr) {
out8(ba + I2C_RW_ADDR, addr);
}
void inline i2c_put_byte(uint16_t ba, uint8_t dat) {
out8(ba + I2C_DATA_REG, dat);
}
void inline i2c_clr_tx(uint16_t ba) {
out8(ba + I2C_STATUS, I2C_TX_DONE);
}
uint16_t inline i2c_wait_tx(uint16_t ba, uint16_t timeout) {
while(timeout > 0) {
if(in8(ba + I2C_STATUS) & I2C_TX_DONE) break;
nanospin_ns(15000);
timeout = timeout - 1;
}
return timeout;
}
uint16_t inline i2c_wait_rx(uint16_t ba, uint16_t timeout) {
while(timeout > 0) {
if(in8(ba + I2C_STATUS) & I2C_RX_RDY) break;
nanospin_ns(15000);
timeout = timeout - 1;
}
return timeout;
}
uint8_t inline i2c_get_byte(uint16_t ba) {
return in8(ba + I2C_DATA_REG);
}
void inline i2c_set_stop(uint16_t ba) {
out8(ba + I2C_CONTROL, I2C_TX_STOP);
}
void inline i2c_reset(uint16_t ba) {
out8(ba + I2C_EX_CONTROL, in8(ba + I2C_EX_CONTROL) | 0x80);
}
int attach_to_device(unsigned vendor, unsigned device)
{
struct pci_dev_info pci_info;
memset(&pci_info, 0, sizeof pci_info);
pci_info.VendorId = vendor;
pci_info.DeviceId = device;
pci = pci_attach_device(NULL, PCI_SEARCH_VENDEV|PCI_SHARE, 0, &pci_info);
if (pci == NULL) {
printf("Device not found. Error: %s \n\n", strerror(errno));
return 0;
}
pci_attach_device(pci, PCI_INIT_ALL|PCI_INIT_ROM|PCI_SHARE, 0, &pci_info);
int_n = pci_info.Irq;
return 1;
}

View File

@ -0,0 +1,7 @@
%C - I2C bus resource manager for vortex86 family
Usage: i2c-vortex [-v*]
Options:
-v Verbose (for debugging purposes)

3
i2c/vortex/pinfo.mk Normal file
View File

@ -0,0 +1,3 @@
define PINFO
PINFO DESCRIPTION=I2C bus driver for the vortex86
endef

129
i2c/vortex/proto.h Executable file
View File

@ -0,0 +1,129 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef PROTO_H_
#define PROTO_H_
#include <stdio.h>
#include <stdint.h>
//#include <i86.h>
#include <sys/neutrino.h>
#include <sys/mman.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <hw/inout.h>
#include <hw/i2c.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <hw/pci.h>
#include <hw/pci_devices.h>
/* logging support */
#include <stdarg.h>
#include <sys/slog.h>
#include <sys/slogcodes.h>
#define I2C_CONTROL 0x00
#define I2C_STATUS 0x01
#define I2C_SL_ADDR 0x02
#define I2C_RW_ADDR 0x03
#define I2C_DATA_REG 0x04
#define I2C_CLK_CONTROL1 0x05
#define I2C_CLK_CONTROL2 0x06
#define I2C_EX_CONTROL 0x07
//#define I2C_TIMEOUT 20000
//SCL CLK=400kHz
#define I2C_CLK1_400 0x26
//SCL CLK=100kHz
#define I2C_CLK1_100 0x98
#define I2C_CLK2_VAL 0x81
#define I2C_TX_DONE 0x20
#define I2C_TX_STOP 0x02
#define I2C_NACK_EN 0x01
#define I2C_RX_RDY 0x40
#define I2C_DEF_STATUS 0xFC
#define I2C_DEF_CONTROL 0x00
#define I2C_DEF_BASE_ADDR 0xF100
#define one_ms_in_ticks 1190
#define I2C_BASE_PCICFG 0x000000D4
#define I2C_PINMODE_PCICFG 0x000000CC
#define I2C_PWR_PCICFG 0x000000BC
#define VORTEX_ICNT_PCICFG 0x800000A0
#define VORTEX_ICNT_START 0x498
#define VORTEX_ICNT_STOP 0x499
#define VORTEX_ICNT_VALUE 0x494
#define TICKS_IN_1USEC 600
#define TICKS_IN_10USEC 6000
#define I2C_CHANNEL0 0
#define I2C_CHANNEL1 1
#define I2C_LASTBYTE 1
#define I2C_NOTLASTBYTE 0
#define DEF_TIMEOUT 200
typedef struct {
unsigned physbase;
unsigned scl_freq;
i2c_addr_t slave_addr;
pthread_mutex_t lock;
uint16_t timeout;
unsigned device;
unsigned vendor;
} vortex_i2c_dev_t;
// uOps
int v_i2c_slogf(int, const char *, ...);
int vortex_i2c_set_bus_speed(void *, unsigned int, unsigned int *);
int vortex_i2c_version_info(i2c_libversion_t *);
int vortex_i2c_driver_info(void *, i2c_driver_info_t *);
int vortex_i2c_set_slave_addr(void *, unsigned int, i2c_addrfmt_t);
void vortex_i2c_fini(void *);
int vortex_i2c_parseopts (vortex_i2c_dev_t*, int, char**);
int attach_to_device (unsigned vendor, unsigned device);
void inline i2c_reset( uint16_t ba );
void inline i2c_clr_status( uint16_t ba );
void inline i2c_put_addr( uint16_t ba, uint8_t addr );
uint8_t inline i2c_get_byte( uint16_t ba );
void inline i2c_put_byte( uint16_t ba, uint8_t dat );
void inline i2c_set_stop( uint16_t ba );
void inline i2c_clr_rx( uint16_t ba );
void inline i2c_clr_tx( uint16_t ba );
void inline i2c_set_stop( uint16_t ba );
uint16_t inline i2c_wait_tx( uint16_t ba, uint16_t timeout );
uint16_t inline i2c_wait_rx( uint16_t ba, uint16_t timeout );
uint32_t inline pci_get_cfg_reg( uint32_t addr );
void inline pci_set_cfg_reg( uint32_t addr, uint32_t dat );
uint16_t inline i2c_get_base( uint8_t channel );
i2c_status_t vortex_i2c_send(void *, void *, unsigned int, unsigned int);
void *vortex_i2c_init(int, char **);
i2c_status_t vortex_i2c_recv(void *, void *, unsigned int, unsigned int);
// API functions
uint16_t I2C_Get_Base();
void I2C_Set_Base( uint16_t ba );
void I2C_Power_On( uint8_t channel );
uint16_t I2C_Init( uint8_t channel ); // set base address if not zero
uint16_t I2C_Start( uint8_t channel, uint8_t addr, uint8_t gen_stop, uint16_t timeout );
uint16_t I2C_ReadByte( uint8_t channel, uint8_t * dat, uint8_t lastbyte, uint16_t timeout );
uint16_t I2C_WriteByte( uint8_t channel, uint8_t dat, uint8_t lastbyte, uint16_t timeout );
#endif /* PROTO_H_ */

2
i2c/vortex/x86/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=VARIANT
include recurse.mk

View File

@ -0,0 +1 @@
include ../../common.mk