[pddf]: Adding support for FPGAPCIe in PDDF (#13476)

Why I did it
Some of the platform vendors use FPGA in the HW design. This FPGA is connected to the CPU via PCIe interface. This FPGA also works as an I2C controller having other devices attached to the I2C channels emanating from it. Adding a common module, a driver and a platform specific algorithm module to be used for such FPGA in PDDF.

How I did it
Added 'pddf_fpgapci_module', 'pddf_fpgapci_driver' and a sample algorithm module for Xilinx device 7021. Kernel modules which takes the platform dependent data from PDDF JSON files and initialises the PCIe FPGA. The sample algorithm module can be used by the ODMs in case the communication algorithms are same for their device. Else, they need to come up with similar algo module.

How to verify it
Any platform having such an FPGA and brought up using PDDF would use these kernel modules. The detail representation of such a device in PDDF JSON file is covered in the HLD.
This commit is contained in:
FuzailBrcm 2023-02-07 03:18:31 +05:30 committed by GitHub
parent 346576bcf4
commit 0704ff5e6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1012 additions and 2 deletions

View File

@ -18,7 +18,7 @@ PACKAGE_PRE_NAME := sonic-platform-pddf
KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver
MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver
MODULE_DIR:= modules
UTILS_DIR := utils
SERVICE_DIR := service

View File

@ -1 +1 @@
obj-m := client/ cpld/ cpldmux/ fpgai2c/ xcvr/ mux/ gpio/ psu/ fan/ led/ sysstatus/
obj-m := client/ cpld/ cpldmux/ fpgai2c/ fpgapci/ xcvr/ mux/ gpio/ psu/ fan/ led/ sysstatus/

View File

@ -0,0 +1,4 @@
obj-m := driver/ algos/
obj-m += pddf_fpgapci_module.o
ccflags-y:= -I$(M)/modules/include

View File

@ -0,0 +1,4 @@
# Sample driver for Xilinx 7021 FPGA device
obj-m += pddf_xilinx_device_7021_algo.o
ccflags-y := -I$(M)/modules/include

View File

@ -0,0 +1,417 @@
/*
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* pddf_xilinx_device_7021_algo.c
* Description:
* A sample i2c driver algorithms for Xilinx Corporation Device 7021 FPGA adapters
*
*********************************************************************************/
#define __STDC_WANT_LIB_EXT1__ 1
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include "pddf_i2c_algo.h"
#define DEBUG 0
enum {
STATE_DONE = 0,
STATE_INIT,
STATE_ADDR,
STATE_ADDR10,
STATE_START,
STATE_WRITE,
STATE_READ,
STATE_STOP,
STATE_ERROR,
};
/* registers */
#define FPGAI2C_REG_PRELOW 0
#define FPGAI2C_REG_PREHIGH 1
#define FPGAI2C_REG_CONTROL 2
#define FPGAI2C_REG_DATA 3
#define FPGAI2C_REG_CMD 4 /* write only */
#define FPGAI2C_REG_STATUS 4 /* read only, same address as FPGAI2C_REG_CMD */
#define FPGAI2C_REG_VER 5
#define FPGAI2C_REG_CTRL_IEN 0x40
#define FPGAI2C_REG_CTRL_EN 0x80
#define FPGAI2C_REG_CMD_START 0x91
#define FPGAI2C_REG_CMD_STOP 0x41
#define FPGAI2C_REG_CMD_READ 0x21
#define FPGAI2C_REG_CMD_WRITE 0x11
#define FPGAI2C_REG_CMD_READ_ACK 0x21
#define FPGAI2C_REG_CMD_READ_NACK 0x29
#define FPGAI2C_REG_CMD_IACK 0x01
#define FPGAI2C_REG_STAT_IF 0x01
#define FPGAI2C_REG_STAT_TIP 0x02
#define FPGAI2C_REG_STAT_ARBLOST 0x20
#define FPGAI2C_REG_STAT_BUSY 0x40
#define FPGAI2C_REG_STAT_NACK 0x80
struct fpgalogic_i2c {
void __iomem *base;
u32 reg_shift;
u32 reg_io_width;
wait_queue_head_t wait;
struct i2c_msg *msg;
int pos;
int nmsgs;
int state; /* see STATE_ */
int ip_clock_khz;
int bus_clock_khz;
void (*reg_set)(struct fpgalogic_i2c *i2c, int reg, u8 value);
u8 (*reg_get)(struct fpgalogic_i2c *i2c, int reg);
u32 timeout;
struct mutex lock;
};
static struct fpgalogic_i2c fpgalogic_i2c[I2C_PCI_MAX_BUS];
extern void __iomem * fpga_ctl_addr;
extern int (*ptr_fpgapci_read)(uint32_t);
extern int (*ptr_fpgapci_write)(uint32_t, uint32_t);
extern int (*pddf_i2c_pci_add_numbered_bus)(struct i2c_adapter *, int);
void i2c_get_mutex(struct fpgalogic_i2c *i2c)
{
mutex_lock(&i2c->lock);
}
/**
* i2c_release_mutex - release mutex
*/
void i2c_release_mutex(struct fpgalogic_i2c *i2c)
{
mutex_unlock(&i2c->lock);
}
static void fpgai2c_reg_set_8(struct fpgalogic_i2c *i2c, int reg, u8 value)
{
iowrite8(value, i2c->base + (reg << i2c->reg_shift));
}
static inline u8 fpgai2c_reg_get_8(struct fpgalogic_i2c *i2c, int reg)
{
return ioread8(i2c->base + (reg << i2c->reg_shift));
}
static inline void fpgai2c_reg_set(struct fpgalogic_i2c *i2c, int reg, u8 value)
{
i2c->reg_set(i2c, reg, value);
udelay(100);
}
static inline u8 fpgai2c_reg_get(struct fpgalogic_i2c *i2c, int reg)
{
udelay(100);
return i2c->reg_get(i2c, reg);
}
/*
* i2c_get_mutex must be called prior to calling this function.
*/
static int fpgai2c_poll(struct fpgalogic_i2c *i2c)
{
u8 stat = fpgai2c_reg_get(i2c, FPGAI2C_REG_STATUS);
struct i2c_msg *msg = i2c->msg;
u8 addr;
/* Ready? */
if (stat & FPGAI2C_REG_STAT_TIP)
return -EBUSY;
if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) {
/* Stop has been sent */
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_IACK);
if (i2c->state == STATE_ERROR)
return -EIO;
return 0;
}
/* Error? */
if (stat & FPGAI2C_REG_STAT_ARBLOST) {
i2c->state = STATE_ERROR;
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_STOP);
return -EAGAIN;
}
if (i2c->state == STATE_INIT) {
if (stat & FPGAI2C_REG_STAT_BUSY)
return -EBUSY;
i2c->state = STATE_ADDR;
}
if (i2c->state == STATE_ADDR) {
/* 10 bit address? */
if (i2c->msg->flags & I2C_M_TEN) {
addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6);
i2c->state = STATE_ADDR10;
} else {
addr = (i2c->msg->addr << 1);
i2c->state = STATE_START;
}
/* Set read bit if necessary */
addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0;
fpgai2c_reg_set(i2c, FPGAI2C_REG_DATA, addr);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_START);
return 0;
}
/* Second part of 10 bit addressing */
if (i2c->state == STATE_ADDR10) {
fpgai2c_reg_set(i2c, FPGAI2C_REG_DATA, i2c->msg->addr & 0xff);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_WRITE);
i2c->state = STATE_START;
return 0;
}
if (i2c->state == STATE_START || i2c->state == STATE_WRITE) {
i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
if (stat & FPGAI2C_REG_STAT_NACK) {
i2c->state = STATE_ERROR;
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_STOP);
return -ENXIO;
}
} else {
msg->buf[i2c->pos++] = fpgai2c_reg_get(i2c, FPGAI2C_REG_DATA);
}
if (i2c->pos >= msg->len) {
i2c->nmsgs--;
i2c->msg++;
i2c->pos = 0;
msg = i2c->msg;
if (i2c->nmsgs) {
if (!(msg->flags & I2C_M_NOSTART)) {
i2c->state = STATE_ADDR;
return 0;
} else {
i2c->state = (msg->flags & I2C_M_RD)
? STATE_READ : STATE_WRITE;
}
} else {
i2c->state = STATE_DONE;
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_STOP);
return 0;
}
}
if (i2c->state == STATE_READ) {
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, i2c->pos == (msg->len - 1) ?
FPGAI2C_REG_CMD_READ_NACK : FPGAI2C_REG_CMD_READ_ACK);
} else {
fpgai2c_reg_set(i2c, FPGAI2C_REG_DATA, msg->buf[i2c->pos++]);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_WRITE);
}
return 0;
}
static int fpgai2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct fpgalogic_i2c *i2c = i2c_get_adapdata(adap);
int ret;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
i2c->msg = msgs;
i2c->pos = 0;
i2c->nmsgs = num;
i2c->state = STATE_INIT;
/* Handle the transfer */
while (time_before(jiffies, timeout)) {
i2c_get_mutex(i2c);
ret = fpgai2c_poll(i2c);
i2c_release_mutex(i2c);
if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR)
return (i2c->state == STATE_DONE) ? num : ret;
if (ret == 0)
timeout = jiffies + HZ;
usleep_range(5, 15);
}
printk("[%s] ERROR STATE_ERROR\n", __FUNCTION__);
i2c->state = STATE_ERROR;
return -ETIMEDOUT;
}
static u32 fpgai2c_func(struct i2c_adapter *adap)
{
/* a typical full-I2C adapter would use the following */
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm fpgai2c_algorithm= {
.master_xfer = fpgai2c_xfer, /*write I2C messages */
.functionality = fpgai2c_func, /* what the adapter supports */
};
static int fpgai2c_init(struct fpgalogic_i2c *i2c)
{
int prescale;
int diff;
u8 ctrl;
i2c->reg_set = fpgai2c_reg_set_8;
i2c->reg_get = fpgai2c_reg_get_8;
ctrl = fpgai2c_reg_get(i2c, FPGAI2C_REG_CONTROL);
/* make sure the device is disabled */
fpgai2c_reg_set(i2c, FPGAI2C_REG_CONTROL, ctrl & ~(FPGAI2C_REG_CTRL_EN|FPGAI2C_REG_CTRL_IEN));
/*
* I2C Frequency depends on host clock
* input clock of 100MHz
* prescale to 100MHz / ( 5*100kHz) -1 = 199 = 0x4F 100000/(5*100)-1=199=0xc7
*/
prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1;
prescale = clamp(prescale, 0, 0xffff);
diff = i2c->ip_clock_khz / (5 * (prescale + 1)) - i2c->bus_clock_khz;
if (abs(diff) > i2c->bus_clock_khz / 10) {
printk("[%s] ERROR Unsupported clock settings: core: %d KHz, bus: %d KHz\n",
__FUNCTION__, i2c->ip_clock_khz, i2c->bus_clock_khz);
return -EINVAL;
}
fpgai2c_reg_set(i2c, FPGAI2C_REG_PRELOW, prescale & 0xff);
fpgai2c_reg_set(i2c, FPGAI2C_REG_PREHIGH, prescale >> 8);
/* Init the device */
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_IACK);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CONTROL, ctrl | FPGAI2C_REG_CTRL_EN);
/* Initialize interrupt handlers if not already done */
init_waitqueue_head(&i2c->wait);
return 0;
}
static int adap_data_init(struct i2c_adapter *adap, int i2c_ch_index)
{
struct fpgapci_devdata *pci_privdata = 0;
pci_privdata = (struct fpgapci_devdata*) dev_get_drvdata(adap->dev.parent);
if (pci_privdata == 0) {
printk("[%s]: ERROR pci_privdata is 0\n", __FUNCTION__);
return -1;
}
#if DEBUG
pddf_dbg(FPGA, KERN_INFO "[%s] index: [%d] fpga_data__base_addr:0x%0x8lx"
" fpgapci_bar_len:0x%08lx fpga_i2c_ch_base_addr:0x%08lx ch_size=0x%x supported_i2c_ch=%d",
__FUNCTION__, i2c_ch_index, pci_privdata->fpga_data_base_addr,
pci_privdata->bar_length, pci_privdata->fpga_i2c_ch_base_addr,
pci_privdata->fpga_i2c_ch_size, pci_privdata->max_fpga_i2c_ch);
#endif
if (i2c_ch_index >= pci_privdata->max_fpga_i2c_ch || pci_privdata->max_fpga_i2c_ch > I2C_PCI_MAX_BUS) {
printk("[%s]: ERROR i2c_ch_index=%d max_ch_index=%d out of range: %d\n",
__FUNCTION__, i2c_ch_index, pci_privdata->max_fpga_i2c_ch, I2C_PCI_MAX_BUS);
return -1;
}
#ifdef __STDC_LIB_EXT1__
memset_s(&fpgalogic_i2c[i2c_ch_index], sizeof(fpgalogic_i2c[0]), 0, sizeof(fpgalogic_i2c[0]));
#else
memset(&fpgalogic_i2c[i2c_ch_index], 0, sizeof(fpgalogic_i2c[0]));
#endif
/* Initialize driver's itnernal data structures */
fpgalogic_i2c[i2c_ch_index].reg_shift = 0; /* 8 bit registers */
fpgalogic_i2c[i2c_ch_index].reg_io_width = 1; /* 8 bit read/write */
fpgalogic_i2c[i2c_ch_index].timeout = 500;//1000;//1ms
fpgalogic_i2c[i2c_ch_index].ip_clock_khz = 100000;//100000;/* input clock of 100MHz */
fpgalogic_i2c[i2c_ch_index].bus_clock_khz = 100;
fpgalogic_i2c[i2c_ch_index].base = pci_privdata->fpga_i2c_ch_base_addr +
i2c_ch_index* pci_privdata->fpga_i2c_ch_size;
mutex_init(&fpgalogic_i2c[i2c_ch_index].lock);
fpgai2c_init(&fpgalogic_i2c[i2c_ch_index]);
adap->algo_data = &fpgalogic_i2c[i2c_ch_index];
i2c_set_adapdata(adap, &fpgalogic_i2c[i2c_ch_index]);
return 0;
}
static int pddf_i2c_pci_add_numbered_bus_default (struct i2c_adapter *adap, int i2c_ch_index)
{
int ret = 0;
adap_data_init(adap, i2c_ch_index);
adap->algo = &fpgai2c_algorithm;
ret = i2c_add_numbered_adapter(adap);
return ret;
}
/*
* FPGAPCI APIs
*/
int board_i2c_fpgapci_read(uint32_t offset)
{
int data;
data=ioread32(fpga_ctl_addr+offset);
return data;
}
int board_i2c_fpgapci_write(uint32_t offset, uint32_t value)
{
iowrite32(value, fpga_ctl_addr+offset);
return (0);
}
static int __init pddf_xilinx_device_7021_algo_init(void)
{
pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__);
pddf_i2c_pci_add_numbered_bus = pddf_i2c_pci_add_numbered_bus_default;
ptr_fpgapci_read = board_i2c_fpgapci_read;
ptr_fpgapci_write = board_i2c_fpgapci_write;
return 0;
}
static void __exit pddf_xilinx_device_7021_algo_exit(void)
{
pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__);
pddf_i2c_pci_add_numbered_bus = NULL;
ptr_fpgapci_read = NULL;
ptr_fpgapci_write = NULL;
return;
}
module_init (pddf_xilinx_device_7021_algo_init);
module_exit (pddf_xilinx_device_7021_algo_exit);
MODULE_DESCRIPTION("Xilinx Corporation Device 7021 FPGAPCIe I2C-Bus algorithm");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,4 @@
TARGET = pddf_fpgapci_driver
obj-m := $(TARGET).o
ccflags-y := -I$(M)/modules/include

View File

@ -0,0 +1,352 @@
/*
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* pddf_fpgapci_driver.c
* Description:
* This is a PDDF FPGAPCIe driver whic creates the PCIE device and add
* the i2c adapters to it. It uses the adapter creation and fpgapcie
* read/write functions defined separately in another kernel module.
*
************************************************************************/
#define __STDC_WANT_LIB_EXT1__ 1
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <asm/siginfo.h> //siginfo
#include <linux/rcupdate.h> //rcu_read_lock
#include <linux/version.h> //kernel_version
#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <linux/moduleparam.h>
#include "pddf_fpgapci_defs.h"
#include "pddf_client_defs.h"
#include "pddf_i2c_algo.h"
#define DEBUG 0
int (*pddf_i2c_pci_add_numbered_bus)(struct i2c_adapter *, int) = NULL;
int (*ptr_fpgapci_read)(uint32_t) = NULL;
int (*ptr_fpgapci_write)(uint32_t, uint32_t) = NULL;
EXPORT_SYMBOL(pddf_i2c_pci_add_numbered_bus);
EXPORT_SYMBOL(ptr_fpgapci_read);
EXPORT_SYMBOL(ptr_fpgapci_write);
FPGA_OPS_DATA pddf_fpga_ops_data={0};
#define DRIVER_NAME "pddf_fpgapci"
#define MAX_PCI_NUM_BARS 6
struct pci_driver pddf_fpgapci_driver;
struct pci_device_id *pddf_fpgapci_ids=NULL;
int total_i2c_pci_bus=0;
int FPGAPCI_BAR_INDEX = -1;
void __iomem * fpga_ctl_addr = NULL;
EXPORT_SYMBOL(fpga_ctl_addr);
static int pddf_fpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void pddf_fpgapci_remove(struct pci_dev *dev);
static int map_bars(struct fpgapci_devdata *pci_privdata, struct pci_dev *dev);
static void free_bars(struct fpgapci_devdata *pci_privdata, struct pci_dev *dev);
static int pddf_pci_add_adapter(struct pci_dev *dev);
/* each i2c bus is represented in linux using struct i2c_adapter */
static struct i2c_adapter i2c_pci_adap[I2C_PCI_MAX_BUS];
static int pddf_pci_add_adapter(struct pci_dev *dev)
{
int i;
total_i2c_pci_bus = pddf_fpga_ops_data.virt_i2c_ch;
pddf_dbg(FPGA, KERN_INFO "[%s] total_i2c_pci_bus=%d\n", __FUNCTION__, total_i2c_pci_bus);
#ifdef __STDC_LIB_EXT1__
memset_s(&i2c_pci_adap, sizeof(i2c_pci_adap), 0, sizeof(i2c_pci_adap));
#else
memset(&i2c_pci_adap, 0, sizeof(i2c_pci_adap));
#endif
for (i = 0 ; i < total_i2c_pci_bus; i ++) {
i2c_pci_adap[i].owner = THIS_MODULE;
i2c_pci_adap[i].class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
/* /dev/i2c-xxx for FPGA LOGIC I2C channel controller 1-7 */
i2c_pci_adap[i].nr = i + pddf_fpga_ops_data.virt_bus ;
sprintf( i2c_pci_adap[ i ].name, "i2c-pci-%d", i );
/* set up the sysfs linkage to our parent device */
i2c_pci_adap[i].dev.parent = &dev->dev;
/* Add the bus via the algorithm code */
if( (pddf_i2c_pci_add_numbered_bus!=NULL) && (pddf_i2c_pci_add_numbered_bus( &i2c_pci_adap[ i ], i ) != 0 ))
{
pddf_dbg(FPGA, KERN_ERR "Cannot add bus %d to algorithm layer\n", i );
return( -ENODEV );
}
pddf_dbg(FPGA, KERN_INFO "[%s] Registered bus id: %s\n", __FUNCTION__, kobject_name(&i2c_pci_adap[ i ].dev.kobj));
}
return 0;
}
static void pddf_pci_del_adapter(void)
{
int i;
for( i = 0; i < total_i2c_pci_bus; i++ ){
i2c_del_adapter(&i2c_pci_adap[i]);
}
}
static int map_bars(struct fpgapci_devdata *pci_privdata, struct pci_dev *dev)
{
unsigned long barFlags, barStart, barEnd, barLen;
int i;
for (i=0; i < MAX_PCI_NUM_BARS; i++) {
if((barLen=pci_resource_len(dev, i)) !=0 && (barStart=pci_resource_start(dev, i)) !=0 ) {
barFlags = pci_resource_flags(dev, i);
barEnd = pci_resource_end(dev, i);
pddf_dbg(FPGA, KERN_INFO "[%s] PCI_BASE_ADDRESS_%d 0x%08lx-0x%08lx bar_len=0x%lx"
" flags 0x%08lx IO_mapped=%s Mem_mapped=%s\n", __FUNCTION__,
i, barStart, barEnd, barLen, barFlags, (barFlags & IORESOURCE_IO)? "Yes": "No",
(barFlags & IORESOURCE_MEM)? "Yes" : "No");
FPGAPCI_BAR_INDEX=i;
break;
}
}
if (FPGAPCI_BAR_INDEX != -1) {
pci_privdata->bar_length = barLen;
pci_privdata->fpga_data_base_addr = ioremap_cache (barStart + pddf_fpga_ops_data.data_base_offset,
pddf_fpga_ops_data.data_size);
fpga_ctl_addr = pci_privdata->fpga_data_base_addr;
pci_privdata->fpga_i2c_ch_base_addr = ioremap_cache (barStart + pddf_fpga_ops_data.i2c_ch_base_offset,
I2C_PCI_MAX_BUS * pddf_fpga_ops_data.i2c_ch_size);
pci_privdata->max_fpga_i2c_ch = pddf_fpga_ops_data.virt_i2c_ch;
pci_privdata->fpga_i2c_ch_size = pddf_fpga_ops_data.i2c_ch_size;
} else {
pddf_dbg(FPGA, KERN_INFO "[%s] Failed to find BAR\n", __FUNCTION__);
return (-1);
}
pddf_dbg(FPGA, KERN_INFO "[%s] fpga_ctl_addr:0x%p fpga_data__base_addr:0x%p"
" bar_index[%d] fpgapci_bar_len:0x%08lx fpga_i2c_ch_base_addr:0x%p supported_i2c_ch=%d",
__FUNCTION__, fpga_ctl_addr, pci_privdata->fpga_data_base_addr, FPGAPCI_BAR_INDEX,
pci_privdata->bar_length, pci_privdata->fpga_i2c_ch_base_addr, pci_privdata->max_fpga_i2c_ch);
return 0;
}
static void free_bars(struct fpgapci_devdata *pci_privdata, struct pci_dev *dev)
{
pci_iounmap(dev, pci_privdata->fpga_data_base_addr);
pci_privdata->fpga_i2c_ch_base_addr = NULL;
}
static int pddf_pci_config_data(struct pci_dev *dev)
{
unsigned short vendorId=0xFFFF, deviceId=0xFFFF;
char revisionId=0xFF, classDev=0xFF, classProg=0xFF;
char irqLine=0xFF, irqPin=0xFF;
pddf_dbg(FPGA, KERN_INFO "[%s] PCI Config Data\n", __FUNCTION__);
/* accessing the configuration region of the PCI device */
pci_read_config_word(dev, PCI_VENDOR_ID, &vendorId);
pci_read_config_word(dev, PCI_DEVICE_ID, &deviceId);
pci_read_config_byte(dev, PCI_REVISION_ID, &revisionId);
pci_read_config_byte(dev, PCI_CLASS_PROG, &classProg);
pci_read_config_byte(dev, PCI_CLASS_DEVICE, &classDev);
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irqPin);
if(pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irqLine)) {
pddf_dbg(FPGA, KERN_ERR "\tPCI_INTERRUPT_LINE Error\n");
}
pddf_dbg(FPGA, KERN_INFO "\t[venId, devId]=[0x%x;0x%x] [group, class]=[%x;%x]\n",
vendorId, deviceId, classProg, classDev);
pddf_dbg(FPGA, KERN_INFO "\trevsionId=0x%x, irq_line=0x%x, irq_support=%s\n",
revisionId, irqLine, (irqPin == 0)? "No":"Yes");
return (0);
}
static int pddf_fpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct fpgapci_devdata *pci_privdata = 0;
int err = 0;
pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__);
if ((err = pci_enable_device(dev))) {
pddf_dbg(FPGA, KERN_ERR "[%s] pci_enable_device failed. dev:%s err:%#x\n",
__FUNCTION__, pci_name(dev), err);
return (err);
}
/* Enable DMA */
pci_set_master(dev);
/* Request MMIO/IOP resources - reserve PCI I/O and memory resources
DRIVE_NAME shows up in /proc/iomem
*/
if ((err = pci_request_regions(dev, DRIVER_NAME)) < 0) {
pddf_dbg(FPGA, KERN_ERR "[%s] pci_request_regions failed. dev:%s err:%#x\n",
__FUNCTION__, pci_name(dev), err);
goto error_pci_req;
}
pci_privdata = kzalloc(sizeof(struct fpgapci_devdata), GFP_KERNEL);
if (!pci_privdata) {
pddf_dbg(FPGA, KERN_ERR "[%s] couldn't allocate pci_privdata memory", __FUNCTION__);
goto error_pci_req;
}
pci_privdata->pci_dev = dev;
dev_set_drvdata(&dev->dev, (void*)pci_privdata);
pddf_pci_config_data(dev);
if (map_bars(pci_privdata, dev)) {
pddf_dbg(FPGA, KERN_ERR "error_map_bars\n");
goto error_map_bars;
}
pddf_pci_add_adapter(dev);
return (0);
/* ERROR HANDLING */
error_map_bars:
pci_release_regions(dev);
error_pci_req:
pci_disable_device(dev);
return -ENODEV;
}
static void pddf_fpgapci_remove(struct pci_dev *dev)
{
struct fpgapci_devdata *pci_privdata = 0;
if (dev == 0) {
pddf_dbg(FPGA, KERN_ERR "[%s]: dev is 0\n", __FUNCTION__);
return;
}
pci_privdata = (struct fpgapci_devdata*) dev_get_drvdata(&dev->dev);
if (pci_privdata == 0) {
pddf_dbg(FPGA, KERN_ERR "[%s]: pci_privdata is 0\n", __FUNCTION__);
return;
}
pddf_pci_del_adapter();
free_bars (pci_privdata, dev);
pci_disable_device(dev);
pci_release_regions(dev);
kfree (pci_privdata);
}
/* Initialize the driver module (but not any device) and register
* the module with the kernel PCI subsystem. */
int pddf_fpgapci_register(FPGA_OPS_DATA* ptr_ops_data)
{
memcpy(&pddf_fpga_ops_data, ptr_ops_data, sizeof(FPGA_OPS_DATA));
#if DEBUG
pddf_dbg(FPGA, KERN_INFO "[%s]: pddf_fpga_ops_data vendor_id=0x%x device_id=0x%x virt_bus=0x%x "
" data_base_offset=0x%x data_size=0x%x i2c_ch_base_offset=0x%x i2c_ch_size=0x%x virt_i2c_ch=%d",
__FUNCTION__, pddf_fpga_ops_data.vendor_id, pddf_fpga_ops_data.device_id,
pddf_fpga_ops_data.virt_bus, pddf_fpga_ops_data.data_base_offset, pddf_fpga_ops_data.data_size,
pddf_fpga_ops_data.i2c_ch_base_offset, pddf_fpga_ops_data.i2c_ch_size,
pddf_fpga_ops_data.virt_i2c_ch);
#endif
struct pci_device_id fpgapci_ids[2] = {
{PCI_DEVICE(pddf_fpga_ops_data.vendor_id, pddf_fpga_ops_data.device_id)},
{0, },
};
int size = sizeof(struct pci_device_id) * 2;
if ((pddf_fpgapci_ids=kmalloc(size, GFP_KERNEL)) == NULL) {
pddf_dbg(FPGA, KERN_INFO "%s kmalloc failed\n", __FUNCTION__);
return 0;
}
memcpy(pddf_fpgapci_ids, fpgapci_ids, size);
pddf_fpgapci_driver.name=DRIVER_NAME;
pddf_fpgapci_driver.id_table=pddf_fpgapci_ids;
pddf_fpgapci_driver.probe=pddf_fpgapci_probe;
pddf_fpgapci_driver.remove=pddf_fpgapci_remove;
if (pci_register_driver(&pddf_fpgapci_driver)) {
pddf_dbg(FPGA, KERN_INFO "%s: pci_unregister_driver\n", __FUNCTION__);
pci_unregister_driver(&pddf_fpgapci_driver);
return -ENODEV;
}
return 0;
}
EXPORT_SYMBOL(pddf_fpgapci_register);
static int __init pddf_fpgapci_driver_init(void)
{
pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__);
return 0;
}
static void __exit pddf_fpgapci_driver_exit(void)
{
pddf_dbg(FPGA, KERN_INFO "[%s]\n", __FUNCTION__);
if (pddf_fpgapci_ids) {
/* unregister this driver from the PCI bus driver */
pci_unregister_driver(&pddf_fpgapci_driver);
kfree(pddf_fpgapci_ids);
}
}
module_init (pddf_fpgapci_driver_init);
module_exit (pddf_fpgapci_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION ("PDDF Driver for FPGAPCI Logic I2C bus");
MODULE_SUPPORTED_DEVICE ("PDDF FPGAPCI Logic I2C bus");

View File

@ -0,0 +1,142 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* A pddf kernel module to create sysfs for fpga
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/kobject.h>
#include "pddf_client_defs.h"
#include "pddf_fpgapci_defs.h"
FPGA_OPS_DATA tmp_pddf_fpga_ops_data={0};
extern int pddf_fpgapci_register(FPGA_OPS_DATA *ptr);
/**************************************************************************
* FPGA SYSFS Attributes
**************************************************************************/
static ssize_t dev_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count);
PDDF_DATA_ATTR(vendor_id, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.vendor_id, NULL);
PDDF_DATA_ATTR(device_id, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.device_id, NULL);
PDDF_DATA_ATTR(virt_bus, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.virt_bus, NULL);
PDDF_DATA_ATTR(data_base_offset, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.data_base_offset, NULL);
PDDF_DATA_ATTR(data_size, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.data_size, NULL);
PDDF_DATA_ATTR(i2c_ch_base_offset, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.i2c_ch_base_offset, NULL);
PDDF_DATA_ATTR(i2c_ch_size, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.i2c_ch_size, NULL);
PDDF_DATA_ATTR(virt_i2c_ch, S_IWUSR|S_IRUGO, show_pddf_data,
store_pddf_data, PDDF_UINT32, sizeof(uint32_t), (void*)&tmp_pddf_fpga_ops_data.virt_i2c_ch, NULL);
PDDF_DATA_ATTR(dev_ops , S_IWUSR|S_IRUGO, show_pddf_data,
dev_operation, PDDF_CHAR, NAME_SIZE, (void*)&tmp_pddf_fpga_ops_data, NULL);
struct attribute* attrs_fpgapci[]={
&attr_vendor_id.dev_attr.attr,
&attr_device_id.dev_attr.attr,
&attr_virt_bus.dev_attr.attr,
&attr_data_base_offset.dev_attr.attr,
&attr_data_size.dev_attr.attr,
&attr_i2c_ch_base_offset.dev_attr.attr,
&attr_i2c_ch_size.dev_attr.attr,
&attr_virt_i2c_ch.dev_attr.attr,
&attr_dev_ops.dev_attr.attr,
NULL,
};
struct attribute_group attr_group_fpgapci={
.attrs = attrs_fpgapci,
};
ssize_t dev_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count)
{
if(strncmp(buf, "fpgapci_init", strlen("fpgapci_init"))==0 ) {
struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da;
FPGA_OPS_DATA* pddf_fpga_ops_data=(FPGA_OPS_DATA*)_ptr->addr;
pddf_dbg(FPGA, KERN_INFO "%s: pddf_fpga_ops_data vendor_id=0x%x device_id=0x%x virt_bus=0x%x:%d "
" data_base_offset=0x%x data_size=0x%x i2c_ch_base_offset=0x%x i2c_ch_size=0x%x virt_i2c_ch=%d",
__FUNCTION__, pddf_fpga_ops_data->vendor_id, pddf_fpga_ops_data->device_id,
pddf_fpga_ops_data->virt_bus, pddf_fpga_ops_data->virt_bus,
pddf_fpga_ops_data->data_base_offset, pddf_fpga_ops_data->data_size,
pddf_fpga_ops_data->i2c_ch_base_offset, pddf_fpga_ops_data->i2c_ch_size, pddf_fpga_ops_data->virt_i2c_ch);
pddf_fpgapci_register(pddf_fpga_ops_data);
}
else {
pddf_dbg(FPGA, KERN_ERR "PDDF_ERROR %s: Invalid value for dev_ops %s\n", __FUNCTION__, buf);
}
return(count);
}
#define KOBJ_FREE(obj) \
if(obj) kobject_put(obj); \
int __init pddf_fpga_data_init(void)
{
int ret = 0;
struct kobject *device_kobj;
pddf_dbg(FPGA, KERN_INFO "%s ..\n", __FUNCTION__);
device_kobj = get_device_i2c_kobj();
if(!device_kobj) {
pddf_dbg(FPGA, KERN_ERR "%s get_device_i2c_kobj failed ..\n", __FUNCTION__);
return -ENOMEM;
}
fpgapci_kobj = kobject_create_and_add("fpgapci", device_kobj);
if(!fpgapci_kobj) {
pddf_dbg(FPGA, KERN_ERR "%s create fpgapci kobj failed ..\n", __FUNCTION__);
return -ENOMEM;
}
ret = sysfs_create_group(fpgapci_kobj, &attr_group_fpgapci);
if (ret)
{
pddf_dbg(FPGA, KERN_ERR "%s create fpga sysfs attributes failed ..\n", __FUNCTION__);
return ret;
}
return (0);
}
void __exit pddf_fpga_data_exit(void)
{
pddf_dbg(FPGA, KERN_INFO "%s ..\n", __FUNCTION__);
KOBJ_FREE(fpgapci_kobj)
return;
}
module_init(pddf_fpga_data_init);
module_exit(pddf_fpga_data_exit);
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("fpga platform data");
MODULE_LICENSE("GPL");

View File

@ -33,6 +33,8 @@
#define GPIO "PDDF_GPIO"
#define SYSSTATUS "PDDF_SYSSTATUS"
#define XCVR "PDDF_XCVR"
#define FPGA "PDDF_FPGAPCI"
#define PDDF_DEBUG
#ifdef PDDF_DEBUG

View File

@ -0,0 +1,49 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Description:
* Platform FPGAPCI defines/structures header file
*/
#ifndef __PDDF_FPGAPCI_DEFS_H__
#define __PDDF_FPGAPCI_DEFS_H__
// FPGA
typedef struct
{
uint32_t vendor_id;
uint32_t device_id;
uint32_t virt_bus;
uint32_t virt_i2c_ch;
uint32_t data_base_offset;
uint32_t data_size;
uint32_t i2c_ch_base_offset;
uint32_t i2c_ch_size;
} FPGA_OPS_DATA;
/*****************************************
* kobj list
*****************************************/
struct kobject *fpgapci_kobj=NULL;
/*****************************************
* Static Data provided from user
* space JSON data file
*****************************************/
#define NAME_SIZE 32
#endif

View File

@ -0,0 +1,36 @@
/*
*
* Description:
* This is the required header file for customed i2c algorithms
*/
#ifndef __PDDF_I2C_ALGO_H__
#define __PDDF_I2C_ALGO_H__
#include "pddf_client_defs.h"
/* max number of adapters */
#define I2C_PCI_MAX_BUS 16
/**
* struct fpgapci_devdata - PCI device data structure
* support one device per PCIe
*/
struct fpgapci_devdata {
struct pci_dev *pci_dev;
/* kernels virtual addr for fpga_data_base_addr */
void * __iomem fpga_data_base_addr;
/* kernels virtual addr. for the i2c_ch_base_addr */
void * __iomem fpga_i2c_ch_base_addr;
/* size per i2c_ch */
int fpga_i2c_ch_size;
/* number of supported virtual i2c buses */
int max_fpga_i2c_ch;
size_t bar_length;
};
#endif