firmware/general/package/hisilicon-osdrv-serdes/src/serdes_comm.c

379 lines
9.5 KiB
C
Executable File

/*
* copyright (c) hisilicon technologies co., ltd. 2016-2019. all rights reserved.
* description: serdes_comm.c
* author: hisilicon multimedia software group
* create: 2019-05-17
*/
#include <linux/i2c.h>
#include <asm/uaccess.h>
#ifndef __HuaweiLite__
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/miscdevice.h>
#else
#include "i2c.h"
#endif
#include "hi_serdes.h"
#include "serdes_comm.h"
#include "type.h"
#define SERDES_DEV_NAME0 "serdes0"
#define SERDES_DEV_NAME1 "serdes1"
#define SERDES_I2C_BUF_MAX_NUM 8
#define ONT_BYTE_EQUAL_BIT_NUM 8
unsigned char sensor_dev_addr[SERDES_MAX_NUM] = {0};
unsigned char serdes_init_flag[SERDES_MAX_NUM] = {0};
static struct i2c_client *g_serdes_client[SERDES_MAX_NUM];
#ifndef __HuaweiLite__
static struct i2c_board_info g_thine_info =
{
I2C_BOARD_INFO(THINE_DEVICE_TYPE, (THINE_DEVICE_ADDR >> 1)),
};
#endif
int serdes_i2c_write(unsigned char i2c_dev, unsigned char dev_addr,
unsigned int reg_addr, unsigned int reg_addr_num,
unsigned int data, unsigned int data_byte_num)
{
unsigned char tmp_buf[SERDES_I2C_BUF_MAX_NUM];
int ret = 0;
int idx = 0;
unsigned int try_count = 0;
struct i2c_client client;
if (i2c_dev >= SERDES_MAX_NUM) {
return HI_FAILURE;
}
if (g_serdes_client[i2c_dev] == NULL) {
return HI_FAILURE;
}
osal_memcpy(&client, g_serdes_client[i2c_dev], sizeof(struct i2c_client));
#ifdef __HuaweiLite__
client.addr = ((dev_addr >> 1) & 0xff);
/* reg_addr config */
if (reg_addr_num == 1) {
client.flags &= ~I2C_M_16BIT_REG;
tmp_buf[idx++] = reg_addr & 0xff;
} else {
client.flags |= I2C_M_16BIT_REG;
tmp_buf[idx++] = (reg_addr >> ONT_BYTE_EQUAL_BIT_NUM) & 0xff;
tmp_buf[idx++] = reg_addr & 0xff;
}
/* data config */
if (data_byte_num == 1) {
client.flags &= ~I2C_M_16BIT_DATA;
tmp_buf[idx++] = data & 0xff;
} else {
client.flags |= I2C_M_16BIT_DATA;
tmp_buf[idx++] = (data >> ONT_BYTE_EQUAL_BIT_NUM) & 0xff;
tmp_buf[idx++] = data & 0xff;
}
#else
client.addr = (dev_addr >> 1);
/* reg_addr config */
if (reg_addr_num == 1) {
tmp_buf[idx++] = reg_addr & 0xff;
} else {
tmp_buf[idx++] = (reg_addr >> ONT_BYTE_EQUAL_BIT_NUM) & 0xff;
tmp_buf[idx++] = reg_addr & 0xff;
}
/* data config */
if (data_byte_num == 1) {
tmp_buf[idx++] = data & 0xff;
} else {
tmp_buf[idx++] = (data >> ONT_BYTE_EQUAL_BIT_NUM) & 0xff;
tmp_buf[idx++] = data & 0xff;
}
#endif
while (1) {
ret = hi_i2c_master_send(&client, (const char *)tmp_buf, idx);
if (ret == idx) {
break;
}
#ifdef __HuaweiLite__
else if ((ret == -EAGAIN))
#else
else if ((ret == -EAGAIN) && (in_atomic() || irqs_disabled()))
#endif
{
try_count++;
if (try_count > 5) {
return HI_FAILURE;
}
} else {
osal_printk("[%s %d] hi_i2c_master_send error, reg_addr=0x%x, ret=%d.\n", __func__, __LINE__, reg_addr, ret);
return ret;
}
}
return HI_SUCCESS;
}
int serdes_i2c_read(unsigned char i2c_dev, const unsigned char dev_addr,
const unsigned int reg_addr, unsigned int *data)
{
int ret;
static struct i2c_msg msg[2];
unsigned char buffer[2];
struct i2c_client client;
if (i2c_dev >= SERDES_MAX_NUM) {
return HI_FAILURE;
}
if (g_serdes_client[i2c_dev] == NULL) {
return HI_FAILURE;
}
osal_memcpy(&client, g_serdes_client[i2c_dev], sizeof(struct i2c_client));
buffer[0] = reg_addr >> ONT_BYTE_EQUAL_BIT_NUM & 0xFF;
buffer[1] = reg_addr & 0xFF;
msg[0].addr = client.addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = buffer;
msg[1].addr = client.addr;
msg[1].flags = client.flags | I2C_M_RD;
msg[1].len = 1;
msg[1].buf = buffer;
ret = hi_i2c_transfer(client.adapter, msg, 2);
if (ret != 2) {
osal_printk("[%s %d] hi_i2c_transfer error, ret=%d.\n", __FUNCTION__, __LINE__, ret);
return HI_FAILURE;
}
*data = buffer[0];
return HI_SUCCESS;
}
static int check_serdes_dev_attr(serdes_dev_attr_t* p_attr)
{
if (p_attr->devno >= SERDES_MAX_NUM) {
osal_printk("invalid serdes_dev number(%d)!\n", p_attr->devno);
return HI_FAILURE;
}
if (p_attr->sendes_en != HI_TRUE && p_attr->sendes_en != HI_FALSE) {
osal_printk("invalid serdes_en(%d)!\n", p_attr->sendes_en);
return HI_FAILURE;
}
if (p_attr->serdes_mode < SERDES_MODE_4LANE_LINEAR ||
p_attr->serdes_mode >= SERDES_MODE_BUTT) {
osal_printk("invalid serdes_mode(%d)!\n", p_attr->serdes_mode);
return HI_FAILURE;
}
return HI_SUCCESS;
}
#ifdef __HuaweiLite__
static int serdes_ioctl(struct file *file, int cmd, unsigned long arg)
#else
static long serdes_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#endif
{
int ret;
serdes_dev_attr_t __user *argp = (serdes_dev_attr_t __user *)arg;
serdes_dev_attr_t serdes_dev_attr;
if (copy_from_user(&serdes_dev_attr, argp, sizeof(serdes_dev_attr_t))) {
return -EFAULT;
}
switch (cmd) {
case HI_SERDES_START:
ret = check_serdes_dev_attr(&serdes_dev_attr);
if (ret < 0) {
return ret;
}
sensor_dev_addr[serdes_dev_attr.devno] = serdes_dev_attr.sensor_i2c_addr;
if ((serdes_dev_attr.sendes_en == HI_TRUE) &&
(serdes_init_flag[serdes_dev_attr.devno] == HI_FALSE)) {
serdes_thine_init(serdes_dev_attr.devno, serdes_dev_attr.serdes_mode);
serdes_init_flag[serdes_dev_attr.devno] = HI_TRUE;
} else if ((serdes_dev_attr.sendes_en == HI_FALSE) &&
(serdes_init_flag[serdes_dev_attr.devno] == HI_TRUE)){
serdes_thine_exit(serdes_dev_attr.devno);
serdes_init_flag[serdes_dev_attr.devno] = HI_FALSE;
} else {
}
break;
default: {
osal_printk("kernel: no such serdes command %#x!\n", cmd);
return HI_FAILURE;
}
}
return HI_SUCCESS;
}
#ifdef __HuaweiLite__
static int serdes_open(struct file *file)
{
return 0;
}
static int serdes_close(struct file *file)
{
return 0;
}
const static struct file_operations_vfs serdes_fops = {
.open = serdes_open,
.close = serdes_close,
.ioctl = serdes_ioctl
};
static struct i2c_client g_serdes_client_obj[SERDES_MAX_NUM];
static struct i2c_client *hi_serdes_i2c_client_init(int i2c_dev)
{
int ret;
struct i2c_client *i2c_client0 = &g_serdes_client_obj[i2c_dev];
i2c_client0->addr = THINE_DEVICE_ADDR >> 1;
i2c_client0->flags = 0;
ret = client_attach(i2c_client0, i2c_dev);
if (ret) {
dprintf("fail to attach g_serdes_client!\n");
return NULL;
}
return &g_serdes_client_obj[i2c_dev];
}
int serdes_mod_init(void)
{
int i = 0;
int ret;
for (i = 0; i < SERDES_MAX_NUM; i++) {
g_serdes_client[i] = hi_serdes_i2c_client_init(i);
}
ret = register_driver("/dev/serdes0", &serdes_fops, 0666, 0);
if (ret) {
osal_printk("register serdes0 device failed with %#x!\n", ret);
return -1;
}
ret = register_driver("/dev/serdes1", &serdes_fops, 0666, 0);
if (ret) {
osal_printk("register serdes1 device failed with %#x!\n", ret);
return -1;
}
osal_printk("\n====> register serdes0_1 success.\n");
return HI_SUCCESS;
}
void serdes_mod_exit(void)
{
unregister_driver("/dev/serdes1");
unregister_driver("/dev/serdes0");
return;
}
#else
static int serdes_open(struct inode *inode, struct file *file)
{
return 0;
}
static int serdes_close(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations serdes_fops = {
.owner = THIS_MODULE,
.open = serdes_open,
.release = serdes_close,
.unlocked_ioctl = serdes_ioctl,
};
static struct miscdevice serdes_dev0 = {
.minor = MISC_DYNAMIC_MINOR,
.name = SERDES_DEV_NAME0,
.fops = &serdes_fops,
};
static struct miscdevice serdes_dev1 = {
.minor = MISC_DYNAMIC_MINOR,
.name = SERDES_DEV_NAME1,
.fops = &serdes_fops,
};
int serdes_mod_init(void)
{
int i;
int ret;
struct i2c_adapter *i2c_adapter;
for (i = 0; i < SERDES_MAX_NUM; i++) {
i2c_adapter = i2c_get_adapter(i);
if (i2c_adapter == NULL) {
osal_printk("i2c_get_adapter error!\n");
return HI_FAILURE;
}
g_serdes_client[i] = i2c_new_device(i2c_adapter, &g_thine_info);
i2c_put_adapter(i2c_adapter);
}
ret = misc_register(&serdes_dev0);
if (ret != HI_SUCCESS) {
osal_printk("kernel: register serdes_0 device failed!\n");
return HI_FAILURE;
}
ret = misc_register(&serdes_dev1);
if (ret != HI_SUCCESS) {
osal_printk("kernel: register serdes_1 device failed!\n");
return HI_FAILURE;
}
return HI_SUCCESS;
}
void serdes_mod_exit(void)
{
int i;
for (i = 0; i < SERDES_MAX_NUM; i++) {
i2c_unregister_device(g_serdes_client[i]);
}
misc_deregister(&serdes_dev1);
misc_deregister(&serdes_dev0);
return;
}
module_init(serdes_mod_init);
module_exit(serdes_mod_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hisilicon");
#endif