文章目录
- cpld_i2c.c
- cpld_lpc.c
-
- [4.3 i2c-ocores.h/c:ocores_attr_test在ocores_i2c_probe函数里添加节点](#4.3 i2c-ocores.h/c:ocores_attr_test在ocores_i2c_probe函数里添加节点)
- [4.1 switchboard.c:fpga下i2c访问CPLD1,CPLD2 , FPGA , QSFP](#4.1 switchboard.c:fpga下i2c访问CPLD1,CPLD2 , FPGA , QSFP)
cpld_i2c.c
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#define CPLD_DRIVER_NAME "cpld_i2c"
#define CPLD_DRIVER_VER "0.1.0"
/* lc cpld register address for read and write.*/
/* lc_cpld_version */
#define HIGH_VERSION 0x01
#define MIDDLE_VERSION 0x02
#define LOW_VERSION 0x03
/*********************** watch dog start : 读写cpu寄存器********************************/
#define GPIOBASE_ADDR 0x500
#define WDT_FEED 15 // gpio15 pin脚 喂狗
#define G_PIN_SEL_ADDRESS (GPIOBASE_ADDR + 0x0) // 设置功能选择
#define G_PIN_IN_OUT_ADDRESS (GPIOBASE_ADDR + 0x04) // 设置输入
#define G_WDT_OUT_ADDRESS (GPIOBASE_ADDR + 0x0C) // 设置输出
#define G_WDT_FEED (1 << WDT_FEED) // 1000 0000 0000 0000 , 用来取第15位
#define G_VALUE_ONE 0x1
#define G_VALUE_ZERO 0x0
#define CPLD_SCRATCH 0x04
#define WDT_MASK 0xd1 // 屏蔽,写入不起作用
#define CLK_STATE 0x0F
struct cpld_hq_data {
struct mutex cpld_lock;
struct mutex wdt_lock;
uint8_t read_addr;
uint8_t base_addr;
};
static struct kobject * g_kobj = NULL;
static void write_wdt_port(uint32_t pin_addr, uint32_t a_ucpins, uint32_t a_ucvalue)
{
uint32_t p_reg_val;
p_reg_val = inl_p(pin_addr); // inl_p 读寄存器, pin_addr: 0x.., pio可以inl_p 。 comecpld连在cpu上用inb, outb读
if (a_ucvalue) { //1
p_reg_val = (a_ucpins | p_reg_val); // 或:有1为1, 第15位被置1
} else { //0
p_reg_val = (~a_ucpins & p_reg_val); // 与:有0为0,第15位被置0
}
outl_p(p_reg_val, pin_addr); //写
}
static uint32_t read_wdt_port(uint32_t pin_addr, uint32_t a_ucpins)
{
uint32_t ret = 0;
if (inl_p(pin_addr) & a_ucpins) { // pin_addr第15位为1
ret = 0x01;
} else {
ret = 0x00;
}
return ret;
}
static ssize_t wdt_feed_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t value;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
value = read_wdt_port(G_WDT_OUT_ADDRESS, G_WDT_FEED);
return snprintf(buf, PAGE_SIZE, "0x%.2x\n", value);
}
static ssize_t wdt_feed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint32_t value;
ssize_t status = 0;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
mutex_lock(&cpld_data->wdt_lock); //读写一对,中间不会被打断
value = read_wdt_port(G_WDT_OUT_ADDRESS, G_WDT_FEED);
value = (~value) & G_VALUE_ONE;
write_wdt_port(G_WDT_OUT_ADDRESS, G_WDT_FEED, value);
mutex_unlock(&cpld_data->wdt_lock);
status = (ssize_t)size;
return status;
}
struct device_attribute dev_attr_watchdog_feed = __ATTR(wdt_feed, 0600, wdt_feed_show, wdt_feed_store);
static ssize_t cpld_i2c_wdt_mask_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t data;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
data = i2c_smbus_read_byte_data(client, WDT_MASK);
if (data < 0)
return data;
return snprintf(buf, PAGE_SIZE, "0x%2.2x\n", data);
}
static ssize_t cpld_i2c_wdt_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint8_t value;
size_t value_get = 0;
ssize_t status;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
status = kstrtoul(buf, 16, &value_get);
if (status < 0)
return status;
value = (uint8_t)value_get;
status = i2c_smbus_write_byte_data(client, WDT_MASK, value);
if (status < 0)
return status;
status = (ssize_t)size;
return status;
}
struct device_attribute dev_attr_cpld_i2c_wdt_mask = __ATTR(wdt_mask, 0600, cpld_i2c_wdt_mask_show, cpld_i2c_wdt_mask_store); //ff,喂狗关闭,0打开
/******************************watch dog end ********************************************/
static ssize_t get_lc_cpld_board_version_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t data1;
int32_t data2;
int32_t data3;
int32_t value = -EINVAL;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
data1 = i2c_smbus_read_byte_data(client, HIGH_VERSION);
data2 = i2c_smbus_read_byte_data(client, MIDDLE_VERSION);
data3 = i2c_smbus_read_byte_data(client, LOW_VERSION);
if (data1 < 0 || data2 < 0 || data3 < 0)
return -EINVAL;
value = snprintf(buf, PAGE_SIZE, "%x.%x.%x\n", data1, data2, data3);
return value;
}
struct device_attribute dev_attr_get_lc_cpld_board_version = __ATTR(getv_lc_cpld, 0400, get_lc_cpld_board_version_show, NULL);
static ssize_t get_lc_cpld_external_100clk_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t data;
int32_t value = -EINVAL;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
data = i2c_smbus_read_byte_data(client, CLK_STATE);
if (data < 0)
return -EINVAL;
data = (data >> G_VALUE_ONE) & G_VALUE_ONE;
value = snprintf(buf, PAGE_SIZE, "%x\n", data);
return value;
}
struct device_attribute dev_attr_get_clk_100_stat = __ATTR(get_clk_100_stat, 0400, get_lc_cpld_external_100clk_show, NULL);
static ssize_t get_lc_cpld_external_156clk_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t data;
int32_t value = -EINVAL;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
data = i2c_smbus_read_byte_data(client, CLK_STATE);
if (data < 0)
return -EINVAL;
data = data & G_VALUE_ONE;
value = snprintf(buf, PAGE_SIZE, "%x\n", data);
return value;
}
struct device_attribute dev_attr_get_clk_156_stat = __ATTR(get_clk_156_stat, 0400, get_lc_cpld_external_156clk_show, NULL); //检测时钟好不好,不好1,好0
static ssize_t cpld_i2c_getreg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t data;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
data = i2c_smbus_read_byte_data(client, cpld_data->read_addr);
if (data < 0)
return data;
return snprintf(buf, PAGE_SIZE, "0x%2.2x\n", data);
}
static ssize_t cpld_i2c_getreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint8_t addr;
ssize_t status = 0;
size_t addr_get = 0;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
status = kstrtoul(buf, 16, &addr_get);
if (status < 0)
return status;
addr = (uint8_t)addr_get;
cpld_data->read_addr = addr;
status = (ssize_t)size;
return status;
}
struct device_attribute dev_attr_cpld_i2c_getreg = __ATTR(getreg, 0600, cpld_i2c_getreg_show, cpld_i2c_getreg_store);
static ssize_t cpld_i2c_scratch_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// CPLD register is one byte
int32_t value;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
value = i2c_smbus_read_byte_data(client, CPLD_SCRATCH);
if (value < 0)
return value;
return snprintf(buf, PAGE_SIZE, "0x%.2x\n", value);
}
static ssize_t cpld_i2c_scratch_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
// CPLD register is one byte
uint8_t value;
ssize_t status;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_hq_data *cpld_data = i2c_get_clientdata(client);
if (cpld_data == NULL)
return -ENODEV;
status = kstrtou8(buf, 0, &value);
if (status != 0) {
return status;
}
status = i2c_smbus_write_byte_data(client, CPLD_SCRATCH, value);
if (status < 0)
return status;
status = (ssize_t)size;
return status;
}
struct device_attribute dev_attr_cpld_i2c_scratch = __ATTR(scratch, 0600, cpld_i2c_scratch_show, cpld_i2c_scratch_store);
static ssize_t cpld_i2c_setreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint8_t addr, value;
size_t addr_get = 0;
size_t value_get = 0;
struct i2c_client *client = to_i2c_client(dev);
int8_t *tok;
int8_t clone[size];
int8_t *pclone = clone;
ssize_t status;
int8_t len = size;
if (client == NULL)
return -ENODEV;
if (buf[size - 1] == '\0' || buf[size - 1] == '\n')
len -= 1;
if (len == 0 || len >= sizeof(clone))
return -EINVAL;
strncpy(clone, buf, len);
clone[len] = '\0';
tok = strsep((char **)&pclone, " ");
if (tok == NULL) {
return -EINVAL;
}
status = kstrtoul(tok, 16, &addr_get);
if (status < 0)
return status;
addr = (uint8_t)addr_get;
tok = strsep((char **)&pclone, " ");
if (tok == NULL) {
return -EINVAL;
}
status = kstrtoul(tok, 16, &value_get);
if (status < 0)
return status;
value = (uint8_t)value_get;
status = i2c_smbus_write_byte_data(client, addr, value);
if (status < 0)
return status;
status = (ssize_t)size;
return status;
}
struct device_attribute dev_attr_cpld_i2c_setreg = __ATTR(setreg, 0200, NULL, cpld_i2c_setreg_store);
static struct attribute *cpld_i2c_attrs[] = {
&dev_attr_cpld_i2c_getreg.attr,
&dev_attr_cpld_i2c_scratch.attr,
&dev_attr_cpld_i2c_setreg.attr,
&dev_attr_cpld_i2c_wdt_mask.attr,
&dev_attr_watchdog_feed.attr,
&dev_attr_get_lc_cpld_board_version.attr,
&dev_attr_get_clk_100_stat.attr,
&dev_attr_get_clk_156_stat.attr,
NULL,
};
static struct attribute_group cpld_i2c_attr_grp = {
.attrs = cpld_i2c_attrs,
};
static int32_t cpld_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter; // 找到adapter
struct cpld_hq_data *cpld_data;
int32_t ret = 0;
struct platform_device *switch_dev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_READ_BYTE)) //检查控制器能不能用,能用给下面client用
return -EPFNOSUPPORT;
cpld_data = devm_kzalloc(&client->dev, sizeof(struct cpld_hq_data), GFP_KERNEL);
if (!cpld_data)
return -ENOMEM;
i2c_set_clientdata(client, cpld_data);
mutex_init(&cpld_data->cpld_lock);
mutex_init(&cpld_data->wdt_lock);
cpld_data->read_addr = 0;
cpld_data->base_addr = 0;
write_wdt_port(G_PIN_SEL_ADDRESS, G_WDT_FEED, G_VALUE_ONE);
write_wdt_port(G_PIN_IN_OUT_ADDRESS, G_WDT_FEED, G_VALUE_ZERO);
write_wdt_port(G_WDT_OUT_ADDRESS, G_WDT_FEED, G_VALUE_ONE);
ret = sysfs_create_group(&client->dev.kobj, &cpld_i2c_attr_grp);
if (ret != 0) {
pr_err("Cannot create CPLD sysfs attributes\n");
goto err_remove_cpld;
}
switch_dev = dev_get_platdata(&client->dev);
if (switch_dev) {
pr_info("switch device %s found \n", switch_dev->name);
ret = sysfs_create_link(
&switch_dev->dev.kobj, &client->dev.kobj,
client->name); // we can use this create link from cpld, link switch to cpld kobj
if (ret != 0) {
pr_err("%s create link to switch device failed \n", client->name);
goto err_remove_cpld_link;
}
} else {
pr_err("switch device not passed\n");
ret = -EAGAIN;
goto err_remove_cpld_link;
}
g_kobj = kobject_create_and_add("ott_lc_cpld",NULL);
if(!g_kobj) {
ret = -EAGAIN;
pr_err("create ott_lc_cpld kobject failed.\n");
goto err_remove_cpld_link;
}
ret = sysfs_create_link(g_kobj,&client->dev.kobj,"lc_cpld");
if(ret) {
pr_err("create ott_lc_cpld sysfs link failed.\n");
goto err_remove_cpld_sys;
}
return 0;
err_remove_cpld_sys:
kobject_del(g_kobj);
kobject_put(g_kobj);
err_remove_cpld_link:
sysfs_remove_group(&client->dev.kobj, &cpld_i2c_attr_grp);
err_remove_cpld:
return ret;
}
static int32_t cpld_i2c_remove(struct i2c_client *client)
{
struct platform_device *switch_dev;
switch_dev = dev_get_platdata(&client->dev);
if (switch_dev != NULL) {
if (&switch_dev->dev.kobj != NULL)
sysfs_remove_link(&switch_dev->dev.kobj, client->name);
}
sysfs_remove_group(&client->dev.kobj, &cpld_i2c_attr_grp);
if(g_kobj) {
kobject_del(g_kobj);
kobject_put(g_kobj);
sysfs_remove_link(g_kobj, "lc_cpld");
}
return 0;
}
static const struct i2c_device_id cpld_i2c_id[] = {{"cpld_lc", 0}, {}}; //cpld_lc匹配字符串 fpga_i2c_adapter
MODULE_DEVICE_TABLE(i2c, cpld_i2c_id);
static struct i2c_driver cpld_i2c_driver = {
.driver = {
.name = CPLD_DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = cpld_i2c_probe,
.remove = cpld_i2c_remove,
.id_table = cpld_i2c_id,
};
module_i2c_driver(cpld_i2c_driver);
MODULE_AUTHOR("@.com");
MODULE_DESCRIPTION("Common CPLD");
MODULE_VERSION(CPLD_DRIVER_VER);
MODULE_LICENSE("GPL");
cpld_lpc.c
// lpc访问come/baseboard cpld :led , watchdog
#include "common_q.h"
#include <linux/kobject.h>
#define CPLD_DRIVER_NAME "sys_cpld"
#define CPLD_DRIVER_VER "0.0.1"
/*
* Base CPLD:0xA100 ~ 0xA1DF
* COMe CPLD:0xA1E0 ~ 0xA1FF
* */
#define BASE_CPLD_ADDR 0xA100
#define COME_CPLD_ADDR 0xA1E0
#define VERSION_ADDR 0xA100
#define BASE_PCB_VER_ADDR 0xA100
#define BASE_H_VER_ADDR 0xA101
#define BASE_M_VER_ADDR 0xA102
#define BASE_L_VER_ADDR 0xA103
#define BASE_SCRATCH_ADDR 0xA104
#define COME_PCB_VER_ADDR 0xA1E0
#define COME_H_VER_ADDR 0xA1E1
#define COME_M_VER_ADDR 0xA1E2
#define COME_L_VER_ADDR 0xA1E3
#define COME_SCRATCH_ADDR 0xA1E4
#define CPLD_REGISTER_SIZE 0xFF
#define CPLD_TOTAL_NUMBER 4
/*watchdog*/
#define BASE_REG_ADDR_WD_EN 0xA190
#define BASE_REG_ADDR_WD_LTIME 0xA191
#define BASE_REG_ADDR_WD_HTIME 0xA192
#define BASE_REG_ADDR_WD_KICK 0xA193
#define BASE_REG_ADDR_WD_FOUN 0xA194
#define BASE_REG_ADDR_WD_STAE 0xA195 //status
#define BASE_REG_ADDR_WD_CLEAR 0xA196
#define BASE_REG_ADDR_WD_LTIMELEFT 0xA197
#define BASE_REG_ADDR_WD_HTIMELEFT 0xA198
/*sysled*/
#define PSU_LED_ADDR 0xA140
#define SYS_LED_ADDR 0xA141
#define Alarm_LED_ADDR 0xA142
#define Fan_LED_ADDR 0xA143
#define BMC_LED_ADDR 0xA144
enum
{
DARK,
GREEN,
YELLOW,
RED,
BLUE,
GREEN_LIGHT_FLASHING,
YELLOW_LIGHT_FLASHING,
RED_LIGHT_FLASHING,
BLUE_LIGHT_FLASHING
};
int set_bit_value(int value,int bit,int status);
int get_bit_value(int value,int bit);
/*************************************watchdog**********************************************/
ssize_t get_main_watchdog_identify(char *buf, size_t count)
{
return sprintf(buf,"CPU_wdt\n");
}
EXPORT_SYMBOL(get_main_watchdog_identify);
ssize_t get_main_watchdog_state(char *buf, size_t count)
{
int data=inb(BASE_REG_ADDR_WD_EN);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",BASE_REG_ADDR_WD_EN,inb(BASE_REG_ADDR_WD_EN));
data = get_bit_value(data,0);
data=sprintf(buf,"%x\n",data); //转为16进制
return data;
}
EXPORT_SYMBOL(get_main_watchdog_state);
ssize_t get_main_watchdog_timeleft(char *buf, size_t count)
{
int data_ligh = inb(BASE_REG_ADDR_WD_HTIMELEFT);
// printk(KERN_INFO "Baseboard CPLD addr BASE_REG_ADDR_WD_HTIME :%.2x,value: %.2x \n",BASE_REG_ADDR_WD_HTIMELEFT,data_ligh);
int data_low = inb(BASE_REG_ADDR_WD_LTIMELEFT);
// printk(KERN_INFO "Baseboard CPLD addr BASE_REG_ADDR_WD_LTIME:%.2x,value: %.2x \n",BASE_REG_ADDR_WD_LTIMELEFT,data_low);
int data=(data_ligh << 8 )+data_low;
// printk(KERN_INFO "data :%.2x \n",data);
data=data/1000;
data=sprintf(buf,"%d\n",data);
return data;
}
EXPORT_SYMBOL(get_main_watchdog_timeleft);
ssize_t get_main_watchdog_timeout(char *buf, size_t count)
{
int data_ligh = inb(BASE_REG_ADDR_WD_HTIME);
// printk(KERN_INFO "Baseboard CPLD addr BASE_REG_ADDR_WD_HTIME :%.2x,value: %.2x \n",BASE_REG_ADDR_WD_HTIME,data_ligh);
int data_low = inb(BASE_REG_ADDR_WD_LTIME);
// printk(KERN_INFO "Baseboard CPLD addr BASE_REG_ADDR_WD_LTIME:%.2x,value: %.2x \n",BASE_REG_ADDR_WD_LTIME,data_low);
int data=(data_ligh << 8 )+data_low; //拼接
// printk(KERN_INFO "data :%.2x \n",data);
data=data/1000; //毫秒变成秒
data=sprintf(buf,"%d\n",data);
return data;
}
EXPORT_SYMBOL(get_main_watchdog_timeout);
int set_main_watchdog_timeout(int value)
{
int value_ms = value * 1000 ; //秒变成毫秒
outb(value_ms&0xff ,BASE_REG_ADDR_WD_LTIME); //outb写,取低八位
outb((value_ms>>8)&0xff ,BASE_REG_ADDR_WD_HTIME);
return;
}
EXPORT_SYMBOL(set_main_watchdog_timeout);
static void feed_watchdog(void)
{
int addr,value;
addr = BASE_REG_ADDR_WD_KICK;
value = inb(addr); // 读
value = set_bit_value(value,0,0); // 设 0x7c
value = set_bit_value(value,1,0);
value = set_bit_value(value,2,1);
value = set_bit_value(value,3,1);
value = set_bit_value(value,4,1);
value = set_bit_value(value,5,1);
value = set_bit_value(value,6,1);
value = set_bit_value(value,7,0);
outb(value,addr); // 写
}
ssize_t get_main_watchdog_enable_status(char *buf, size_t count)
{
int addr,value;
addr = BASE_REG_ADDR_WD_EN;
value=inb(addr);
value = get_bit_value(value,0); //看最后一位
return sprintf(buf,"%02X\n", value);
}
EXPORT_SYMBOL(get_main_watchdog_enable_status);
int set_main_watchdog_enable_status(int status)
{
int addr,value;
addr = BASE_REG_ADDR_WD_EN;
value = inb(addr);
switch(status)
{
case 0:
value = set_bit_value(value,0,0);
outb(value,addr);
break;
case 1:
value = set_bit_value(value,0,1);
outb(value,addr);
feed_watchdog();
break;
default:
return -ENOSYS;
}
return status;
}
EXPORT_SYMBOL(set_main_watchdog_enable_status);
/*************************************new enable node***********************************/
ssize_t get_watchdog_enable_status(struct device *dev, struct device_attribute *attr, char *buf)
{
size_t count=0;
return get_main_watchdog_enable_status(buf, count);
}
static ssize_t set_watchdog_enable_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
int value,rc = 0;
rc = kstrtoint(buf,10,&value);
if (rc != 0) {
return size;
}
set_main_watchdog_enable_status(value);
return size;
}
static struct device_attribute watchdog_enable_attr = __ATTR(enable, S_IRUGO | S_IWUSR,get_watchdog_enable_status,set_watchdog_enable_status);
/**********************************new timeout node*******************************/
ssize_t get_watchdog_timeout(struct device *dev, struct device_attribute *attr, char *buf)
{
return get_main_watchdog_timeout(buf,NULL);
}
static ssize_t set_watchdog_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
int value,rc = 0;
rc = kstrtoint(buf,10,&value);
if (rc != 0) {
return size;
}
set_main_watchdog_timeout(value);
return size;
}
static struct device_attribute watchdog_timeout_attr = __ATTR(timeout, S_IRUGO | S_IWUSR,get_watchdog_timeout,set_watchdog_timeout);
/*********************************************new feed node*******************************/
static ssize_t hq_set_watchdog_feed(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
int data,rc = 0;
rc = kstrtoint(buf,16,&data);
if (rc != 0) {
return size;
}
// printk(KERN_INFO "Baseboard CPLD addr BASE_REG_ADDR_WD_KICK :%.2x,value: %.2x \n",BASE_REG_ADDR_WD_KICK,data);
if(data==0x7c)
{
outb(data,BASE_REG_ADDR_WD_KICK);
// printk(KERN_INFO "Baseboard CPLD feed dog getreg value : %.2x \n",inb(BASE_REG_ADDR_WD_KICK));
}
else
{
return -EINVAL;
}
return size;
}
static struct device_attribute watchdog_feed_attt = __ATTR(feed, S_IRUGO | S_IWUSR, NULL, hq_set_watchdog_feed);
/*******************************************new strategy node*******************************/
ssize_t hq_get_watchdog_strategy(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%0.2x\n",inb(BASE_REG_ADDR_WD_FOUN));
}
static ssize_t hq_set_watchdog_strategy(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
int data,rc = 0;
rc = kstrtoint(buf,16,&data);
if (rc != 0) {
return size;
}
if((data==0x01) || (data ==0x02))
{
outb(data,BASE_REG_ADDR_WD_FOUN);
// printk(KERN_INFO " hq_set_watchdog_strategy addr value : %.2x \n",inb(BASE_REG_ADDR_WD_FOUN));
}
else
{
return -EINVAL;
}
return size;
}
static struct device_attribute watchdog_strategy_attr = __ATTR(strategy, S_IRUGO | S_IWUSR,hq_get_watchdog_strategy ,hq_set_watchdog_strategy);
/******************************************new timeout_counts node*******************************/
ssize_t hq_get_watchdog_timeout_times(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%d\n",inb(BASE_REG_ADDR_WD_STAE));
}
static struct device_attribute watchdog_timeout_counts_attr = __ATTR(timeout_counts, S_IWUSR|S_IRUGO,hq_get_watchdog_timeout_times, NULL);
/****************************************new timeout_reset node*******************************/
static ssize_t hq_set_watchdog_timeout_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
int data,rc = 0;
rc = kstrtoint(buf,16,&data);
if (rc != 0) {
return size;
}
if(data==1)
{
outb(data,BASE_REG_ADDR_WD_KICK);
}
else
{
return -EINVAL;
}
return size;
}
static struct device_attribute watchdog_timeout_reset_attr = __ATTR(timeout_reset, S_IRUGO | S_IWUSR,NULL ,hq_set_watchdog_timeout_reset);
static struct attribute *watchdog_dir_attrs[] = {
&watchdog_enable_attr.attr,
&watchdog_timeout_attr.attr,
&watchdog_feed_attt.attr,
&watchdog_strategy_attr.attr,
&watchdog_timeout_counts_attr.attr,
&watchdog_timeout_reset_attr.attr,
NULL
};
static struct attribute_group watchdog_attr_group = { // 在下面cpld_hq_drv_probe函数里: kobject_create_and_add, sysfs_create_group
.attrs = watchdog_dir_attrs,
};
/**********************************watchdog end******************************************/
/*************************************sysled begain************************************/
int set_sysled_state(int status,char *names)
{
int addr,value;
if(strcmp(names,"PSU")==0)
{
addr=PSU_LED_ADDR;
}
else if(strcmp(names,"SYS")==0)
{
addr=SYS_LED_ADDR;
}
else if(strcmp(names,"Alarm")==0)
{
addr=Alarm_LED_ADDR;
}
else if(strcmp(names,"Fan")==0)
{
addr=Fan_LED_ADDR;
}
else if(strcmp(names,"BMC")==0)
{
addr=BMC_LED_ADDR;
}
else
{
return -ENOSYS;
}
value=inb(addr);
switch(status)
{
case DARK:
value=set_bit_value(value,6,1);
value=set_bit_value(value,0,1);
value=set_bit_value(value,1,0);
outb(value,addr);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",addr,inb(addr));
break;
case GREEN:
value=set_bit_value(value,6,1);
value=set_bit_value(value,4,0);
value=set_bit_value(value,5,0);
value=set_bit_value(value,1,0);
value=set_bit_value(value,0,0);
outb(value,addr);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",addr,inb(addr));
break;
case YELLOW:
value=set_bit_value(value,6,1);
value=set_bit_value(value,4,1);
value=set_bit_value(value,5,0);
value=set_bit_value(value,1,0);
value=set_bit_value(value,0,0);
outb(value,addr);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",addr,inb(addr));
break;
case RED:
return -ENOSYS;
case BLUE:
return -ENOSYS;
case GREEN_LIGHT_FLASHING:
value=set_bit_value(value,6,1);
value=set_bit_value(value,4,0);
value=set_bit_value(value,5,0);
value=set_bit_value(value,1,1);
value=set_bit_value(value,0,0);
outb(value,addr);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",addr,inb(addr));
break;
case YELLOW_LIGHT_FLASHING:
value=set_bit_value(value,6,1);
value=set_bit_value(value,4,1);
value=set_bit_value(value,5,0);
value=set_bit_value(value,1,1);
value=set_bit_value(value,0,0);
outb(value,addr);
// printk(KERN_INFO "Baseboard CPLD addr:%.2x ,value: %.2x \n",addr,inb(addr));
break;
case RED_LIGHT_FLASHING:
return -ENOSYS;
case BLUE_LIGHT_FLASHING:
return -ENOSYS;
default:
return -ENOSYS;
}
return 1;
}
EXPORT_SYMBOL(set_sysled_state);
ssize_t get_sysled_state(char *buf, size_t count,char *names)
{
int addr,value;
int len=-1;
if(strcmp(names,"PSU")==0)
{
addr=PSU_LED_ADDR;
}
else if(strcmp(names,"SYS")==0)
{
addr=SYS_LED_ADDR;
}
else if(strcmp(names,"Alarm")==0)
{
addr=Alarm_LED_ADDR;
}
else if(strcmp(names,"Fan")==0)
{
addr=Fan_LED_ADDR;
}
else if(strcmp(names,"BMC")==0)
{
addr=BMC_LED_ADDR;
}
else
{
return -ENOSYS;
}
value=inb(addr);
if(get_bit_value(value,0)==1 && get_bit_value(value,1)==0)
{
len=sprintf(buf,"dark\n");
return len;
}
else if(get_bit_value(value,0)==0 && get_bit_value(value,1)==0)
{
//buf chang liang 常亮
if(get_bit_value(value,4)==0 && get_bit_value(value,5)==0)
{
len=sprintf(buf,"green\n");
return len;
}
else if(get_bit_value(value,4)==1 && get_bit_value(value,5)==0)
{
len=sprintf(buf,"yellow\n");
return len;
}
else
{
;
}
}
else if(get_bit_value(value,0)==0 && get_bit_value(value,1)==1)
{
//buf 闪烁
if(get_bit_value(value,4)==0 && get_bit_value(value,5)==0)
{
len=sprintf(buf,"green light flashing\n");
return len;
}
else if(get_bit_value(value,4)==1 && get_bit_value(value,5)==0)
{
len=sprintf(buf,"yellow light flashing\n");
return len;
}
else
{
;
}
}
else
{
return -ENOSYS;
}
}
EXPORT_SYMBOL(get_sysled_state);
int get_bit_value(int value,int bit)
{
if(value<0 || value>0xff || bit<0 || bit>7)
{
return -1;
}
int data ;
data=value >> bit & 0x01; // 右移bit位并取最后一位
return data;
}
int set_bit_value(int value,int bit,int status)
{
if(value<0 || value>0xff || bit<0 || bit>7|| status<0 || status>1)
{
return -1;
}
else
{
if(status==0) //将1111 1111第二位(从0开始即第三位)置为0
{
value=(~(1<<bit)) & value; //0000 0100 (1<<2) 1111 1011 & 1111 1111 = 1111 1011 (&:有0为0)
return value;
}
else if(status==1) //将0001 0001第二位(从0开始即第三位)置为1
{
value = (1<<bit) | value; //0000 0100 (1<<2) 0000 0100 | 0001 0001 = 0001 0101 (|:有1为1)
return value;
}
else
{
;
}
}
}
/***********************sysled end**************************************************/
/***************************CPLD begain********************************************/
int get_board_cpld_number(void)
{
/* add vendor codes here */
return CPLD_TOTAL_NUMBER;
}
EXPORT_SYMBOL(get_board_cpld_number);
ssize_t get_board_cpld_alias(unsigned int cpld_index, char *buf, size_t count)
{
/* add vendor codes here */
int len = -1 ;
switch(cpld_index)
{
case 1 :
len=sprintf(buf,"Base board CPLD\n");
break ;
case 2 :
len=sprintf(buf,"Come CPLD\n");
break ;
default:
len = -1 ;
break;
}
return len;
}
EXPORT_SYMBOL(get_board_cpld_alias);
ssize_t get_board_cpld_type(unsigned int cpld_index, char *buf, size_t count)
{
/* add vendor codes here */
int len = -1 ;
switch(cpld_index)
{
case 1 :
len=sprintf(buf,"LCMXO3LF-4300C-5BG400C CPLD\n");
break ;
case 2 :
len=sprintf(buf,"LCMXO3LF-2100C-5BG400C CPLD\n");
break ;
default:
len = -1 ;
break;
}
return len;
}
EXPORT_SYMBOL(get_board_cpld_type);
ssize_t get_board_cpld_firmware_version(unsigned int cpld_index, char *buf, size_t count)
{
/* add vendor codes here */
int len = -1;
switch(cpld_index){
case 1:
printk(KERN_INFO "BASE CPLD H version:0x%2.2x\n", inb(BASE_H_VER_ADDR));
printk(KERN_INFO "BASE CPLD M version:0x%2.2x\n", inb(BASE_M_VER_ADDR));
printk(KERN_INFO "BASE CPLD L version:0x%2.2x\n", inb(BASE_L_VER_ADDR));
len = sprintf(buf,"%x.%x.%x\n", inb(BASE_H_VER_ADDR),inb(BASE_M_VER_ADDR),inb(BASE_L_VER_ADDR));
break;
case 2:
printk(KERN_INFO "COME CPLD H version:0x%2.2x\n", inb(COME_H_VER_ADDR));
printk(KERN_INFO "COME CPLD M version:0x%2.2x\n", inb(COME_M_VER_ADDR));
printk(KERN_INFO "COME CPLD L version:0x%2.2x\n", inb(COME_L_VER_ADDR));
len = sprintf(buf,"%x.%x.%x\n", inb(COME_H_VER_ADDR),inb(COME_M_VER_ADDR),inb(COME_L_VER_ADDR));
break;
default:
len = -1;
break;
}
return len;
}
EXPORT_SYMBOL(get_board_cpld_firmware_version);
ssize_t get_board_cpld_board_version(unsigned int cpld_index, char *buf, size_t count)
{
/* add vendor codes here */
int len = -1;
switch(cpld_index){
case 1:
len = sprintf(buf,"%02X\n", inb(BASE_PCB_VER_ADDR));
break;
case 2:
len = sprintf(buf,"%02X\n", inb(COME_PCB_VER_ADDR));
break;
default:
len = -1;
break;
}
return len;
}
EXPORT_SYMBOL(get_board_cpld_board_version);
ssize_t get_board_cpld_test_reg(unsigned int cpld_index, char *buf, size_t count)
{
/* add vendor codes here */
int len = -1 ;
switch(cpld_index)
{
case 1 :
len=sprintf(buf,"%02x\n",inb(BASE_SCRATCH_ADDR));
break ;
case 2 :
len=sprintf(buf,"%02x\n",inb(COME_SCRATCH_ADDR));
break ;
default:
len = -1 ;
break;
}
return len;
}
EXPORT_SYMBOL(get_board_cpld_test_reg);
int set_board_cpld_test_reg(unsigned int cpld_index, unsigned int value)
{
/* add vendor codes here */
int ret = -1 ;
switch(cpld_index)
{
case 1 :
outb(value,BASE_SCRATCH_ADDR);
ret = 1;
break ;
case 2 :
outb(value,COME_SCRATCH_ADDR);
ret = 1;
break ;
default:
ret = -1 ;
break;
}
return ret;
}
EXPORT_SYMBOL(set_board_cpld_test_reg);
/***********************CPLD end******************************************************/
struct cpld_hq_data {
struct mutex cpld_lock;
uint16_t read_addr;
uint16_t base_addr;
};
struct cpld_hq_data *cpld_data;
static ssize_t get_cpld_reg_address(struct device *dev, struct device_attribute *attr, char *buf)
{
int len = 0;
// CPLD register is one byte
mutex_lock(&cpld_data->cpld_lock);
len = sprintf(buf, "0x%2.2x\n",inb(cpld_data->read_addr));
mutex_unlock(&cpld_data->cpld_lock);
return len;
}
/**
* Store the register address
* @param buf address wanted to be read value of
* @return number of bytes stored, or an error code
*/
static ssize_t set_cpld_reg_address(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
uint32_t addr;
char *last;
mutex_lock(&cpld_data->cpld_lock);
addr = (uint32_t)strtoul(buf, &last, 16);
if (addr == 0 && buf == last) {
return -EINVAL;
}
cpld_data->read_addr = cpld_data->base_addr + addr;
mutex_unlock(&cpld_data->cpld_lock);
return count;
}
static DEVICE_ATTR( getreg, 0600, get_cpld_reg_address, set_cpld_reg_address);
static ssize_t setreg_store(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
// CPLD register is one byte
uint16_t addr;
uint8_t value;
char *tok;
char clone[count+1];
char *pclone = clone;
char *last;
strcpy(clone, buf);
mutex_lock(&cpld_data->cpld_lock);
tok = strsep((char**)&pclone, " ");
if(tok == NULL){
mutex_unlock(&cpld_data->cpld_lock);
return -EINVAL;
}
addr = (uint16_t)strtoul(tok, &last,16);
if(addr == 0 && tok == last){
mutex_unlock(&cpld_data->cpld_lock);
return -EINVAL;
}
tok = strsep((char**)&pclone, " ");
if(tok == NULL){
mutex_unlock(&cpld_data->cpld_lock);
return -EINVAL;
}
value = (uint8_t)strtoul(tok, &last,16);
if(value == 0 && tok == last){
mutex_unlock(&cpld_data->cpld_lock);
return -EINVAL;
}
outb(value, cpld_data->base_addr + addr);
mutex_unlock(&cpld_data->cpld_lock);
return count;
}
//static DEVICE_ATTR_WO(setreg);
static DEVICE_ATTR( setreg, 0200, NULL , setreg_store);
/**
* Read all CPLD register in binary mode.
* @return number of byte read.
*/
static ssize_t dump_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
unsigned long i = 0;
ssize_t status;
mutex_lock(&cpld_data->cpld_lock);
begin:
if(i < count){
buf[i++] = inb(VERSION_ADDR + off);
off++;
msleep(1);
goto begin;
}
status = count;
exit:
mutex_unlock(&cpld_data->cpld_lock);
return status;
}
static BIN_ATTR_RO(dump, CPLD_REGISTER_SIZE);
static struct attribute *cpld_hq_attrs[] = {
&dev_attr_getreg.attr,
&dev_attr_setreg.attr,
NULL,
};
static struct bin_attribute *cpld_hq_bin_attrs[] = {
&bin_attr_dump,
NULL,
};
static struct attribute_group cpld_hq_attrs_group = {
.attrs = cpld_hq_attrs,
.bin_attrs = cpld_hq_bin_attrs,
};
static struct resource cpld_hq_resources[] = {
{
.start = 0xA100,
.end = 0xA1FF,
.flags = IORESOURCE_IO,
},
};
static void cpld_hq_dev_release( struct device * dev)
{
return;
}
static struct platform_device cpld_hq_dev = {
.name = CPLD_DRIVER_NAME,
.id = -1,
.num_resources = ARRAY_SIZE(cpld_hq_resources),
.resource = cpld_hq_resources,
.dev = {
.release = cpld_hq_dev_release,
}
};
static int cpld_hq_drv_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &cpld_hq_attrs_group);
return 0;
}
static struct kobject *watchdog = NULL;
static int cpld_hq_drv_probe(struct platform_device *pdev)
{
struct resource *resource;
int err = 0;
cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct cpld_hq_data), GFP_KERNEL);
if(!cpld_data)
return -ENOMEM;
mutex_init(&cpld_data->cpld_lock);
cpld_data ->read_addr = VERSION_ADDR;
cpld_data ->base_addr = BASE_CPLD_ADDR;
resource = platform_get_resource(pdev, IORESOURCE_IO, 0);
if(unlikely(!resource))
{
printk(KERN_ERR "Specified Resource Not Available...\n");
return -ENODEV;
}
err = sysfs_create_group(&pdev->dev.kobj, &cpld_hq_attrs_group);
if(err) {
printk(KERN_ERR "Cannot create sysfs for system CPLD\n");
return err;
}
printk(KERN_INFO "CPLD Probe driver");
printk(KERN_INFO "BASE CPLD Address:ox%2.2x\n", BASE_CPLD_ADDR);
printk(KERN_INFO "COMe CPLD Address:ox%2.2x\n", COME_CPLD_ADDR);
printk(KERN_INFO "BASE CPLD PCB version:0x%2.2x\n", inb(BASE_PCB_VER_ADDR));
printk(KERN_INFO "BASE CPLD H version:0x%2.2x\n", inb(BASE_H_VER_ADDR));
printk(KERN_INFO "BASE CPLD M version:0x%2.2x\n", inb(BASE_M_VER_ADDR));
printk(KERN_INFO "BASE CPLD L version:0x%2.2x\n", inb(BASE_L_VER_ADDR));
printk(KERN_INFO "COME CPLD PCB version:0x%2.2x\n", inb(COME_PCB_VER_ADDR));
printk(KERN_INFO "COME CPLD H version:0x%2.2x\n", inb(COME_H_VER_ADDR));
printk(KERN_INFO "COME CPLD M version:0x%2.2x\n", inb(COME_M_VER_ADDR));
printk(KERN_INFO "COME CPLD L version:0x%2.2x\n", inb(COME_L_VER_ADDR));
printk(KERN_INFO "GPIO base address:0x%x\n", inl(0x48));
//watchdog
watchdog = kobject_create_and_add("watchdog",&pdev->dev.kobj);
if (!watchdog)
{
return -ENOMEM;
}
if(sysfs_create_group(watchdog,&watchdog_attr_group))
{
printk(KERN_INFO"watchdog_attr_group failed\n");
return -1;
}
/*
int ret;
ret=watchdog_root_create();
if (ret < 0)
{
WDT_ERR("watchdog create error.\n");
return ret;
}
*/
return 0;
}
static struct platform_driver cpld_hq_drv = {
.probe = cpld_hq_drv_probe,
.remove = __exit_p(cpld_hq_drv_remove),
.driver = {
.name = CPLD_DRIVER_NAME,
},
};
static int __init cpld_hq_init(void)
{
/*Register Platform device and Platform Driver*/
platform_device_register(&cpld_hq_dev);
platform_driver_register(&cpld_hq_drv);
printk(KERN_INFO "%s: version %s loaded successfully\n", CPLD_DRIVER_NAME, CPLD_DRIVER_VER);
return 0;
}
static void __exit cpld_hq_exit(void)
{
/*Unregister Platform device and Platform Driver*/
platform_driver_unregister(&cpld_hq_drv);
platform_device_unregister(&cpld_hq_dev);
printk(KERN_INFO "%s: unloaded successfully\n", CPLD_DRIVER_NAME);
}
module_init(cpld_hq_init);
module_exit(cpld_hq_exit);
MODULE_AUTHOR("Technology Co.,Ltd.");
MODULE_DESCRIPTION("Common CPLD");
MODULE_VERSION(CPLD_DRIVER_VER);
MODULE_LICENSE("GPL");
4.3 i2c-ocores.h/c:ocores_attr_test在ocores_i2c_probe函数里添加节点
// i2c-ocores.h
#ifndef _LINUX_I2C_OCORES_H
#define _LINUX_I2C_OCORES_H
struct ocores_i2c_platform_data {
uint32_t reg_shift; /* register offset shift value */
uint32_t reg_io_width; /* register io read/write width */
uint32_t clock_khz; /* input clock in kHz */
uint32_t bus_khz; /* bus clock in kHz */
bool big_endian; /* registers are big endian */
uint8_t num_devices; /* number of devices in the devices list */
struct i2c_board_info const *devices; /* devices connected to the bus */
};
#endif /* _LINUX_I2C_OCORES_H */
struct ocores_i2c {
int prescale; //属性
};
static ssize_t i2c_clk_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ocores_i2c *i2c_test = dev_get_drvdata(dev);
if(i2c_test->prescale==0xe0)
{
return sprintf(buf,"100\n");
}
else if(i2c_test->prescale==0x71)
{
return sprintf(buf,"200\n");
}
else if(i2c_test->prescale==0x36)
{
return sprintf(buf,"400\n");
}
else
{
return sprintf(buf,"please echo 100/200/400 hz bus clk \n");
}
}
static ssize_t i2c_clk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t status;
int value;
struct ocores_i2c *i2c_test = dev_get_drvdata(dev);
i2c_test->prescale=0xe6;
u8 ctrl = oc_getreg(i2c_test, OCI2C_CONTROL);
// printk(KERN_INFO "into set \n");
status = kstrtoint(buf,10,&value);
if(value==100)
{
i2c_test->prescale=0xe0;
// dev_info(dev, "----------------------------100-------------------------\n");
}
else if(value==200)
{
i2c_test->prescale=0x71;
// dev_info(dev, "----------------------------200-------------------------\n");
}
else if(value==400)
{
i2c_test->prescale=0x36;
// dev_info(dev, "----------------------------400-------------------------\n");
}
else
{
dev_info(dev, "This I2C bus frequency cannot be provided\n");
return -EINVAL;
}
oc_setreg(i2c_test, OCI2C_PRELOW, i2c_test->prescale & 0xff);
oc_setreg(i2c_test, OCI2C_PREHIGH, i2c_test->prescale >> 8);
oc_setreg(i2c_test, OCI2C_CMD, OCI2C_CMD_IACK); /* Init the device */
oc_setreg(i2c_test, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN);
u8 data = oc_getreg(i2c_test,OCI2C_PRELOW);
printk(KERN_INFO "OCI2C_PRELOW: %x\n",data);
u8 data1 =oc_getreg(i2c_test,OCI2C_PREHIGH);
printk(KERN_INFO "OCI2C_PREHIGH: %x\n",data1);
u8 data2 = oc_getreg(i2c_test,OCI2C_CMD);
printk(KERN_INFO "OCI2C_CMD: %x\n",data2);
u8 data3 =oc_getreg(i2c_test,OCI2C_CONTROL);
printk(KERN_INFO "OCI2C_CONTROL: %x\n",data3);
// dev_info(dev, "Address: %lx\n", i2c_test->base);
// dev_info(dev, "Prescale: %d\n", i2c_test->prescale);
return size;
}
struct device_attribute i2c_ocores_test = __ATTR(i2c_bus_freq, 0600, i2c_clk_show,i2c_clk_store);
static struct attribute *i2c_ocores_dttrs[] = {
&i2c_ocores_test.attr,
NULL,
};
static struct attribute_group ocores_attr_test = {
.attrs = i2c_ocores_dttrs ,
};
static void i2c_ocores_dev_release(struct device * dev)
{
return;
}
/******************create /sys/bus/platform/device/ocores-i2c-hq.1***********************/
static struct platform_device i2c_ocores_dev = {
.name = "ocores-i2c-hq",
.id = -1,
.num_resources = 0,
.dev = {
.release = i2c_ocores_dev_release,
}
};
static int ocores_i2c_probe(struct platform_device *pdev)
{
struct ocores_i2c_platform_data *pdata;
struct resource *res;
struct ocores_i2c *i2c_test;
int irq;
int ret;
int i;
i2c_test = devm_kzalloc(&pdev->dev, sizeof(*i2c_test), GFP_KERNEL);
if (!i2c_test)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, i2c_test); /////////////////////////////
spin_lock_init(&i2c_test->process_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
i2c_test->base = devm_ioremap_resource(&pdev->dev, res);
printk(KERN_INFO "-----------------------------ocore Address-------------------------------: %lx\n", i2c_test->base);
// dev_info(pdev,"ocore Address: %lx\n", i2c_test->base);
if (IS_ERR(i2c_test->base))
return PTR_ERR(i2c_test->base);
/*******************************************create i2c_bus_freq****************************************/
int err = 0;
err = sysfs_create_group(&pdev->dev.kobj,&ocores_attr_test);
if(err)
{
printk(KERN_ERR "Cannot create sysfs for system I2C\n");
return err;
}
return 0;
err_clk:
clk_disable_unprepare(i2c_test->clk);
return ret;
}
# 读取0x38寄存器内容
echo 0x38 > getreg
cat getreg
# 往0x38寄存器写0x01值
echo 0x38 0x01 > setreg
4.1 switchboard.c:fpga下i2c访问CPLD1,CPLD2 , FPGA , QSFP
// fpga驱动,设备在fpga_i2c_adapter.c
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
#include "def.h"
#define MOD_VERSION "0.2.0"
#define MOD_NAME "switchboard"
#define CLASS_NAME "hq_fpga"
/* MISC */
#define FPGA_VERSION 0x0000
#define FPGA_VERSION_MJ_MSK 0xff00
#define FPGA_VERSION_MN_MSK 0x00ff
#define FPGA_SCRATCH 0x0004
#define FPGA_PORT_XCVR_READY 0x000c
#define PORT_XCVR_REGISTER_SIZE 0x1000
#define VIRTUAL_I2C_QSFP_PORT 16
#define VIRTUAL_I2C_SFP_PORT 0
#define SFF_PORT_TOTAL (VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT)
/* check qsfp is on or off*/
#define QSFP_PRESENT 0
#define QSFP_ABSENT 1
#define QSFP_PORT_NUM 16
#define QSFP_PORT_MASK 0x1
#define FPGA_CALLING_BUF_SIZE 20
#define FPGA_MODE_BIT32 32
#define FPGA_MODE_BIT8 8
#define FPGA_REG_NUM 4
enum PORT_TYPE
{
NONE,
QSFP,
SFP
};
struct sff_device_data {
int32_t portid;
enum PORT_TYPE port_type;
};
struct i2c_switch {
enum PORT_TYPE port_type; // QSFP/SFP transceiver port type.
char calling_name[FPGA_CALLING_BUF_SIZE];
};
struct switchboard_fpga_data {
struct device *sff_devices[SFF_PORT_TOTAL];
struct mutex fpga_lock; // For FPGA internal lock
void __iomem *fpga_read_addr;
void __iomem *fpga_irq_addr;
void __iomem *data_misc_base_addr;
void __iomem *data_irq_base_addr;
};
static struct class *fpgafwclass = NULL; // The device-driver class struct pointer
static struct i2c_switch fpga_i2c_bus_dev[] = {
/* BUS3 QSFP Exported as virtual bus */
{QSFP, "QSFP1"},
{QSFP, "QSFP2"},
{QSFP, "QSFP3"},
{QSFP, "QSFP4"},
{QSFP, "QSFP5"},
{QSFP, "QSFP6"},
{QSFP, "QSFP7"},
{QSFP, "QSFP8"},
{QSFP, "QSFP9"},
{QSFP, "QSFP10"},
{QSFP, "QSFP11"},
{QSFP, "QSFP12"},
{QSFP, "QSFP13"},
{QSFP, "QSFP14"},
{QSFP, "QSFP15"},
{QSFP, "QSFP16"},
};
static struct switchboard_fpga_data *fpga_data;
struct switchboard_fpga_data g_fpga_data;
EXPORT_SYMBOL(g_fpga_data);
/*
* Kernel object for other module drivers.
* Other module can use these kobject as a parent.
*/
static struct kobject *fpga = NULL;
/**
* Device node in sysfs tree.
*/
static struct device *sff_dev = NULL;
static void fpga_pci_remove(void);
/////////////////////////////////////////////////getreg start
/**
* Show the value of the register set by 'set_fpga_reg_address'
* If the address is not set by 'set_fpga_reg_address' first,
* The version register is selected by default.
* @param buf register value in hexstring
* @return number of bytes read, or an error code
*/
static ssize_t get_fpga_reg_value(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
uint32_t data;
if (!fpga_data)
return -EINVAL;
data = ioread32(fpga_data->fpga_read_addr);
return snprintf(buf, PAGE_SIZE, "0x%8.8x\n", data);
}
/**
* Store the register address
* @param buf address wanted to be read value of
* @return number of bytes stored, or an error code
*/
static ssize_t set_fpga_reg_address(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
ssize_t ret = 0;
uint32_t addr;
size_t value;
if (!fpga_data)
return -EINVAL;
ret = kstrtoul(buf, 16, &value); // 命令行echo进来的buff赋给value
if (ret < 0) {
return -EINVAL;
}
addr = (uint32_t)value;
if ((addr % FPGA_REG_NUM) != 0) {
return -EFAULT;
}
if (addr <= FPGA_MISC_END) {
fpga_data->fpga_read_addr = fpga_data->data_misc_base_addr + addr;
} else if ((addr >= FPGA_IRQ_BASE) && (addr <= FPGA_IRQ_END)) {
fpga_data->fpga_read_addr = fpga_data->data_irq_base_addr + addr - FPGA_IRQ_BASE;
} else {
return -EINVAL;
}
ret = (ssize_t)count;
return ret;
}
static struct kobj_attribute dev_attr_getreg = __ATTR(getreg, 0600, get_fpga_reg_value, set_fpga_reg_address);
/////////////////////////////////////////////////////getreg end
///////////////////////////////////////////////////scratch start
/**
* Show value of fpga scratch register
* @param buf register value in hexstring
* @return number of bytes read, or an error code
*/
static ssize_t get_fpga_scratch(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
if (!fpga_data)
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "0x%8.8x\n", ioread32(fpga_data->data_misc_base_addr + FPGA_SCRATCH) & 0xffffffff);
}
/**
* Store value of fpga scratch register
* @param buf scratch register value passing from user space
* @return number of bytes stored, or an error code
*/
static ssize_t set_fpga_scratch(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
ssize_t ret = 0;
size_t value;
uint32_t data;
if (!fpga_data)
return -EINVAL;
ret = kstrtoul(buf, 16, &value);
if (ret < 0) {
return -EINVAL;
}
data = (uint32_t)value;
iowrite32(data, fpga_data->data_misc_base_addr + FPGA_SCRATCH);
ret = (ssize_t)count;
return ret;
}
static struct kobj_attribute dev_attr_scratch = __ATTR(scratch, 0600, get_fpga_scratch, set_fpga_scratch);
/////////////////////////////////////////////////////scratch end
///////////////////////////////////////////////////setreg start
/**
* Store a value in a specific register address
* @param buf the value and address in format '0xhhhh 0xhhhhhhhh'
* @return number of bytes sent by user space, or an error code
*/
static ssize_t set_fpga_reg_value(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
// register are 4 bytes
ssize_t ret = 0;
size_t value;
uint32_t addr;
uint32_t data;
uint32_t mode = FPGA_MODE_BIT32; //32bit or 8bit
int8_t *tok;
int8_t clone[count + 1];
int8_t *pclone = clone;
void __iomem *fpga_write_addr;
if (!fpga_data)
return -EINVAL;
strncpy(clone, buf, count + 1);
tok = strsep((char **)&pclone, " ");
if (tok == NULL) {
return -EINVAL;
}
ret = kstrtoul(tok, 16, &value);
if (ret < 0) {
return -EINVAL;
}
addr = (uint32_t)value;
tok = strsep((char **)&pclone, " ");
if (tok == NULL) {
return -EINVAL;
}
ret = kstrtoul(tok, 16, &value);
if (ret < 0) {
return -EINVAL;
}
data = (uint32_t)value;
tok = strsep((char **)&pclone, " ");
if (tok == NULL) {
mode = FPGA_MODE_BIT32;
} else {
ret = kstrtoul(tok, 16, &value);
if (ret < 0) {
return -EINVAL;
}
mode = (uint32_t)value;
}
if ((mode != FPGA_MODE_BIT32) && (mode != FPGA_MODE_BIT8)) {
return -EINVAL;
}
if (addr <= FPGA_MISC_END) {
fpga_write_addr = fpga_data->data_misc_base_addr + addr;
} else if ((addr >= FPGA_IRQ_BASE) && (addr <= FPGA_IRQ_END)) {
fpga_write_addr = fpga_data->data_irq_base_addr + addr - FPGA_IRQ_BASE;
} else {
return -EINVAL;
}
if (mode == FPGA_MODE_BIT32) {
iowrite32(data, fpga_write_addr);
} else {
iowrite8((uint8_t)data, fpga_write_addr);
}
ret = (ssize_t)count;
return ret; //this count logic need be check
}
static struct kobj_attribute dev_attr_setreg = __ATTR(setreg, 0200, NULL, set_fpga_reg_value);
////////////////////////////////////////////////////setreg end
///////////////////////////////////////ready, fpga_version_m, fpga_version_s start
/**
* Show FPGA port AVS status
* @param buf AVS value
* @return number of bytes read, or an error code
*/
static ssize_t ready_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
uint32_t data;
if (!fpga_data)
return -EINVAL;
data = ioread32(fpga_data->data_misc_base_addr + FPGA_PORT_XCVR_READY);
return snprintf(buf, PAGE_SIZE, "%d\n", (uint8_t)data);
}
static struct kobj_attribute dev_attr_ready = __ATTR(ready, 0400, ready_show, NULL);
static ssize_t fpga_version_m_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
if (!fpga_data)
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%4.4x\n", ioread32(fpga_data->data_misc_base_addr + FPGA_VERSION) & 0xffff0000);
}
static struct kobj_attribute dev_attr_fpga_version_m = __ATTR(fpga_version_m, 0400, fpga_version_m_show, NULL);
static ssize_t fpga_version_s_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
if (!fpga_data)
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "%4.4x\n", ioread32(fpga_data->data_misc_base_addr + FPGA_VERSION) & 0x0000ffff);
}
static struct kobj_attribute dev_attr_fpga_version_s = __ATTR(fpga_version_s, 0400, fpga_version_s_show, NULL);
/////////////////////////////ready, fpga_version_m, fpga_version_s end
static ssize_t
dump_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
size_t i = 0;
ssize_t status;
uint8_t read_reg;
if ((off + count) > PORT_XCVR_REGISTER_SIZE) {
return -EINVAL;
}
if (!fpga_data)
return -EINVAL;
mutex_lock(&fpga_data->fpga_lock);
while (i < count) {
read_reg = ioread8(fpga_data->data_misc_base_addr + off + i);
buf[i++] = read_reg;
}
mutex_unlock(&fpga_data->fpga_lock);
status = count;
return status;
}
static BIN_ATTR_RO(dump, PORT_XCVR_REGISTER_SIZE); // RO:read only , ATTR:节点 , 创建dump二进制节点
static struct bin_attribute *fpga_bin_attrs[] = {
&bin_attr_dump,
NULL,
};
static struct attribute *fpga_attrs[] = {
&dev_attr_getreg.attr,
&dev_attr_scratch.attr,
&dev_attr_setreg.attr,
&dev_attr_ready.attr,
&dev_attr_fpga_version_m.attr,
&dev_attr_fpga_version_s.attr,
NULL,
};
static struct attribute_group fpga_attr_grp = {
.attrs = fpga_attrs,
.bin_attrs = fpga_bin_attrs,
};
/////////////////////////////////////qsfp_lpmode start
static ssize_t qsfp_lpmode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid = dev_data->portid;
if (!fpga_data)
return -EINVAL;
data = ioread32(fpga_data->data_misc_base_addr + QSFP_LPMOD_REG);
data = ((data & 0xffff) & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM)));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
static ssize_t qsfp_lpmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t status;
size_t value = 0;
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid = dev_data->portid;
if (!fpga_data)
return -EINVAL;
status = kstrtoul(buf, 0, &value);
if (status < 0)
return -EINVAL;
mutex_lock(&fpga_data->fpga_lock);
data = ioread32(fpga_data->data_misc_base_addr + QSFP_LPMOD_REG);
// if value is 0, disable the lpmode
if (!value)
data = data & ~((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
else
data = data | ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
iowrite32(data, fpga_data->data_misc_base_addr + QSFP_LPMOD_REG);
mutex_unlock(&fpga_data->fpga_lock);
status = size;
return status;
}
DEVICE_ATTR_RW(qsfp_lpmode);
////////////////////////////////////////qsfp_lpmode end
///////////////////////////////////////qsfp_rst start
static ssize_t qsfp_rst_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_misc_base_addr + QSFP_INT_RSLT);
data = data & ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
static ssize_t qsfp_rst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t status;
size_t value = 0;
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
status = kstrtoul(buf, 0, &value);
if (status < 0)
return -EINVAL;
mutex_lock(&fpga_data->fpga_lock);
data = ioread32(fpga_data->data_misc_base_addr + QSFP_INT_RSLT);
if (!value)
data = data & ~((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
else
data = data | ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
iowrite32(data, fpga_data->data_misc_base_addr + QSFP_INT_RSLT);
mutex_unlock(&fpga_data->fpga_lock);
status = size;
return status;
}
DEVICE_ATTR_RW(qsfp_rst);
///////////////////////////////////////qsfp_rst end
///////////////////////////////////////qsfp_pwren start
static ssize_t qsfp_pwren_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_misc_base_addr + QSFP_DD_PWR_EN);
data = data & ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
static ssize_t qsfp_pwren_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t status;
size_t value = 0;
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
status = kstrtoul(buf, 0, &value);
if (status < 0)
return -EINVAL;
mutex_lock(&fpga_data->fpga_lock);
data = ioread32(fpga_data->data_misc_base_addr + QSFP_DD_PWR_EN);
if (!value)
data = data & ~((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
else
data = data | ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
iowrite32(data, fpga_data->data_misc_base_addr + QSFP_DD_PWR_EN);
mutex_unlock(&fpga_data->fpga_lock);
status = size;
return status;
}
DEVICE_ATTR_RW(qsfp_pwren);
///////////////////////////////////////qsfp_pwren end
///////////////////////////////////////qsfp_pwrgd, qsfp_present start
static ssize_t qsfp_pwrgd_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_misc_base_addr + QSFP_DD_PWR_GD);
data = data & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
DEVICE_ATTR_RO(qsfp_pwrgd);
static ssize_t qsfp_present_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_misc_base_addr + QSFP_DD_PRESENT);
data = data & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? QSFP_ABSENT : QSFP_PRESENT));
}
DEVICE_ATTR_RO(qsfp_present);
///////////////////////////////////////qsfp_pwrgd, qsfp_present end
/////////////////////////////////////qsfp_int_clear start
static ssize_t qsfp_int_clear_show(struct device *dev, struct device_attribute *attr, char *buf)
{
if (!fpga_data)
return -EINVAL;
return snprintf(
buf, PAGE_SIZE, "0x%8.8x\n",
ioread32(fpga_data->data_irq_base_addr + QSFP_DD_INT_CLEAR - QSFP_DD_INT_CLEAR) & 0xf);
}
static ssize_t qsfp_int_clear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t ret = 0;
size_t value;
uint32_t data;
if (!fpga_data)
return -EINVAL;
ret = kstrtoul(buf, 0, &value);
if (ret < 0)
return -EINVAL;
data = (uint32_t)value;
iowrite32(data, fpga_data->data_irq_base_addr + QSFP_DD_INT_CLEAR - QSFP_DD_INT_CLEAR);
ret = (ssize_t)size;
return ret;
}
DEVICE_ATTR_RW(qsfp_int_clear);
///////////////////////////////////////qsfp_int_clear end
///////////////////////////////////////qsfp_int , qsfp_int_result start
static ssize_t qsfp_int_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_irq_base_addr + QSFP_DD_INT - QSFP_DD_INT_CLEAR);
data = data & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
DEVICE_ATTR_RO(qsfp_int);
static ssize_t qsfp_int_result_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_irq_base_addr + QSFP_DD_INT_RESULT - QSFP_DD_INT_CLEAR);
data = data & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
DEVICE_ATTR_RO(qsfp_int_result);
//////////////////////////////////////qsfp_int , qsfp_int_result end
/////////////////////////////////////////qsfp_int_mask start
static ssize_t qsfp_int_mask_show(struct device *dev, struct device_attribute *attr, char *buf)
{
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
data = ioread32(fpga_data->data_irq_base_addr + QSFP_DD_INT_MASK - QSFP_DD_INT_CLEAR);
data = data & (QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
return snprintf(buf, PAGE_SIZE, "%d\n", (data ? 1 : 0));
}
static ssize_t qsfp_int_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
ssize_t status;
size_t value = 0;
uint32_t data = 0;
struct sff_device_data *dev_data = dev_get_drvdata(dev);
uint32_t portid;
if ((!dev_data) || (!fpga_data))
return -EINVAL;
portid = dev_data->portid;
status = kstrtoul(buf, 0, &value);
if (status < 0)
return -EINVAL;
mutex_lock(&fpga_data->fpga_lock);
data = ioread32(fpga_data->data_irq_base_addr + QSFP_DD_INT_MASK - QSFP_DD_INT_CLEAR);
if (!value)
data = data & ~((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
else
data = data | ((uint32_t)QSFP_PORT_MASK << ((portid - 1) % QSFP_PORT_NUM));
iowrite32(data, fpga_data->data_irq_base_addr + QSFP_DD_INT_MASK - QSFP_DD_INT_CLEAR);
mutex_unlock(&fpga_data->fpga_lock);
status = size;
return status;
}
DEVICE_ATTR_RW(qsfp_int_mask);
//////////////////////////////////////qsfp_int_mask end
static struct attribute *qsfp_attrs[] = {
&dev_attr_qsfp_rst.attr,
&dev_attr_qsfp_lpmode.attr,
&dev_attr_qsfp_pwren.attr,
&dev_attr_qsfp_pwrgd.attr,
&dev_attr_qsfp_int_clear.attr,
&dev_attr_qsfp_int.attr,
&dev_attr_qsfp_int_result.attr,
&dev_attr_qsfp_int_mask.attr,
&dev_attr_qsfp_present.attr,
NULL,
};
static struct attribute_group qsfp_attr_grp = {
.attrs = qsfp_attrs,
};
static const struct attribute_group *qsfp_attr_grps[] = {&qsfp_attr_grp, NULL};
/////////////////////////////////////////port_led_mode start
static ssize_t port_led_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// value can be "nomal", "test"
uint32_t led_mode = 0;
if (!fpga_data)
return -EINVAL;
led_mode = ioread32(fpga_data->data_misc_base_addr + QSFP_DD_LEDMOD);
return snprintf(buf, PAGE_SIZE, "%s \n", led_mode ? "test" : "normal");
}
static ssize_t port_led_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint32_t led_mode;
ssize_t status;
if (!fpga_data)
return -EINVAL;
if (sysfs_streq(buf, "test")) {
led_mode = 0x01;
} else if (sysfs_streq(buf, "normal")) {
led_mode = 0x00;
} else {
return -EINVAL;
}
iowrite32(led_mode, fpga_data->data_misc_base_addr + QSFP_DD_LEDMOD);
status = (ssize_t)size;
return status;
}
DEVICE_ATTR_RW(port_led_mode);
/////////////////////////////////////////port_led_mode end
/////////////////////////////////////////port_led_test start
static ssize_t port_led_test_show(struct device *dev, struct device_attribute *attr, char *buf)
{
if (!fpga_data)
return -EINVAL;
return snprintf(buf, PAGE_SIZE, "0x%8.8x\n", ioread32(fpga_data->data_misc_base_addr + QSFP_DD_LEDTEST) & 0xffff);
}
static ssize_t port_led_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
size_t value = 0;
ssize_t status;
if (!fpga_data)
return -EINVAL;
if (kstrtoul(buf, 0, &value) < 0)
return -EINVAL;
iowrite32(value & 0xffff, fpga_data->data_misc_base_addr + QSFP_DD_LEDTEST);
status = (ssize_t)size;
return status;
}
DEVICE_ATTR_RW(port_led_test);
/////////////////////////////////////////port_led_test end
static struct attribute *sff_led_test[] = {
&dev_attr_port_led_mode.attr,
&dev_attr_port_led_test.attr,
NULL,
};
static struct attribute_group sff_led_test_grp = {
.attrs = sff_led_test,
};
static struct device *switchboard_sff_init(int32_t port_id)
{
struct sff_device_data *new_data;
struct device *new_device;
if (IS_ERR(sff_dev)) {
return NULL;
}
new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
if (!new_data) {
pr_err("Cannot alloc sff device data for @port%d", port_id);
return NULL;
}
/* The QSFP port ID start from 1 */
new_data->portid = port_id + 1;
new_data->port_type = fpga_i2c_bus_dev[port_id].port_type;
new_device = device_create_with_groups(
fpgafwclass, sff_dev, MKDEV(0, 0), new_data, qsfp_attr_grps, "%s", fpga_i2c_bus_dev[port_id].calling_name);
if (IS_ERR(new_device)) {
pr_err("Cannot create sff device for @port%d", port_id);
kfree(new_data);
return NULL;
}
return new_device;
}
static int32_t switchboard_drv_probe(struct platform_device *pdev)
{
int32_t ret = 0;
int32_t portid_count;
uint32_t fpga_version;
struct resource *res_misc;
struct resource *res_irq;
fpga_data = devm_kzalloc(&pdev->dev, sizeof(struct switchboard_fpga_data), GFP_KERNEL);
if (!fpga_data) {
ret = -ENOMEM;
goto err_exit;
}
res_misc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_misc) {
fpga_data->data_misc_base_addr = devm_ioremap_resource(&pdev->dev, res_misc);
if (IS_ERR(fpga_data->data_misc_base_addr))
return PTR_ERR(fpga_data->data_misc_base_addr);
} else {
ret = -ENODEV;
goto err_exit;
}
res_irq = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res_irq) {
fpga_data->data_irq_base_addr = devm_ioremap_resource(&pdev->dev, res_irq);
if (IS_ERR(fpga_data->data_irq_base_addr))
return PTR_ERR(fpga_data->data_irq_base_addr);
} else {
ret = -ENODEV;
goto err_exit;
}
fpga_version = ioread32(fpga_data->data_misc_base_addr + FPGA_VERSION);
pr_info("FPGA VERSION : %8.8x\n", fpga_version);
fpgafwclass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(fpgafwclass)) {
ret = PTR_ERR(fpgafwclass);
goto err_exit;
}
/* The device class need to be instantiated before this function called */
BUG_ON(fpgafwclass == NULL);
fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj);
if (!fpga) {
ret = -ENOMEM;
goto err_remove_fpgaclass;
}
ret = sysfs_create_group(fpga, &fpga_attr_grp);
if (ret != 0) {
pr_err("Cannot create FPGA sysfs attributes\n");
goto err_remove_fpga;
}
sff_dev = device_create(fpgafwclass, NULL, MKDEV(0, 0), NULL, "sff_device");
if (IS_ERR(sff_dev)) {
ret = PTR_ERR(sff_dev);
goto err_remove_grp_fpga;
}
ret = sysfs_create_group(&sff_dev->kobj, &sff_led_test_grp);
if (ret != 0) {
goto err_remove_sff;
}
ret = sysfs_create_link(
&pdev->dev.kobj, &sff_dev->kobj, "SFF"); // we can use this create link from cpld, link switch to cpld kobj
if (ret != 0) {
goto err_remove_grp_sff;
}
/* Init SFF devices */
for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) {
fpga_data->sff_devices[portid_count] = switchboard_sff_init(portid_count);
}
// Set default read address to VERSION
fpga_data->fpga_read_addr = fpga_data->data_misc_base_addr;
fpga_data->fpga_irq_addr = fpga_data->data_irq_base_addr;
mutex_init(&fpga_data->fpga_lock);
g_fpga_data =*fpga_data;
pr_info("Switchboard Driver init success\n");
return 0;
err_remove_grp_sff:
sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp);
err_remove_sff:
device_destroy(fpgafwclass, MKDEV(0, 0));
err_remove_grp_fpga:
sysfs_remove_group(fpga, &fpga_attr_grp);
err_remove_fpga:
kobject_put(fpga);
err_remove_fpgaclass:
fpga_pci_remove();
err_exit:
pr_err("Switchboard Driver init fail\n");
return ret;
}
static void fpga_pci_remove(void)
{
if (!IS_ERR(fpgafwclass)) {
class_unregister(fpgafwclass);
class_destroy(fpgafwclass);
}
};
static int32_t switchboard_drv_remove(struct platform_device *pdev)
{
int32_t portid_count;
struct sff_device_data *rem_data;
sysfs_remove_link(&pdev->dev.kobj, "SFF"); // add for remove link before remove attr group
for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) {
if (fpga_data->sff_devices[portid_count] != NULL) {
rem_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]);
device_unregister(fpga_data->sff_devices[portid_count]);
if (rem_data)
kfree(rem_data);
}
}
if (!IS_ERR(sff_dev)) {
sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp);
device_destroy(fpgafwclass, MKDEV(0, 0));
}
if (fpga) {
sysfs_remove_group(fpga, &fpga_attr_grp);
kobject_put(fpga);
}
fpga_pci_remove();
pr_info("Switchboard Driver remove success\n");
return 0;
}
static struct platform_driver switchboard_drv = {
.probe = switchboard_drv_probe,
.remove = __exit_p(switchboard_drv_remove),
.driver = {
.name = SWITCH_DRIVER_NAME,
},
};
static int32_t __init switchboard_init(void)
{
platform_driver_register(&switchboard_drv);
pr_info("%s: version %s loaded successfully\n", MOD_NAME, MOD_VERSION);
return 0;
}
static void __exit switchboard_exit(void)
{
platform_driver_unregister(&switchboard_drv);
pr_info("%s: unloaded successfully\n", SWITCH_DRIVER_NAME);
}
module_init(switchboard_init);
module_exit(switchboard_exit);
MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("Linecard or SWITCHBOARD");
MODULE_VERSION(MOD_VERSION);
MODULE_LICENSE("GPL");
#ifndef _DEF_H
#define _DEF_H
/* Reserve bus numbers for CPU or FPGA */
#define FPGA_DEVICE_ID 0x7021
#define FPGA_MMIO_BAR 0
#define LC_CPLD_ADD 0x0D
#define OCORE_I2C_BASE 0x2000
#define FPGA_MISC_START 0x0000
#define FPGA_MISC_END 0x0fff
#define FPGA_IRQ_BASE 0x3000
#define FPGA_IRQ_END 0x5fff
#define FPGA_MISC_SEC 0
#define FPGA_IRQ_SEC 1
#define QSFP_DD_PRESENT 0x0010
#define QSFP_DD_PWR_GD 0x0014
#define QSFP_DD_PWR_EN 0x0020
#define QSFP_INT_RSLT 0x0018
#define QSFP_LPMOD_REG 0x001c
#define QSFP_DD_LEDMOD 0x0024
#define QSFP_DD_LEDTEST 0x0028
#define SWITCH_DRIVER_NAME "hq_switch"
#endif