diff --git a/platform/pddf/i2c/debian/rules b/platform/pddf/i2c/debian/rules index edca93ee06a8..3e0bb7c4ffc9 100755 --- a/platform/pddf/i2c/debian/rules +++ b/platform/pddf/i2c/debian/rules @@ -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 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 fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver MODULE_DIR:= modules UTILS_DIR := utils SERVICE_DIR := service diff --git a/platform/pddf/i2c/modules/Makefile b/platform/pddf/i2c/modules/Makefile index ebfce193c273..71c1db661367 100644 --- a/platform/pddf/i2c/modules/Makefile +++ b/platform/pddf/i2c/modules/Makefile @@ -1 +1 @@ -obj-m := client/ cpld/ cpldmux/ xcvr/ mux/ gpio/ psu/ fan/ led/ sysstatus/ +obj-m := client/ cpld/ cpldmux/ fpgai2c/ xcvr/ mux/ gpio/ psu/ fan/ led/ sysstatus/ diff --git a/platform/pddf/i2c/modules/fpgai2c/Makefile b/platform/pddf/i2c/modules/fpgai2c/Makefile new file mode 100644 index 000000000000..2fe22efb7cf4 --- /dev/null +++ b/platform/pddf/i2c/modules/fpgai2c/Makefile @@ -0,0 +1,4 @@ +obj-m := driver/ +obj-m += pddf_fpgai2c_module.o + +ccflags-y:= -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/fpgai2c/driver/Makefile b/platform/pddf/i2c/modules/fpgai2c/driver/Makefile new file mode 100644 index 000000000000..409bf0aa472b --- /dev/null +++ b/platform/pddf/i2c/modules/fpgai2c/driver/Makefile @@ -0,0 +1,5 @@ +TARGET = pddf_fpgai2c_driver + +obj-m := $(TARGET).o + +ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/fpgai2c/driver/pddf_fpgai2c_driver.c b/platform/pddf/i2c/modules/fpgai2c/driver/pddf_fpgai2c_driver.c new file mode 100644 index 000000000000..b3092e5ee81d --- /dev/null +++ b/platform/pddf/i2c/modules/fpgai2c/driver/pddf_fpgai2c_driver.c @@ -0,0 +1,228 @@ +/* + * + * 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: + * A pddf kernel driver module for FPGA connected to the CPU by I2C bus + */ + +#include +#include +#include +#include +#include +#include "pddf_fpgai2c_defs.h" + +extern PDDF_FPGAI2C_DATA pddf_fpgai2c_data; + + +static LIST_HEAD(fpgai2c_client_list); +static struct mutex list_lock; + +struct fpgai2c_client_node { + struct i2c_client *client; + struct list_head list; +}; + +int board_i2c_fpga_read(unsigned short fpga_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct fpgai2c_client_node *fpga_node = NULL; + int ret = -EPERM; + + + mutex_lock(&list_lock); + + list_for_each(list_node, &fpgai2c_client_list) + { + fpga_node = list_entry(list_node, struct fpgai2c_client_node, list); + + if (fpga_node->client->addr == fpga_addr) { + ret = i2c_smbus_read_byte_data(fpga_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(board_i2c_fpga_read); + +int board_i2c_fpga_write(unsigned short fpga_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct fpgai2c_client_node *fpga_node = NULL; + int ret = -EIO; + + + mutex_lock(&list_lock); + + list_for_each(list_node, &fpgai2c_client_list) + { + fpga_node = list_entry(list_node, struct fpgai2c_client_node, list); + + if (fpga_node->client->addr == fpga_addr) { + ret = i2c_smbus_write_byte_data(fpga_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(board_i2c_fpga_write); + +ssize_t regval_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = 0; + struct i2c_client *client = to_i2c_client(dev); + + mutex_lock(&pddf_fpgai2c_data.fpga_lock); + // Put code here to read the register value and print it + if (pddf_fpgai2c_data.reg_addr!=0) + len = sprintf(buf, "0x%2.2x\n", board_i2c_fpga_read(client->addr, pddf_fpgai2c_data.reg_addr)); + else + len = sprintf(buf, "xx\n"); + + mutex_unlock(&pddf_fpgai2c_data.fpga_lock); + return len; +} + +static DEVICE_ATTR_RO(regval); + +static struct attribute *fpgai2c_attrs[] = { + &dev_attr_regval.attr, + NULL, +}; + +static struct attribute_group fpgai2c_attribute_group = { + .attrs = fpgai2c_attrs, +}; + + + + +/* Addresses scanned for board_i2c_fpga + */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static void board_i2c_fpga_add_client(struct i2c_client *client) +{ + struct fpgai2c_client_node *node = kzalloc(sizeof(struct fpgai2c_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate fpgai2c_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &fpgai2c_client_list); + mutex_unlock(&list_lock); +} + +static void board_i2c_fpga_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct fpgai2c_client_node *fpga_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &fpgai2c_client_list) + { + fpga_node = list_entry(list_node, struct fpgai2c_client_node, list); + + if (fpga_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(fpga_node); + } + + mutex_unlock(&list_lock); +} + +static int board_i2c_fpga_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &fpgai2c_attribute_group); + if (status) { + goto exit; + } + + dev_dbg(&client->dev, "chip found\n"); + board_i2c_fpga_add_client(client); + + return 0; + +exit: + return status; +} + +static int board_i2c_fpga_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &fpgai2c_attribute_group); + board_i2c_fpga_remove_client(client); + + return 0; +} + +static const struct i2c_device_id board_i2c_fpga_id[] = { + { "i2c_fpga", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, board_i2c_fpga_id); + +static struct i2c_driver board_i2c_fpga_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "i2c_fpga", + }, + .probe = board_i2c_fpga_probe, + .remove = board_i2c_fpga_remove, + .id_table = board_i2c_fpga_id, + .address_list = normal_i2c, +}; + +static int __init board_i2c_fpga_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&board_i2c_fpga_driver); +} + +static void __exit board_i2c_fpga_exit(void) +{ + i2c_del_driver(&board_i2c_fpga_driver); +} + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("board_i2c_fpga driver"); +MODULE_LICENSE("GPL"); + +module_init(board_i2c_fpga_init); +module_exit(board_i2c_fpga_exit); diff --git a/platform/pddf/i2c/modules/fpgai2c/pddf_fpgai2c_module.c b/platform/pddf/i2c/modules/fpgai2c/pddf_fpgai2c_module.c new file mode 100644 index 000000000000..303f209dca4a --- /dev/null +++ b/platform/pddf/i2c/modules/fpgai2c/pddf_fpgai2c_module.c @@ -0,0 +1,220 @@ +/* + * + * 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: + * A pddf kernel module to create I2C client for an I2CFPGA + */ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pddf_client_defs.h" +#include "pddf_fpgai2c_defs.h" + +PDDF_FPGAI2C_DATA pddf_fpgai2c_data={0}; +EXPORT_SYMBOL(pddf_fpgai2c_data); + +static ssize_t do_device_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static ssize_t store_pddf_fpgai2c_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count); +static ssize_t show_pddf_fpgai2c_data(struct device *dev, struct device_attribute *da, char *buf); + +extern void *get_device_table(char *name); +extern void delete_device_table(char *name); + + +/* MUX CLIENT DATA */ +PDDF_DATA_ATTR(dev_ops, S_IWUSR, NULL, do_device_operation, PDDF_CHAR, 8, NULL, (void*)&pddf_data); +PDDF_DATA_ATTR(reg_addr, S_IWUSR|S_IRUGO, show_pddf_fpgai2c_data, store_pddf_fpgai2c_data, PDDF_USHORT, sizeof(unsigned short), (void*)&pddf_fpgai2c_data.reg_addr, NULL); + + + +static struct attribute *fpgai2c_attributes[] = { + &attr_dev_ops.dev_attr.attr, + &attr_reg_addr.dev_attr.attr, + NULL +}; + +static const struct attribute_group pddf_fpgai2c_client_data_group = { + .attrs = fpgai2c_attributes, +}; + + +static ssize_t store_pddf_fpgai2c_data(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + int ret = 0; + int num = 0; + PDDF_ATTR *ptr = (PDDF_ATTR *)da; + + ret = kstrtoint(buf,16,&num); + if (ret==0) + { + mutex_lock(&pddf_fpgai2c_data.fpga_lock); + *(unsigned short *)(ptr->addr) = (unsigned short)num; + mutex_unlock(&pddf_fpgai2c_data.fpga_lock); + pddf_dbg(FPGAI2C, KERN_ERR "Stored value: 0x%x, num: 0x%x\n", *(int*)(ptr->addr), num); + } + + return count; +} + +ssize_t show_pddf_fpgai2c_data(struct device *dev, struct device_attribute *da, char *buf) +{ + int ret = 0; + PDDF_ATTR *ptr = (PDDF_ATTR *)da; + pddf_dbg(FPGAI2C, KERN_ERR "[ READ ] DATA ATTR PTR TYPE:%d, ADDR=%p\n", ptr->type, ptr->addr); + + mutex_lock(&pddf_fpgai2c_data.fpga_lock); + ret = sprintf(buf, "0x%x\n", *(unsigned short *)(ptr->addr)); + mutex_unlock(&pddf_fpgai2c_data.fpga_lock); + + return ret; +} + +static ssize_t do_device_operation(struct device *dev, struct device_attribute *da, const char *buf, size_t count) +{ + PDDF_ATTR *ptr = (PDDF_ATTR *)da; + NEW_DEV_ATTR *device_ptr = (NEW_DEV_ATTR *)(ptr->data); + struct i2c_adapter *adapter; + static struct i2c_board_info board_info; + struct i2c_client *client_ptr; + + if (strncmp(buf, "add", strlen(buf)-1)==0) + { + adapter = i2c_get_adapter(device_ptr->parent_bus); + + if (strncmp(device_ptr->dev_type, "i2c_fpga", strlen("i2c_fpga"))==0) + { + board_info = (struct i2c_board_info) { + .platform_data = (void *)NULL, + }; + + board_info.addr = device_ptr->dev_addr; + strcpy(board_info.type, device_ptr->dev_type); + + client_ptr = i2c_new_client_device(adapter, &board_info); + + if (!IS_ERR(client_ptr)) { + i2c_put_adapter(adapter); + pddf_dbg(FPGAI2C, KERN_ERR "Created %s client: 0x%p\n", device_ptr->i2c_name, (void *)client_ptr); + add_device_table(device_ptr->i2c_name, (void*)client_ptr); + } + else { + i2c_put_adapter(adapter); + goto free_data; + } + + } + else + { + printk(KERN_ERR "%s: Unsupported type of fpga device id - unable to add i2c client\n", __FUNCTION__); + } + } + else if (strncmp(buf, "delete", strlen(buf)-1)==0) + { + /*Get the i2c_client handle for the created client*/ + client_ptr = (struct i2c_client *)get_device_table(device_ptr->i2c_name); + if (client_ptr) + { + pddf_dbg(FPGAI2C, KERN_ERR "Removing %s client: 0x%p\n", device_ptr->i2c_name, (void *)client_ptr); + i2c_unregister_device(client_ptr); + delete_device_table(device_ptr->i2c_name); + } + else + { + printk(KERN_ERR "Unable to get the client handle for %s\n", device_ptr->i2c_name); + } + } + else + { + printk(KERN_ERR "PDDF_ERROR: %s: Invalid value for dev_ops %s", __FUNCTION__, buf); + } + +free_data: + /*TODO: free the device_ptr->data is dynamically allocated*/ +#ifdef __STDC_LIB_EXT1__ + memset_s(device_ptr, sizeof(NEW_DEV_ATTR), 0 , sizeof(NEW_DEV_ATTR)); +#else + memset(device_ptr, 0 , sizeof(NEW_DEV_ATTR)); +#endif + + return count; +} + + +static struct kobject *fpgai2c_kobj; + +int __init fpgai2c_data_init(void) +{ + struct kobject *device_kobj; + int ret = 0; + + + pddf_dbg(FPGAI2C, "FPGAI2C_DATA MODULE.. init\n"); + + device_kobj = get_device_i2c_kobj(); + if(!device_kobj) + return -ENOMEM; + + fpgai2c_kobj = kobject_create_and_add("fpgai2c", device_kobj); + if(!fpgai2c_kobj) + return -ENOMEM; + + + ret = sysfs_create_group(fpgai2c_kobj, &pddf_clients_data_group); + if (ret) + { + kobject_put(fpgai2c_kobj); + return ret; + } + pddf_dbg(FPGAI2C, "CREATED PDDF I2C CLIENTS CREATION SYSFS GROUP\n"); + + mutex_init(&pddf_fpgai2c_data.fpga_lock); + + ret = sysfs_create_group(fpgai2c_kobj, &pddf_fpgai2c_client_data_group); + if (ret) + { + sysfs_remove_group(fpgai2c_kobj, &pddf_clients_data_group); + kobject_put(fpgai2c_kobj); + return ret; + } + pddf_dbg(FPGAI2C, "CREATED PDDF I2C CLIENTS CREATION SYSFS GROUP\n"); + return ret; +} + +void __exit fpgai2c_data_exit(void) +{ + pddf_dbg(FPGAI2C, "FPGAI2C_DATA MODULE.. exit\n"); + sysfs_remove_group(fpgai2c_kobj, &pddf_fpgai2c_client_data_group); + sysfs_remove_group(fpgai2c_kobj, &pddf_clients_data_group); + kobject_put(fpgai2c_kobj); + pddf_dbg(FPGAI2C, KERN_ERR "%s: Removed the kobjects for 'fpgai2c'\n",__FUNCTION__); + return; +} + +module_init(fpgai2c_data_init); +module_exit(fpgai2c_data_exit); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("fpgai2c platform data"); +MODULE_LICENSE("GPL"); diff --git a/platform/pddf/i2c/modules/include/pddf_client_defs.h b/platform/pddf/i2c/modules/include/pddf_client_defs.h index 1c98a73e6eb6..b9d5090f731f 100644 --- a/platform/pddf/i2c/modules/include/pddf_client_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_client_defs.h @@ -28,6 +28,7 @@ #define CLIENT "PDDF_CLIENT" #define CPLD "PDDF_CPLD" #define CPLDMUX "PDDF_CPLDMUX" +#define FPGAI2C "PDDF_FPGAI2C" #define MUX "PDDF_MUX" #define GPIO "PDDF_GPIO" #define SYSSTATUS "PDDF_SYSSTATUS" diff --git a/platform/pddf/i2c/modules/include/pddf_fpgai2c_defs.h b/platform/pddf/i2c/modules/include/pddf_fpgai2c_defs.h new file mode 100644 index 000000000000..bc3cbc2b7bba --- /dev/null +++ b/platform/pddf/i2c/modules/include/pddf_fpgai2c_defs.h @@ -0,0 +1,30 @@ +/* + * Copyright 2019 Broadcom. + * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + * 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 FPGA I2C defines/structures header file + */ + +#ifndef __PDDF_FPGAI2C_DEFS_H__ +#define __PDDF_FPGAI2C_DEFS_H__ + +/* FPGAI2C DATA - DATA FOR I2C FPGA CLIENT READ/WRITE*/ +typedef struct FPGAI2C_DATA +{ + struct mutex fpga_lock; + uint16_t reg_addr; +}PDDF_FPGAI2C_DATA; + + +#endif //__PDDF_FPGAI2C_DEFS_H__