Files
KY-XTZ/src/drivers/platform/xilinx_i2c.c
T
2024-11-29 11:44:46 +08:00

515 lines
13 KiB
C

/***************************************************************************//**
* @file xilinx/xilinx_i2c.c
* @brief Implementation of Xilinx I2C Generic Driver.
* @author Antoniu Miclaus ([email protected])
********************************************************************************
* Copyright 2019(c) Analog Devices, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* - Neither the name of Analog Devices, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* - The use of this software may or may not infringe the patent rights
* of one or more patent holders. This license does not release you
* from the requirement that you obtain separate licenses from these
* patent holders to use this software.
* - Use of the software either in source or binary form, must be run
* on or directly connected to an Analog Devices Inc. component.
*
* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdlib.h>
#include <xparameters.h>
#ifdef XPAR_XIIC_NUM_INSTANCES
#include <xiic.h>
#endif
#ifdef XPAR_XIICPS_NUM_INSTANCES
#include <xiicps.h>
#endif
#include "error.h"
#include "i2c.h"
#include "xilinx_i2c.h"
#include "list.h"
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
static struct list_desc *pl_list = NULL;
static struct iterator *pl_it = NULL;
#ifdef XIICPS_H
static struct list_desc *ps_list = NULL;
static struct iterator *ps_it = NULL;
#endif /* XIICPS_H */
/**
* @struct inst_table_item
* @brief Table item for I2C cores already started.
*/
struct inst_table_item {
/** Instance the BSP driver uses for this core. */
void *instance;
/** Core ID in BSP. */
uint32_t device_id;
/** Number of ADI driver instances running on this core. */
uint32_t inst_no;
};
#ifdef XPAR_XIICPS_NUM_INSTANCES
/** Save the current state of the bitrate to not change it each time */
static uint32_t ps_last_bitrate = 0;
#endif
/**
* @brief Xilinx platform specific I2C platform ops structure
*/
const struct i2c_platform_ops xil_i2c_ops = {
.i2c_ops_init = &xil_i2c_init,
.i2c_ops_write = &xil_i2c_write,
.i2c_ops_read = &xil_i2c_read,
.i2c_ops_remove = &xil_i2c_remove
};
/******************************************************************************/
/************************ Functions Definitions *******************************/
/******************************************************************************/
/**
* @brief Compare two I2C cores by ID.
* @param el1 - First I2C core to compare.
* @param el2 - Second I2C core to compare.
* @return \ref 0 if the two cores have the same ID,
* \ref difference between the first and the second IDs otherwise.
*/
static int32_t xil_i2c_cmp(void *el1, void *el2)
{
struct inst_table_item *sel1 = el1, *sel2 = el2;
return (sel1->device_id - sel2->device_id);
}
/**
* @brief Configure slave address and bitrate if needed
* @param desc - Descriptor of the I2C device
* @return 0 in case of success, -1 otherwise.
*/
static int32_t xil_i2c_set_transmission_config(struct i2c_desc *desc)
{
struct xil_i2c_desc *xil_i2c_desc = desc->extra;
#ifdef XIICPS_H
int32_t ret;
#endif
switch (xil_i2c_desc->type) {
case IIC_PL:
#ifdef XIIC_H
break;
#endif
return -1;
case IIC_PS:
#ifdef XIICPS_H
if (desc->max_speed_hz != ps_last_bitrate) {
ret = XIicPs_SetSClk(xil_i2c_desc->instance, desc->max_speed_hz);
if (ret != XST_SUCCESS)
return -1;
ps_last_bitrate = desc->max_speed_hz;
}
break;
#endif
return -1;
default:
return -1;
}
return 0;
}
/**
* @brief Initialize the I2C communication peripheral.
* @param desc - The I2C descriptor.
* @param param - The structure that contains the I2C parameters.
* @return 0 in case of success, -1 otherwise.
*/
int32_t xil_i2c_init(struct i2c_desc **desc,
const struct i2c_init_param *param)
{
int32_t ret;
struct i2c_desc *idesc;
struct xil_i2c_desc *xdesc;
struct xil_i2c_init_param *xinit;
idesc = (struct i2c_desc *)malloc(sizeof(*idesc));
xdesc = (struct xil_i2c_desc *)malloc(sizeof(*xdesc));
if(!idesc || !xdesc)
goto error;
idesc->max_speed_hz = param->max_speed_hz;
idesc->slave_address = param->slave_address;
xinit = param->extra;
idesc->extra = xdesc;
xdesc->type = xinit->type;
xdesc->device_id = xinit->device_id;
switch (xinit->type) {
case IIC_PL:
#ifdef XIIC_H
; /** Intentional empty statement */
struct inst_table_item tab_check_pl;
struct inst_table_item *temp_el_pl;
if (!pl_list)
list_init(&pl_list, LIST_DEFAULT, xil_i2c_cmp);
if (!pl_it)
iterator_init(&pl_it, pl_list, true);
tab_check_pl.device_id = xdesc->device_id;
if (!IS_ERR_VALUE(list_read_find(pl_list, (void **)&temp_el_pl,
&tab_check_pl))) {
xdesc->instance = temp_el_pl->instance;
temp_el_pl->inst_no++;
break;
}
xdesc->instance = (XIic *)malloc(sizeof(XIic));
if(!xdesc->instance)
goto pl_error;
xdesc->config = XIic_LookupConfig(xinit->device_id);
if(xdesc->config == NULL)
goto pl_error;
ret = XIic_CfgInitialize(xdesc->instance,
xdesc->config,
((XIic_Config*)xdesc->config)
->BaseAddress);
if(ret != 0)
goto pl_error;
ret = XIic_Start(xdesc->instance);
if(ret != 0)
goto pl_error;
ret = XIic_SetAddress(xdesc->instance,
XII_ADDR_TO_SEND_TYPE,
param->slave_address);
if(ret != 0)
goto pl_error;
ret = XIic_SelfTest(xdesc->instance);
if(ret < 0)
goto pl_error;
ret = XIic_SetGpOutput(xdesc->instance, 1);
if(ret < 0)
goto pl_error;
temp_el_pl = (struct inst_table_item*)calloc(1, sizeof(*temp_el_pl));
temp_el_pl->device_id = xinit->device_id;
temp_el_pl->inst_no = 1;
temp_el_pl->instance = xdesc->instance;
pl_list->push(pl_list, temp_el_pl);
break;
pl_error:
free(xdesc->instance);
#endif
goto error;
case IIC_PS:
#ifdef XIICPS_H
; /** Intentional empty statement */
struct inst_table_item tab_check_ps;
struct inst_table_item *temp_el_ps;
if (!pl_list)
list_init(&ps_list, LIST_DEFAULT, xil_i2c_cmp);
if (!pl_it)
iterator_init(&ps_it, ps_list, true);
tab_check_ps.device_id = xdesc->device_id;
if (!IS_ERR_VALUE(list_read_find(ps_list,
(void **)&temp_el_ps,
&tab_check_ps))) {
xdesc->instance = temp_el_ps->instance;
temp_el_ps->inst_no++;
break;
}
xdesc->instance = (XIicPs *)malloc(sizeof(XIicPs));
if(!xdesc->instance)
goto ps_error;
xdesc->config = XIicPs_LookupConfig(xinit->device_id);
if(xdesc->config == NULL)
goto ps_error;
ret = XIicPs_CfgInitialize(xdesc->instance,
xdesc->config,
((XIicPs_Config*)xdesc->config)
->BaseAddress);
if(ret != 0)
goto ps_error;
XIicPs_SetSClk(xdesc->instance, param->max_speed_hz);
temp_el_ps = (struct inst_table_item*)calloc(1, sizeof(*temp_el_ps));
temp_el_ps->device_id = xinit->device_id;
temp_el_ps->inst_no = 1;
temp_el_ps->instance = xdesc->instance;
ps_list->push(ps_list, temp_el_ps);
break;
ps_error:
free(xdesc->instance);
#endif
goto error;
default:
goto error;
break;
}
*desc = idesc;
return 0;
error:
free(idesc);
free(xdesc);
return -1;
}
/**
* @brief Free the resources allocated by i2c_init().
* @param desc - The I2C descriptor.
* @return 0 in case of success, -1 otherwise.
*/
int32_t xil_i2c_remove(struct i2c_desc *desc)
{
struct xil_i2c_desc *xdesc;
int32_t ret;
xdesc = desc->extra;
switch (xdesc->type) {
case IIC_PL:
#ifdef XIIC_H
; /** Intentional empty statement */
struct inst_table_item tab_check_pl;
struct inst_table_item *temp_el_pl;
tab_check_pl.device_id = xdesc->device_id;
ret = iterator_find(pl_it, &tab_check_pl);
if (ret != 0)
goto error;
iterator_read(pl_it, (void **)&temp_el_pl);
temp_el_pl->inst_no--;
if (temp_el_pl->inst_no != 0)
break;
ret = XIic_Stop(((XIic *)xdesc->instance));
if(ret != 0)
goto error;
/** Remove list element */
iterator_get(pl_it, (void **)&temp_el_pl);
free(temp_el_pl);
break;
#endif
goto error;
case IIC_PS:
#ifdef XIICPS_H
; /** Intentional empty statement */
struct inst_table_item tab_check_ps;
struct inst_table_item *temp_el_ps;
tab_check_ps.device_id = xdesc->device_id;
ret = iterator_find(ps_it, &tab_check_ps);
if (ret != 0)
goto error;
iterator_read(ps_it, (void **)&temp_el_ps);
temp_el_ps->inst_no--;
if (temp_el_ps->inst_no != 0)
break;
/** Remove list element */
iterator_get(ps_it, (void **)&temp_el_ps);
free(temp_el_ps);
break;
#endif
/* Intended fallthrough */
error:
default:
return -1;
}
free(desc->extra);
free(desc);
return 0;
}
/**
* @brief Write data to a slave device.
* @param desc - The I2C descriptor.
* @param data - Buffer that stores the transmission data.
* @param bytes_number - Number of bytes to write.
* @param stop_bit - Stop condition control.
* Example: 0 - A stop condition will not be generated;
* 1 - A stop condition will be generated.
* @return 0 in case of success, -1 otherwise.
*/
int32_t xil_i2c_write(struct i2c_desc *desc,
uint8_t *data,
uint8_t bytes_number,
uint8_t stop_bit)
{
struct xil_i2c_desc *xdesc;
int32_t ret;
xdesc = desc->extra;
switch (xdesc->type) {
case IIC_PL:
#ifdef XIIC_H
ret = xil_i2c_set_transmission_config(desc);
if (ret != 0)
return -1;
XIic_Send(((XIic*)xdesc->instance)->BaseAddress,
desc->slave_address,
data,
bytes_number,
stop_bit ? XIIC_STOP : XIIC_REPEATED_START);
break;
#endif
goto error;
case IIC_PS:
#ifdef XIICPS_H
ret = xil_i2c_set_transmission_config(desc);
if (ret != 0)
return -1;
ret = XIicPs_SetOptions(xdesc->instance,
stop_bit ? 0 : XIICPS_REP_START_OPTION);
if(ret != 0)
goto error;
XIicPs_MasterSend(xdesc->instance,
data,
bytes_number,
desc->slave_address);
if(ret != 0)
goto error;
break;
#endif
/* Intended fallthrough */
error:
default:
return -1;
}
return 0;
}
/**
* @brief Read data from a slave device.
* @param desc - The I2C descriptor.
* @param data - Buffer that will store the received data.
* @param bytes_number - Number of bytes to read.
* @param stop_bit - Stop condition control.
* Example: 0 - A stop condition will not be generated;
* 1 - A stop condition will be generated.
* @return 0 in case of success, -1 otherwise.
*/
int32_t xil_i2c_read(struct i2c_desc *desc,
uint8_t *data,
uint8_t bytes_number,
uint8_t stop_bit)
{
struct xil_i2c_desc *xdesc;
int32_t ret;
xdesc = desc->extra;
switch (xdesc->type) {
case IIC_PL:
#ifdef XIIC_H
ret = xil_i2c_set_transmission_config(desc);
if (ret != 0)
return -1;
ret = XIic_Recv(((XIic*)xdesc->instance)->BaseAddress,
desc->slave_address,
data,
bytes_number,
stop_bit ? XIIC_STOP : XIIC_REPEATED_START);
if(ret != bytes_number)
goto error;
break;
#endif
goto error;
case IIC_PS:
#ifdef XIICPS_H
ret = xil_i2c_set_transmission_config(desc);
if (ret != 0)
return -1;
ret = XIicPs_SetOptions(xdesc->instance,
stop_bit ? 0 : XIICPS_REP_START_OPTION);
if(ret != 0)
goto error;
XIicPs_MasterRecv(xdesc->instance,
data,
bytes_number,
desc->slave_address);
if(ret != 0)
goto error;
break;
#endif
/* Intended fallthrough */
error:
default:
return -1;
break;
}
return 0;
}