【notes9】

文章目录

  • 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

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

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函数里添加节点

c 复制代码
// 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 */
c 复制代码
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;
}
shell 复制代码
# 读取0x38寄存器内容
echo 0x38 > getreg
cat getreg
# 往0x38寄存器写0x01值
echo 0x38 0x01 > setreg

4.1 switchboard.c:fpga下i2c访问CPLD1,CPLD2 , FPGA , QSFP

c 复制代码
// 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");
c 复制代码
#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
相关推荐
RisunJan2 小时前
Linux命令-objdump(显示二进制文件信息)
linux·运维
bloglin999993 小时前
TabClaw(交互式表格分析 AI 智能体)在线下载,离线部署
linux·运维·服务器·tabclaw
云栖梦泽3 小时前
WIFI通信测试
linux·运维·服务器·压力测试
Dlrb12113 小时前
Linux系统编程-进程回收
linux·exec·进程·进程回收
妖怪喜欢风3 小时前
connecthomeip/matter 专栏文章汇总
linux·matter
wefg13 小时前
【Linux】网络高级 IO
linux·运维·服务器
kebidaixu3 小时前
VSCode 安装和使用 Claude Code 完整指南
linux