【bmc7】fan


2.算法

pwm:0%(cpld的0即0x0), 50%(cpld的127即0x7f), 100%(cpld的255即0xff)

调节参数达到某一稳定值,用比例积分微分式控制(PID)算法,温度影响计算得出的新的风扇PWM转速 = 当前风扇最大转速 + 比例系数(P) * (所有线卡上芯片当前最大温度 – 芯片上次温度) + 积分系数(I)*(所有线卡上芯片当前最大温度 – 设定的目标温度) + 微分系数(D)*(所有线卡上芯片当前最大温度 – 芯片上次温度 – (芯片上次温度 – 芯片上上次温度 ))。内存中需要记录上次和上上次温度值。
在这里插入图片描述
进风口sensor芯片即环境温度采用如下图的线性模式调速,如下图T(k)<T(k-1)<T(k-2)时判定为降温过程,使用降温曲线(蓝色)进行调速,其余状态(升温或温度稳定状态)以升温曲线(黑色)进行调速。降温调速曲线与升温曲线间有3℃以上gap,避免环温小幅变化时风扇转速变化。
在这里插入图片描述
线性算法曲线可以分为4种,持续/震荡上升,持续/震荡下降。判断震荡曲线时,当采样点温度与上一次温度差小于回滞温度时,此采样点应判定为脏数据丢弃,PWM取上次PWM保持转速稳定。
在这里插入图片描述

if (temp > line_node->last_temp && line_node->last_temp > line_node->last_temp_2) //keep raising
       is_raising = true;
else if (temp < line_node->last_temp && line_node->last_temp < line_node->last_temp_2) //keep failing
       is_raising = false;
else { // 第一次和第二次上升下降相反如上图1和2
    if ((fabs(temp - line_node->last_temp) >= line_node->temp_hyst) && (temp > line_node->last_temp)) //snake raising //图1
        is_raising = true;
    else if ((fabs(temp - line_node->last_temp) >= line_node->temp_hyst) && (temp < line_node->last_temp)) //snake failing //图2
        is_raising = false;
    else { // 小于回滞温度
        //dirty data, we use last temp and last curve to cal pwm
        //to aovid fsc donot take affect when inlet remain unchanged
        temp = line_node->last_temp;
        is_raising = line_node->last_curve;
    }
}

3.

if( (g_last_temp_status >= CPU_MIN_ALARM) && (rc == NO_ERROR) )   // 需求:上次温度不正常(没有温度丢失,因为丢失的话,赋为上次温度), 这次温度正常, 30s持续满转(30s内维持上次温度/转速不变rc = g_last_temp_status)后才自适应
// 温度丢失大于15秒直接满转,15秒内维持丢失前转速不变,g_temp_recover_time=15 :上次温度正常,这次温度丢失,15秒内维持丢失前温度不变
{                               // 15          20
    if ( g_temp_recover_cnt*g_fsc_period < g_temp_recover_time )
    {
        if( g_temp_recover_cnt < MAX_FAIL_COUNT )
            g_temp_recover_cnt++;     // 0 ,1 ,2  每次只进1次
        rc = g_last_temp_status;  // 第0次【上次不正常,这次正常】cnt=1 (rc!=0不正常满转)   ,  第1次15秒后【上次不正常,这次正常】cnt=2 (rc!=0不正常满转)
    }
    else
    {
        g_last_temp_status=rc;  // 第2次30秒后【上次不正常,这次正常】cnt=3(rc=NO_ERROR正常自适应)
        g_temp_recover_cnt=0;
    }
}
else
{
    g_last_temp_status=rc;  // 第0次 【 (上次正常,这次正常) 或 (上次不正常,这次不正常)】
    g_temp_recover_cnt=0;
}
#include <stdio.h>
#include <stdlib.h>

int g_board_cnt = 6;
short switch_fatal_cnt = 0;
static int get_switch_temp_single_board(int board_index)
{
    switch_fatal_cnt |= (0x1 << board_index);   // |:有1为1 :将switch_fatal_cnt的第board_index位设为1
    printf("111,0x%x,%d\n", switch_fatal_cnt, switch_fatal_cnt);

    // switch_fatal_cnt = ~(0x1 << board_index);   // 得到一个只有第board_index位为0的二进制数
    // switch_fatal_cnt &= ~(0x1 << board_index);     // &:有0为0 :将switch_fatal_cnt的第board_index位设为0
    // printf("222,0x%x,%d\n", switch_fatal_cnt, switch_fatal_cnt);
}

int main()
{
    int i, rc , cnt = 0;
    for (i = 0; i < g_board_cnt; i++) {
        rc=get_switch_temp_single_board(i);
        cnt=(switch_fatal_cnt & (0x1<<i)) >> i;  //保留 switch_fatal_cnt 中第i位的值,其余位为0, 将保留的值移回到最低位
        printf("333,%d\n", cnt);
    }
}

$ gcc a.c -o a
$ ./a
111,0x1,1
333,1
111,0x3,3
333,1
111,0x7,7
333,1
111,0xf,15
333,1
111,0x1f,31
333,1
111,0x3f,63
333,1

2.fcbcpld.c:fan10是fan1的rear rotor speed 后转子速度, 前转子front

如下第一列也就是ratio比例(reg:0-ff),在get_speed_tolerance函数中用到。
在这里插入图片描述

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <i2c_dev_sysfs.h>

#define REG_PWM_INLET1    0x32 // ...
#define REG_PWM_INLET5    0x72
#define REG_PWM_OUTLET1   0x37 // ...
#define REG_PWM_OUTLET5   0x77
#define SPEED_TARGET_ARRAY_SIZE   11

#define  FAN_LED_STATUS_DARK                     0x41
#define  FAN_LED_STATUS_GREEN                    0x44
#define  FAN_LED_STATUS_YELLOW                   0x54
#define  FAN_LED_STATUS_GREEN_LIGHT_FLASHING     0x46
#define  FAN_LED_STATUS_YELLOW_LIGHT_FLASHING    0x56

typedef enum{  // 一串数字
    DARK,      // 0
    GREEN,
    YELLOW,
    RED,
    BLUE,
    GREEN_LIGHT_FLASHING,
    YELLOW_LIGHT_FLASHING,
    RED_LIGHT_FLASHING,
    BLUE_LIGHT_FLASHING
}fan_led_status_t;
typedef enum{
    FAN1_MIN,  // 0 , 模拟寄存器dev_attr->ida_reg,当标志位用
    FAN1_MAX,
    FAN1_TOLERANCE, //误差值
    FAN1_TARGET, //实际值
    FAN10_MIN,
    FAN10_MAX,
    FAN10_TOLERANCE,
    FAN10_TARGET,
	...
} threshold_reg_t;

typedef struct{  // 定义风扇 前后转子转速结构体
    int alarm_min;
    int alarm_max;
    int tolerance;
    int target;
}threshold_data_t;  
typedef struct{  // 分类存储传进的值,在赋给新变量传出去
    threshold_data_t fan1;
    threshold_data_t fan10;
	...
    threshold_data_t fan5;
    threshold_data_t fan50;
    int motor_num;
    int fan_num;
}fan_threshold_data_t;
fan_threshold_data_t fan_threshold_data;

//begain
typedef enum
{
    RATIO, /* {Duty Cycle(%) */    // 0
    INLET, /* IN FAN Typ. */
    OUTLET /* OUT FAN Typ. */
} threshold_rotor_t;

const int speed_target[SPEED_TARGET_ARRAY_SIZE][3] = { //11行3列
    /* {Duty Cycle(%), IN FAN Typ., OUT FAN Typ.} */
    {0, 11818, 11247},
    {10, 2329, 2148},
    {20, 4889, 4496},
    {30, 7127, 6886},
    {40, 9420, 9106},
    {50, 11802, 11239},
    {60, 14257, 13556},
    {70, 16740, 16045},
    {80, 19191, 17742},
    {90, 21535, 19279},
    {100, 22628, 19293}};

int get_speed_target(int ratio, threshold_reg_t reg)
{
    int i = 0;
    threshold_rotor_t rotor_index = 0;
    int value = 0;

    switch (reg) {
        case FAN1_TARGET: //case里没有break
		...
        case FAN5_TARGET:
        case FAN1_TOLERANCE:
		...
        case FAN5_TOLERANCE:
            rotor_index = INLET; //如果上面匹配到某一个,这里一定会走到,值为1
            break;

        case FAN10_TARGET:
		...
        case FAN50_TARGET:
        case FAN10_TOLERANCE:
		...
        case FAN50_TOLERANCE:
            rotor_index = OUTLET; //值为2
            break;
        default:
            break;
    }

    for (i = 0; i < SPEED_TARGET_ARRAY_SIZE - 1; i++) { // i:0-9,行,一直循环直到匹配到下一行if
        if (ratio <= speed_target[i + 1][RATIO]) { // 循环第一次即第1行(不是第0行)第0列即10 , 判断ratio在哪个区间如ratio=55在50-60间即确定i和i+1
            if (speed_target[i + 1][rotor_index] != speed_target[i][rotor_index]) {
                value = ((speed_target[i + 1][rotor_index] - speed_target[i][rotor_index])   // =(14257-11802)/(60-50)*(60-55)+11802 或 =(14257-11802)/(60-50)*(55-60)+14257
                    * (ratio - speed_target[i + 1][RATIO]))                 
                    / (speed_target[i + 1][RATIO] - speed_target[i][RATIO])
                    + speed_target[i + 1][rotor_index];
            } else {
                value = speed_target[i][rotor_index];
            }
            break;  //跳出for,不是if,value只取一个值,所以这里break出for循环
        }
    }
    return value;  // value是根据传入的ratio在speed_target二维数组中计算出INLET或OUTLET值
}

int get_speed_tolerance(int ratio, threshold_reg_t reg)
{
    int value = 0;

    if ((ratio <= 5) || (ratio > 10))
    {
        value = get_speed_target(ratio, reg) / 10;  // 相当于*10%
    } else {
        value = 500;
    }

    return value;  // 转速误差值,不是实际RPM转速
}
/end

static ssize_t chip_target_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr; // 传进来dev_attr->ida_reg为FAN1_TARGET

    int value = 0;
    int ratio = 0;
    unsigned char reg = 0;
    switch(dev_attr->ida_reg)
    {
        case FAN1_TARGET:
            reg = REG_PWM_INLET1;
            break;
		...
        case FAN5_TARGET:
            reg = REG_PWM_INLET5;
            break;
        case FAN10_TARGET:
            reg = REG_PWM_OUTLET1;
            break;
		...
        case FAN50_TARGET:
            reg = REG_PWM_OUTLET5;
            break;
        default:
            break;
    }

    ratio = i2c_smbus_read_byte_data(client, reg);  // 0-ff , 0-255
    ratio *= 100;
    if (ratio % 255 != 0)
        ratio = ratio / 255 + 1;
    else
        ratio = ratio / 255;
    value = get_speed_target(ratio, dev_attr->ida_reg);  // ratio(0%-100%) , reg区分inlet还是outlet
    return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}

static ssize_t chip_tolerance_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;

    int value = 0;
    int ratio = 0;
    unsigned char reg = 0;
    switch(dev_attr->ida_reg)
    {
        case FAN1_TOLERANCE:
            reg = REG_PWM_INLET1;
            break;
		...
        case FAN5_TOLERANCE:
            reg = REG_PWM_INLET5;
            break;
        case FAN10_TOLERANCE:
            reg = REG_PWM_OUTLET1;
            break;
		...
        case FAN50_TOLERANCE:
            reg = REG_PWM_OUTLET5;
            break;
        default:
            break;
    }

    ratio = i2c_smbus_read_byte_data(client, reg);
    ratio *= 100;
    if (ratio % 255 != 0)
        ratio = ratio / 255 + 1;
    else
        ratio = ratio / 255;
    value = get_speed_tolerance(ratio, dev_attr->ida_reg);
    return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}

static int chip_thrshold_write(threshold_reg_t reg,long value )
{
    int result = -1;
    switch(reg)
    {
    case FAN1_MIN:
        fan_threshold_data.fan1.alarm_min = value;
        break;
    case FAN1_MAX:
        fan_threshold_data.fan1.alarm_max = value;
        break;
    case FAN10_MIN:
        fan_threshold_data.fan10.alarm_min = value;
        break;
    case FAN10_MAX:
        fan_threshold_data.fan10.alarm_max = value;
        break;
	...
    case FAN5_MIN:
        fan_threshold_data.fan5.alarm_min = value;
        break;
    case FAN5_MAX:
        fan_threshold_data.fan5.alarm_max = value;
        break;
    case FAN50_MIN:
        fan_threshold_data.fan50.alarm_min = value;
        break;
    case FAN50_MAX:
        fan_threshold_data.fan50.alarm_max = value;
        break;
    default:
        break;
    }
    return 0;
}

static int chip_thrshold_read(threshold_reg_t reg )
{
    int result = -1;
    switch(reg)
    {
    case FAN1_MIN:
        result = fan_threshold_data.fan1.alarm_min;
        break;
    case FAN1_MAX:
        result = fan_threshold_data.fan1.alarm_max;
        break;
    case FAN10_MIN:
        result = fan_threshold_data.fan10.alarm_min;
        break;
    case FAN10_MAX:
        result = fan_threshold_data.fan10.alarm_max;
        break;
	...
    case FAN5_MIN:
        result = fan_threshold_data.fan5.alarm_min;
        break;
    case FAN5_MAX:
        result = fan_threshold_data.fan5.alarm_max;
        break;
    case FAN50_MIN:
        result = fan_threshold_data.fan50.alarm_min;
        break;
    case FAN50_MAX:
        result = fan_threshold_data.fan50.alarm_max;
        break;
    default:
        break;
    }
    return result;
}

static ssize_t chip_threshold_show(struct device *dev,struct device_attribute *attr,char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int result = -1;

    result =  chip_thrshold_read(dev_attr->ida_reg);
    return scnprintf(buf, PAGE_SIZE, "%d\n",  result);
}

static ssize_t chip_threshold_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int rc = 0;
    long write_value=0;
    if (buf == NULL) {
        return -ENXIO;
    }

    rc = kstrtol(buf, 0, &write_value);
    if (rc != 0) {
        return count;
    }
    chip_thrshold_write(dev_attr->ida_reg,write_value);
    return count;
}

static ssize_t fan_led_status_show(struct device *dev,struct device_attribute *attr,char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    fan_led_status_t fan_led_status;
    int value = -1;
    value = i2c_smbus_read_byte_data(client,(dev_attr->ida_reg));
    switch (value)
    {
      case FAN_LED_STATUS_DARK:
          fan_led_status = DARK;
          break;
      case FAN_LED_STATUS_GREEN:
          fan_led_status = GREEN;
          break;
      case FAN_LED_STATUS_YELLOW:
          fan_led_status = YELLOW;
          break;
      case FAN_LED_STATUS_GREEN_LIGHT_FLASHING:
          fan_led_status = GREEN_LIGHT_FLASHING;
          break;
      case FAN_LED_STATUS_YELLOW_LIGHT_FLASHING:
          fan_led_status = YELLOW_LIGHT_FLASHING;
          break;
      default:
          return scnprintf(buf, PAGE_SIZE, "NA\n");
          break;
    }
    return scnprintf(buf, PAGE_SIZE, "%d\n", fan_led_status);
}

static ssize_t fan_led_status_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int rc = 0, write_value = 0;
    if (buf == NULL) {
        return -ENXIO;
    }
    rc = kstrtoint(buf, 10, &write_value);
    if (rc != 0) {
        return count;
    }

    switch(write_value)
    {
        case DARK:
              rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_DARK);
              if (rc != 0) {
                return count;
              }
              break;
        case GREEN:
              rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_GREEN);
              if (rc != 0) {
                return count;
              }
              break;
        case YELLOW:
              rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_YELLOW);
              if (rc != 0) {
                return count;
              }
              break;
        case GREEN_LIGHT_FLASHING:
              rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_GREEN_LIGHT_FLASHING);
              if (rc != 0) {
                return count;
              }
              break;
        case YELLOW_LIGHT_FLASHING:
              rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), FAN_LED_STATUS_YELLOW_LIGHT_FLASHING);
              if (rc != 0) {
                return count;
              }
              break;
        default:
              break;
    }
    return count;
}

static ssize_t fan_pwm_ctrl_show(struct device *dev,struct device_attribute *attr,char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int value = -1;
    value = i2c_smbus_read_byte_data(client,(dev_attr->ida_reg));  //0x7F  //127
    value *= 100;
    if ( value%255 != 0 )
        value = value/255+1;
    else
        value = value/255;
    return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}

static ssize_t fan_pwm_ctrl_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int rc = 0, write_value = 0, right_value = 0;
    if (buf == NULL) {
        return -ENXIO;
    }
    rc = kstrtoint(buf, 10, &write_value);
    if (rc != 0) {
        return count;
    }
    if (((write_value-1)*255) %255 !=0 )
        right_value = (write_value-1)*255/100;
    else
        right_value = write_value*255/100;
    rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), right_value);
    if (rc != 0) {
      return count;
    }
    return count;
}

static ssize_t fan_watchdog_count_show(struct device *dev,struct device_attribute *attr,char *buf)
{
    unsigned int val = i2c_dev_read_word_littleendian(dev,attr);
    if(val <= 0)
    {
        DEV_DEBUG("Read Fan_Watchdog error!\n");
    }
    return scnprintf(buf, PAGE_SIZE, "%d\n", val);
}

static ssize_t fan_watchdog_count_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    const i2c_dev_attr_st *dev_attr = i2c_attr->isa_i2c_attr;
    int rc = 0, write_value = 0;
    if (buf == NULL) {
        return -ENXIO;
    }
    rc = kstrtoint(buf, 10, &write_value);
    if (rc != 0) {
        return count;
    }
    rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg), write_value&0xff);
    if (rc != 0) {
      return count;
    }
    rc = i2c_smbus_write_byte_data(client, (dev_attr->ida_reg+1), (write_value>>8)&0xff);
    if (rc != 0) {
      return count;
    }
    return count;
}

static ssize_t fcbcpld_fan_rpm_show(struct device *dev,
                                    struct device_attribute *attr,
                                    char *buf)
{
  int val;
  val = i2c_dev_read_byte(dev, attr);
  if (val < 0) {
    return val;
  }
  /* Multiply by 150 to get the RPM */
  val *= 150;
  return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}

//config fan rear index as real index*10
//only for fan speed in order to disapply in sensors command
static const i2c_dev_attr_st fcbcpld_attr_table[] = {
  {
    "fan1_input",
    "fan 1 front speed",
    fcbcpld_fan_rpm_show,
    I2C_DEV_ATTR_STORE_DEFAULT,
    0x30, 0, 8,
  },
  {
    "fan10_input",
    "fan 1 rear speed",
    fcbcpld_fan_rpm_show,
    I2C_DEV_ATTR_STORE_DEFAULT,
    0x31, 0, 8,
  },
  {
    "fan1_pwm_ctrl",
    "0xff means max",
    fan_pwm_ctrl_show,
    fan_pwm_ctrl_store,
    0x32, 0, 8,
  },
  {
    "fan10_pwm_ctrl",
    "0xff means max",
    fan_pwm_ctrl_show,
    fan_pwm_ctrl_store,
    0x37, 0, 8,
  },
  {
    "fan1_led_status",
    NULL,
    fan_led_status_show,
    fan_led_status_store,
    0x34, 0, 8,
  },
  {
    "fan1_min",
    NULL,
    chip_threshold_show,
    chip_threshold_store,
    FAN1_MIN, 0, 8,
  },
  {
    "fan1_max",
    NULL,
    chip_threshold_show,
    chip_threshold_store,
    FAN1_MAX, 0, 8,
  },
  {
    "fan1_tolerance",
    NULL,
    chip_tolerance_show,
    NULL,
    FAN1_TOLERANCE, 0, 8,
  },
  {
    "fan1_target",
    NULL,
    chip_target_show,
    NULL,
    FAN1_TARGET, 0, 8,
  },
  {
    "fan10_min",
    NULL,
    chip_threshold_show,
    chip_threshold_store,
    FAN10_MIN, 0, 8,
  },
};

static i2c_dev_data_st fcbcpld_data;

/* FCBCPLD id */
static const struct i2c_device_id fcbcpld_id[] = {
  { "fcbcpld", 0 },
  { },
};
MODULE_DEVICE_TABLE(i2c, fcbcpld_id);

/* Return 0 if detection is successful, -ENODEV otherwise */
static int fcbcpld_detect(struct i2c_client *client,
                          struct i2c_board_info *info)
{
  /*
   * We don't currently do any detection of the FCBCPLD
   */
  strlcpy(info->type, "fcbcpld", I2C_NAME_SIZE);
  return 0;
}

static int fcbcpld_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
{
  int n_attrs = sizeof(fcbcpld_attr_table) / sizeof(fcbcpld_attr_table[0]);
  return i2c_dev_sysfs_data_init(client, &fcbcpld_data,
                                 fcbcpld_attr_table, n_attrs);
}

static int fcbcpld_remove(struct i2c_client *client)
{
  i2c_dev_sysfs_data_clean(client, &fcbcpld_data);
  return 0;
}

static struct i2c_driver fcbcpld_driver = {
  .class    = I2C_CLASS_HWMON,
  .driver = {
    .name = "fcbcpld",
  },
  .probe    = fcbcpld_probe,
  .remove   = fcbcpld_remove,
  .id_table = fcbcpld_id,
  .detect   = fcbcpld_detect,
};

static int __init fcbcpld_mod_init(void)
{
  return i2c_add_driver(&fcbcpld_driver);
}

static void __exit fcbcpld_mod_exit(void)
{
  i2c_del_driver(&fcbcpld_driver);
}

MODULE_AUTHOR("Siyu Li");
MODULE_DESCRIPTION("fcbcpld Driver");
MODULE_LICENSE("GPL");

module_init(fcbcpld_mod_init);
module_exit(fcbcpld_mod_exit);

1.

散热会提供一个fan table(openloop,不是pid有反馈的closeloop,),里面会详细描述风扇要怎么转
BMC会透过PWM讯号去设定风扇转速,风扇也能透过TACH讯号来回传目前的转速
在这里插入图片描述

PWM是透过平均电压来传递类比讯号,简单来说就是在一个cycle中,高电为占百分之多少,就表示他要传递的值是多少

例如在一个周期中,如果高电位占25%,低电位占75%,这样表示我们要传递的值是25%,风扇就会转25%

那如果今天我们都一直是high (高电位),这样风扇就会全转,因此在线路图的review过程中,都会注意PWM有没有pull high,避免在BMC更新过程或是死掉后,机器过热

那每个周期的时间是多少呢? PWM的传递频率会定义在风扇的spec中,每一颗风扇的接收频率有可能会不一样
在这里插入图片描述

TACH (Tachometer)

我们可以透过Tach来传递风扇的转速,有修过机械方面的课程就会知道,马达会有n个机械原点,传一圈的话会产生n个pulse,一圈会产生几个pulse也是定义在风扇的spec中

假如今天风扇转一圈会产生两个pulse,我们在一秒内收到1000个pulse,这样表示风扇一秒转了500圈,风扇转速是用rpm(一分钟转几圈)表示的,因此500*60 rpm就是我们要求的值

在传统控制(Classical Control )理论中,通常会用有没有feedback 来区分open loop control 和 closed loop control,前者通常就是我们常说的stepwise,后者就是工业控制中广泛使用的PID control

在这里插入图片描述

1.

该文档讲述OCP BMC中如何使用phosphor-pid-control模块实现风扇自动控速。该模块可以通过IPMI指令开启和关闭自动控速模式,也可以通过kill 进程来关闭自动控速逻辑,手动控速可以通过fan-speed-set.sh脚本控制,当进程退出或则BMC reboot 系统会自动调用fan-full-speed.sh设fan到指定转速。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# json源码
{
    "version": "R02",
    "sensors": [
        {
            "name": "fan0_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan0_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan0_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan0_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan0_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan1_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan1_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan1_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan2_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan2_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan2_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan3_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan3_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan3_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan3_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan3_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan4_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan4_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan4_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan4_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan4_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan5_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan5_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan5_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan5_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan5_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan6_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan6_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan6_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan6_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan6_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan7_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan7_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan7_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan7_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan7_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan8_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan8_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan8_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan8_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan8_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan9_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan9_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan9_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan9_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan9_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan10_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan10_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan10_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan10_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan10_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan11_f_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan11_f_speed",
            "writePath": "/xyz/openbmc_project/control/fanpwm/fan11_pwm",
            "min": 0,
            "max": 255
        },
        {
            "name": "fan11_r_speed",
            "type": "fan",
            "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan11_r_speed",
            "min": 0,
            "max": 255
        },
        {
            "name": "Inlet_temp",
            "type": "temp",
            "readPath": "/xyz/openbmc_project/sensors/temperature/Inlet_temp",
            "writePath": "",
            "min": 0,
            "max": 0,
            "ignoreDbusMinMax": true,
            "timeout": 0
        },
        {
            "name": "MB_PCH_TEMP",
            "type": "temp",
            "readPath": "/xyz/openbmc_project/sensors/temperature/MB_PCH_TEMP",
            "writePath": "",
            "min": 0,
            "max": 0,
            "ignoreDbusMinMax": true,
            "timeout": 0
        },
        {
            "name": "DTS_CPU1",
            "type": "temp",
            "readPath": "/xyz/openbmc_project/sensors/temperature/DTS_CPU1",
            "writePath": "",
            "min": 0,
            "max": 0,
            "ignoreDbusMinMax": true,
            "timeout": 0
        },
        {
            "name": "DTS_CPU2",
            "type": "temp",
            "readPath": "/xyz/openbmc_project/sensors/temperature/DTS_CPU2",
            "writePath": "",
            "min": 0,
            "max": 0,
            "ignoreDbusMinMax": true,
            "timeout": 0
        }
    ],
    "zones": [
        {
            "id": 1,
            "minThermalOutput": 0.0,
            "failsafePercent": 75.0,
            "pids": [
                {
                    "name": "fan_tachs",
                    "type": "fan",
                    "inputs": [
                        "fan0_f_speed",
                        "fan0_r_speed",
                        "fan1_f_speed",
                        "fan1_r_speed",
                        "fan2_f_speed",
                        "fan2_r_speed",
                        "fan3_f_speed",
                        "fan3_r_speed",
                        "fan4_f_speed",
                        "fan4_r_speed",
                        "fan5_f_speed",
                        "fan5_r_speed",
                        "fan6_f_speed",
                        "fan6_r_speed",
                        "fan7_f_speed",
                        "fan7_r_speed",
                        "fan8_f_speed",
                        "fan8_r_speed",
                        "fan9_f_speed",
                        "fan9_r_speed",
                        "fan10_f_speed",
                        "fan10_r_speed",
                        "fan11_f_speed",
                        "fan11_r_speed"
                    ],
                    "setpoint": 0.0,
                    "pid": {
                        "samplePeriod": 1.0,
                        "proportionalCoeff": 0.0,
                        "integralCoeff": 0.0,
                        "feedFwdOffsetCoeff": 0.0,
                        "feedFwdGainCoeff": 0.0044,
                        "integralLimit_min": 0.0,
                        "integralLimit_max": 0.0,
                        "outLim_min": 0.0,
                        "outLim_max": 100.0,
                        "slewNeg": 0.0,
                        "slewPos": 0.0
                    }
                },
                {
                    "name": "Inlet",
                    "type": "stepwise",
                    "inputs": [
                        "Inlet_temp"
                    ],
                    "setpoint": 10.0,
                    "pid": {
                        "samplePeriod": 1.0,
                        "positiveHysteresis": 1.0,
                        "negativeHysteresis": 3.0,
                        "isCeiling": false,
                        "reading": {
                            "0": 10.0,
                            "1": 15.0,
                            "2": 17.0,
                            "3": 18.0,
                            "4": 20.0,
                            "5": 21.0,
                            "6": 23.0,
                            "7": 25.0,
                            "8": 26.0,
                            "9": 28.0,
                            "10": 30.0,
                            "11": 31.0,
                            "12": 33.0,
                            "13": 35.0,
                            "14": 37.0,
                            "15": 40.0
                        },
                        "output": {
                            "0": 4560.0,
                            "1": 4560.0,
                            "2": 4560.0,
                            "3": 5700.0,
                            "4": 5700.0,
                            "5": 5700.0,
                            "6": 6840.0,
                            "7": 6840.0,
                            "8": 6840.0,
                            "9": 7980.0,
                            "10": 7980.0,
                            "11": 7980.0,
                            "12": 9120.0,
                            "13": 9120.0,
                            "14": 9120.0,
                            "15": 10260.0
                        }
                    }
                },
                {
                    "name": "MB_PCH_TEMP",
                    "type": "temp",
                    "inputs": [
                        "MB_PCH_TEMP"
                    ],
                    "setpoint": 70.0,
                    "pid": {
                        "samplePeriod": 1.0,
                        "proportionalCoeff": -714.0,
                        "integralCoeff": -9.0,
                        "feedFwdOffsetCoeff": 0.0,
                        "feedFwdGainCoeff": 0.0,
                        "integralLimit_min": 0.0,
                        "integralLimit_max": 0.0,
                        "outLim_min": 4560.0,
                        "outLim_max": 22800.0,
                        "slewNeg": 0.0,
                        "slewPos": 0.0,
                        "positiveHysteresis": 0.0,
                        "negativeHysteresis": 0.0
                    }
                },
                {
                    "name": "DTS_CPU1",
                    "type": "temp",
                    "inputs": [
                        "DTS_CPU1"
                    ],
                    "setpoint": 70.0,
                    "pid": {
                        "samplePeriod": 1.0,
                        "proportionalCoeff": -714.0,
                        "integralCoeff": -9.0,
                        "feedFwdOffsetCoeff": 0.0,
                        "feedFwdGainCoeff": 0.0,
                        "integralLimit_min": 0.0,
                        "integralLimit_max": 0.0,
                        "outLim_min": 4560.0,
                        "outLim_max": 22800.0,
                        "slewNeg": 0.0,
                        "slewPos": 0.0,
                        "positiveHysteresis": 0.0,
                        "negativeHysteresis": 0.0
                    }
                },
                {
                    "name": "DTS_CPU2",
                    "type": "temp",
                    "inputs": [
                        "DTS_CPU2"
                    ],
                    "setpoint": 70.0,
                    "pid": {
                        "samplePeriod": 1.0,
                        "proportionalCoeff": -714.0,
                        "integralCoeff": -9.0,
                        "feedFwdOffsetCoeff": 0.0,
                        "feedFwdGainCoeff": 0.0,
                        "integralLimit_min": 0.0,
                        "integralLimit_max": 0.0,
                        "outLim_min": 4560.0,
                        "outLim_max": 22800.0,
                        "slewNeg": 0.0,
                        "slewPos": 0.0,
                        "positiveHysteresis": 0.0,
                        "negativeHysteresis": 0.0
                    }
                }
            ]
        }
    ]
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农编程录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值