/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_SMBUS 1 #define CPLD_POLLING_PERIOD 1000 #define ENABLE_SIMULATE 0 #define ENABLE_AUTOFAN 1 #define CPLD2_ADDRESS 0x33 static struct i2c_client *client2; static u8 hasCPLD2 = 1; #if ENABLE_SIMULATE u8 sim_register[0x90]; #endif /* definition */ #define CPLD_INFO_OFFSET 0x00 #define CPLD_BIOSCS_OFFSET 0x04 #define CPLD_CTL_OFFSET 0x0C #define CPLD_LED_OFFSET 0x2E #define CPLD_INT_OFFSET 0x30 #define CPLD_INTMASK_OFFSET 0x31 #define CPLD_INT2_OFFSET 0x32 #define CPLD_INTMASK2_OFFSET 0x33 #define CPLD_PSU_OFFSET 0x40 #define CPLD_POWERSTATUS_OFFSET 0x41 #define CPLD_PWM_OFFSET 0x50 #define CPLD_RPM_OFFSET 0x55 #define CPLD_FANSTATUS_OFFSET 0x69 #define CPLD_FANLED_OFFSET 0x6B #define CPLD_RESETBUTTONSTATUS_OFFSET 0x75 #define CPLD_RSTCAUSE_OFFSET 0x76 #define CPLD_WATCHDOGCOUNTER_OFFSET 0x77 #define CPLD_WATCHDOGCONFIG_OFFSET 0x78 #define CPLD_WATCHDOGENABLE_OFFSET 0x79 #define CPLD_PANICCODE_OFFSET 0x7E #define FAN_NUM 5 #define PWM_MIN 30 #define PWM_DEFAULT 150 /* Each client has this additional data */ struct cpld_data { struct device *hwmon_dev; struct mutex update_lock; struct task_struct *cpld_thread; u8 diag; u8 model; u8 fan_direction; u8 operation_command; u8 stack_mode; }; /*-----------------------------------------------------------------------*/ static ssize_t cpld_i2c_read(struct i2c_client *client, u8 *buf, u8 offset, size_t count) { #if ENABLE_SIMULATE memcpy(buf,&sim_register[offset],count); return count; #else #if USE_SMBUS int i; for(i=0; iaddr; msg[0].buf = msgbuf; msg[0].len = 1; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = count; status = i2c_transfer(client->adapter, msg, 2); if(status == 2) status = count; return status; #endif #endif } static ssize_t cpld_i2c_write(struct i2c_client *client, char *buf, unsigned offset, size_t count) { #if ENABLE_SIMULATE memcpy(&sim_register[offset],buf,count); return count; #else #if USE_SMBUS int i; for(i=0; iaddr; msg.flags = 0; /* msg.buf is u8 and casts will mask the values */ msg.buf = writebuf; msg.buf[i++] = offset; memcpy(&msg.buf[i], buf, count); msg.len = i + count; status = i2c_transfer(client->adapter, &msg, 1); if (status == 1) status = count; return status; #endif #endif } /*-----------------------------------------------------------------------*/ /* sysfs attributes for hwmon */ static char* model_str[] = { "10GBaseT", //0 "SFP", //1 }; static char* fandirection_str[] = { "Rear-to-Front", //0 "Front-to-Rear", //1 }; static ssize_t show_info(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte[4] = {0,0,0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client, byte, CPLD_INFO_OFFSET, 4); mutex_unlock(&data->update_lock); sprintf (buf, "The CPLD release date is %02d/%02d/%d.\n", byte[2] & 0xf, (byte[3] & 0x1f), 2014+(byte[2] >> 4)); /* mm/dd/yyyy*/ sprintf (buf, "%sThe Model is %s %s\n", buf, model_str[(byte[0]>>5) & 0x01],fandirection_str[(byte[0]>>7) & 0x01]); sprintf (buf, "%sThe PCB version is %X\n", buf, byte[0]&0xf); sprintf (buf, "%sThe CPLD version is %d.%d\n", buf, byte[1]>>4, byte[1]&0xf); if(hasCPLD2) { mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, CPLD_INFO_OFFSET, 4); mutex_unlock(&data->update_lock); sprintf (buf, "%s\nThe CPLD2 release date is %02d/%02d/%d.\n", buf, byte[2] & 0xf, (byte[3] & 0x1f), 2014+(byte[2] >> 4)); /* mm/dd/yyyy*/ sprintf (buf, "%sThe CPLD2 version is %d.%d\n", buf, byte[1]>>4, byte[1]&0xf); } return strlen(buf); } static char* powerstatus_str[] = { "Failed", //0 "Good", //1 }; static ssize_t show_powerstatus(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte[2] = {0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, CPLD_POWERSTATUS_OFFSET, 2); mutex_unlock(&data->update_lock); sprintf (buf, "PGD_P5V: %s\n", powerstatus_str[(byte[0]>>7) & 0x01]); sprintf (buf, "%sPGD_P3V3_STBY: %s\n", buf, powerstatus_str[(byte[0]>>6) & 0x01]); //if(data->model==0) sprintf (buf, "%sPGD_P1V88: %s\n", buf, powerstatus_str[(byte[0]>>5) & 0x01]); sprintf (buf, "%sPGD_P1V8_A: %s\n", buf, powerstatus_str[(byte[0]>>4) & 0x01]); sprintf (buf, "%sPGD_P3V3_SYS: %s\n", buf, powerstatus_str[(byte[0]>>3) & 0x01]); sprintf (buf, "%sPGD_P3V3_A: %s\n", buf, powerstatus_str[(byte[0]>>2) & 0x01]); sprintf (buf, "%sPGD_P3V3_B: %s\n", buf, powerstatus_str[(byte[0]>>1) & 0x01]); sprintf (buf, "%sPGD_P1V2: %s\n", buf, powerstatus_str[(byte[0]>>0) & 0x01]); sprintf (buf, "%sPGD_P0V8_A: %s\n", buf,powerstatus_str[(byte[1]>>7) & 0x01]); sprintf (buf, "%sPGD_P0V89_ROV: %s\n", buf, powerstatus_str[(byte[1]>>6) & 0x01]); //if(data->model==0) sprintf (buf, "%sPGD_P1V0_A: %s\n", buf, powerstatus_str[(byte[1]>>5) & 0x01]); //if(data->model==0) sprintf (buf, "%sPGD_P1V0_B: %s\n", buf, powerstatus_str[(byte[1]>>4) & 0x01]); sprintf (buf, "%sSW_PWR_READY: %s\n", buf, powerstatus_str[(byte[1]>>3) & 0x01]); //sprintf (buf, "%sCORE_PWRGD_TO_CPLD: %s\n", buf, powerstatus_str[(byte[1]>>2) & 0x01]); //sprintf (buf, "%sCPU_STBY_PWROK: %s\n", buf, powerstatus_str[(byte[1]>>1) & 0x01]); sprintf (buf, "%sPGD_P1V8_A_STBY: %s\n", buf, powerstatus_str[(byte[1]>>0) & 0x01]); return strlen(buf); } static ssize_t show_diag(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); return sprintf (buf, "%d\n", data->diag); } static ssize_t set_diag(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 diag = simple_strtol(buf, NULL, 10); data->diag = diag?1:0; return count; } static char* stackmode_str[] = { "Non-Stack member", //0 "Stack Master", //1 "Stack Backup/Member", //2 "Stack Error", //3 }; static ssize_t show_stackmode(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); return sprintf (buf, "%d: %s\n", data->stack_mode,stackmode_str[data->stack_mode]); } static ssize_t set_stackmode(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 stackmode = simple_strtol(buf, NULL, 10); if(stackmode<4) data->stack_mode = stackmode; return count; } static char* resetbutton_str[] = { "No press", //0 "Reserved", //1 "Press and hold <5s", //2 "Press and hold >5s", //3 }; static ssize_t show_resetbuttonstatus(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_RESETBUTTONSTATUS_OFFSET, 1); mutex_unlock(&data->update_lock); byte &=0x03; return sprintf (buf, "0x%02X:%s\n", byte,resetbutton_str[byte]); } static char* resetcause_str[] = { "Power-On", //0 "Watchdog", //1 "SoftwareReboot", //2 "ResetButton", //3 "Panic", //4 "ThermalTrip" //5 }; static ssize_t show_resetcause(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_RSTCAUSE_OFFSET, 1); mutex_unlock(&data->update_lock); sprintf (buf, "0x%02X:", byte); if(byte==0) sprintf (buf, "%sNone",buf); if(byte&0x01) sprintf (buf, "%s%s ",buf,resetcause_str[0]); if(byte&0x02) sprintf (buf, "%s%s ",buf,resetcause_str[1]); if(byte&0x04) sprintf (buf, "%s%s ",buf,resetcause_str[2]); if(byte&0x08) sprintf (buf, "%s%s ",buf,resetcause_str[3]); if(byte&0x10) sprintf (buf, "%s%s ",buf,resetcause_str[4]); if(byte&0x20) sprintf (buf, "%s%s ",buf,resetcause_str[5]); return sprintf (buf, "%s\n", buf); } static ssize_t set_resetcause(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; mutex_lock(&data->update_lock); cpld_i2c_write(client, &byte, CPLD_RSTCAUSE_OFFSET, 1); mutex_unlock(&data->update_lock); return count; } static char* interrupt_str[] = { "PCIE_INTR_L", //0 "EXT_USB_OC_N", //1 "PS2_ALERT_N", //2 "PS1_ALERT_N", //3 "PLD_SEN5_ALERT_N", //4 "PLD_SEN4_ALERT_N", //5 "PLD_SEN3_ALERT_N", //6 "UCD90160_TEMP_INT_N", //7 "RSTBTN_INT_N", //8 "WDT_IRQ_N", //9 "RSTBTN_5s_INT_N", //10 "Reserved" //11 }; static ssize_t show_interrupt(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte[4] = {0,0,0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client, byte, CPLD_INT_OFFSET, 4); mutex_unlock(&data->update_lock); sprintf (buf, "0x%02X 0x%02X:", byte[0],byte[2]); if(byte[0]==0xff && byte[2]==0x07) sprintf (buf, "%sNone",buf); if(!(byte[0]&0x01)) sprintf (buf, "%s%s ",buf,interrupt_str[0]); if(!(byte[0]&0x02)) sprintf (buf, "%s%s ",buf,interrupt_str[1]); //if(!(byte[0]&0x04)) sprintf (buf, "%s%s ",buf,interrupt_str[2]); //if(!(byte[0]&0x08)) sprintf (buf, "%s%s ",buf,interrupt_str[3]); if(!(byte[0]&0x10)) sprintf (buf, "%s%s ",buf,interrupt_str[4]); if(!(byte[0]&0x20)) sprintf (buf, "%s%s ",buf,interrupt_str[5]); if(!(byte[0]&0x40)) sprintf (buf, "%s%s ",buf,interrupt_str[6]); if(!(byte[0]&0x80)) sprintf (buf, "%s%s ",buf,interrupt_str[7]); if(!(byte[2]&0x01)) sprintf (buf, "%s%s%s ",buf,interrupt_str[8] ,(byte[3]&0x01)?"(Blocked)":""); if(!(byte[2]&0x02)) sprintf (buf, "%s%s%s ",buf,interrupt_str[9] ,(byte[3]&0x02)?"(Blocked)":""); if(!(byte[2]&0x04)) sprintf (buf, "%s%s%s ",buf,interrupt_str[10],(byte[3]&0x04)?"(Blocked)":""); return sprintf (buf, "%s\n", buf); } static ssize_t show_operationcommand(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); return sprintf (buf, "%d\n", data->operation_command); } static ssize_t set_operationcommand(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 operationcommand = simple_strtol(buf, NULL, 10); data->operation_command = operationcommand?1:0; return count; } static char* bios_str[] = { "BIOS1", //0 "BIOS2", //1 }; static ssize_t show_bios_cs(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_BIOSCS_OFFSET, 1); mutex_unlock(&data->update_lock); byte &= 0x01; return sprintf (buf, "%d:%s\n", byte,bios_str[byte]); } static ssize_t set_bios_cs(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; u8 temp = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_BIOSCS_OFFSET, 1); if(temp) byte |= 0x01; else byte &= ~(0x01); cpld_i2c_write(client, &byte, CPLD_BIOSCS_OFFSET, 1); mutex_unlock(&data->update_lock); return count; } static char* led_str[] = { "OFF", //000 "ON", //001 "1 Hz", //010 "2 Hz", //011 }; static ssize_t show_led(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; int shift = attr->index; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_LED_OFFSET, 1); mutex_unlock(&data->update_lock); byte = (byte >> shift) & 0x3; return sprintf (buf, "%d: %s\n", byte, led_str[byte]); } static ssize_t set_led(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 temp = simple_strtol(buf, NULL, 16); u8 byte = 0; int shift = attr->index; temp &= 0x3; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_LED_OFFSET, 1); byte &= ~(0x3<update_lock); return count; } static char* psu_str[] = { "unpowered", //00 "normal", //01 "not installed", //10 "not installed", //11 }; static ssize_t show_psu(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; int shift = (attr->index == 0)?0:4; mutex_lock(&data->update_lock); cpld_i2c_read(client2, &byte, CPLD_PSU_OFFSET, 1); mutex_unlock(&data->update_lock); byte = (byte >> shift) & 0x3; return sprintf (buf, "%d:%s\n", byte, psu_str[byte]); } static ssize_t show_pwm(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; u8 offset = attr->index + CPLD_PWM_OFFSET; mutex_lock(&data->update_lock); cpld_i2c_read(client2, &byte, offset, 1); mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", byte); } static ssize_t set_pwm(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 offset = attr->index + CPLD_PWM_OFFSET; u8 byte = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); cpld_i2c_write(client2, &byte, offset, 1); mutex_unlock(&data->update_lock); return count; } static ssize_t show_rpm(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 offset = attr->index*2 + CPLD_RPM_OFFSET; u8 byte[2] = {0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, offset, 2); mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", (byte[0]<<8 | byte[1])); } static char* fantype_str[] = { "Normal Type", //00 "REVERSAL Type", //01 "UNPLUGGED", //10 "UNPLUGGED", //11 }; static ssize_t show_fantype(struct device *dev, struct device_attribute *da, char *buf) { int status; struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 offset = CPLD_FANSTATUS_OFFSET; u8 byte[2] = {0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, offset, 2); mutex_unlock(&data->update_lock); status = (((byte[0] >> attr->index) & 0x01)) | (((byte[1] >> attr->index) & 0x01)<<1); return sprintf(buf, "%d:%s\n",status,fantype_str[status]); } static char* fanled_str[] = { "None", //00 "Green", //01 "Red", //10 "Both", //11 }; static ssize_t show_fanled(struct device *dev, struct device_attribute *da, char *buf) { int status; struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte[2] = {0,0}; mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, CPLD_FANLED_OFFSET, 2); mutex_unlock(&data->update_lock); status = (((byte[0] >> attr->index) & 0x01)) | (((byte[1] >> attr->index) & 0x01)<<1); return sprintf(buf, "%d:%s\n",status,fanled_str[status]); } static ssize_t set_fanled(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte[2] = {0,0}; u8 temp = simple_strtol(buf, NULL, 16); int shift = attr->index; temp &= 0x3; mutex_lock(&data->update_lock); cpld_i2c_read(client2, byte, CPLD_FANLED_OFFSET, 2); byte[0] &= ~(1<> 1) & 0x01)<update_lock); return count; } static ssize_t set_watchdog_feed(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_WATCHDOGENABLE_OFFSET, 1); byte |= 0x02; cpld_i2c_write(client, &byte, CPLD_WATCHDOGENABLE_OFFSET, 1); mutex_unlock(&data->update_lock); return count; } static ssize_t set_watchdog_enable(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0x03; mutex_lock(&data->update_lock); cpld_i2c_write(client, &byte, CPLD_WATCHDOGENABLE_OFFSET, 1); mutex_unlock(&data->update_lock); return count; } static ssize_t show_watchdog_enable(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_WATCHDOGENABLE_OFFSET, 1); mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n",(byte&0x01)); } static ssize_t set_watchdog_config(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = simple_strtol(buf, NULL, 10); if (byte<6) byte=6; mutex_lock(&data->update_lock); cpld_i2c_write(client, &byte, CPLD_WATCHDOGCONFIG_OFFSET, 1); mutex_unlock(&data->update_lock); return count; } static ssize_t show_watchdog_config(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_WATCHDOGCONFIG_OFFSET, 1); mutex_unlock(&data->update_lock); return sprintf(buf, "%d seconds\n",byte); } static ssize_t show_watchdog_counter(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte=0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_WATCHDOGCOUNTER_OFFSET, 1); mutex_unlock(&data->update_lock); return sprintf(buf, "%d seconds\n",byte); } static ssize_t show_paniccode(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 byte = 0; mutex_lock(&data->update_lock); cpld_i2c_read(client, &byte, CPLD_PANICCODE_OFFSET, 1); mutex_unlock(&data->update_lock); return sprintf (buf, "0x%02X\n", byte); } #if ENABLE_SIMULATE static ssize_t show_simdump(struct device *dev, struct device_attribute *da, char *buf) { u8 i, j; sprintf(buf,"usage: echo 0xAABB > sim_buffer (AA is address, BB is value)\n\n"); sprintf(buf,"%s 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n",buf); sprintf(buf,"%s======================================================\n",buf); sprintf(buf,"%s0000: ",buf); for (j = 0, i = 0; j < sizeof(sim_register); j++, i++) { sprintf(buf,"%s%02x ", buf, (int)sim_register[i]); if ((i & 0x0F) == 0x0F) sprintf(buf,"%s\n%04x: ", buf, (i+1)); } return sprintf(buf,"%s\n",buf); } static ssize_t set_simbuffer(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { u16 byte = simple_strtol(buf, NULL, 16); u8 address = (byte >> 8); u8 value = (byte & 0xff); sim_register[address]=value; return count; } #endif static SENSOR_DEVICE_ATTR(info, S_IRUGO, show_info, 0, 0); static SENSOR_DEVICE_ATTR(diag, S_IWUSR|S_IRUGO, show_diag, set_diag, 0); static SENSOR_DEVICE_ATTR(interrupt, S_IRUGO, show_interrupt, 0, 0); static SENSOR_DEVICE_ATTR(stacking_led, S_IWUSR|S_IRUGO, show_led, set_led, 0); static SENSOR_DEVICE_ATTR(fan_led, S_IWUSR|S_IRUGO, show_led, set_led, 2); static SENSOR_DEVICE_ATTR(power_led, S_IWUSR|S_IRUGO, show_led, set_led, 4); static SENSOR_DEVICE_ATTR(service_led, S_IWUSR|S_IRUGO, show_led, set_led, 6); static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR|S_IRUGO, show_pwm, set_pwm, 0); static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR|S_IRUGO, show_pwm, set_pwm, 1); static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR|S_IRUGO, show_pwm, set_pwm, 2); static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR|S_IRUGO, show_pwm, set_pwm, 3); static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR|S_IRUGO, show_pwm, set_pwm, 4); static SENSOR_DEVICE_ATTR(fanmodule1_type, S_IRUGO, show_fantype, 0, 0); static SENSOR_DEVICE_ATTR(fanmodule2_type, S_IRUGO, show_fantype, 0, 1); static SENSOR_DEVICE_ATTR(fanmodule3_type, S_IRUGO, show_fantype, 0, 2); static SENSOR_DEVICE_ATTR(fanmodule4_type, S_IRUGO, show_fantype, 0, 3); static SENSOR_DEVICE_ATTR(fanmodule5_type, S_IRUGO, show_fantype, 0, 4); static SENSOR_DEVICE_ATTR(fanmodule1_led, S_IWUSR|S_IRUGO, show_fanled, set_fanled, 0); static SENSOR_DEVICE_ATTR(fanmodule2_led, S_IWUSR|S_IRUGO, show_fanled, set_fanled, 1); static SENSOR_DEVICE_ATTR(fanmodule3_led, S_IWUSR|S_IRUGO, show_fanled, set_fanled, 2); static SENSOR_DEVICE_ATTR(fanmodule4_led, S_IWUSR|S_IRUGO, show_fanled, set_fanled, 3); static SENSOR_DEVICE_ATTR(fanmodule5_led, S_IWUSR|S_IRUGO, show_fanled, set_fanled, 4); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, 0, 0); static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_rpm, 0, 1); static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_rpm, 0, 2); static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_rpm, 0, 3); static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_rpm, 0, 4); static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_rpm, 0, 5); static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_rpm, 0, 6); static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_rpm, 0, 7); static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_rpm, 0, 8); static SENSOR_DEVICE_ATTR(fan10_input,S_IRUGO, show_rpm, 0, 9); static SENSOR_DEVICE_ATTR(psu1, S_IRUGO, show_psu, 0, 0); static SENSOR_DEVICE_ATTR(psu2, S_IRUGO, show_psu, 0, 1); static SENSOR_DEVICE_ATTR(power_status, S_IRUGO, show_powerstatus, 0, 0); static SENSOR_DEVICE_ATTR(reset_cause, S_IWUSR|S_IRUGO, show_resetcause, set_resetcause, 0); static SENSOR_DEVICE_ATTR(resetbutton_status, S_IRUGO, show_resetbuttonstatus, 0, 0); static SENSOR_DEVICE_ATTR(panic_code, S_IRUGO, show_paniccode, 0, 0); static SENSOR_DEVICE_ATTR(watchdog_feed, S_IWUSR, 0, set_watchdog_feed, 0); static SENSOR_DEVICE_ATTR(watchdog_enable, S_IWUSR|S_IRUGO, show_watchdog_enable, set_watchdog_enable, 0); static SENSOR_DEVICE_ATTR(watchdog_config, S_IWUSR|S_IRUGO, show_watchdog_config, set_watchdog_config, 0); static SENSOR_DEVICE_ATTR(watchdog_counter, S_IRUGO, show_watchdog_counter, 0, 0); static SENSOR_DEVICE_ATTR(stack_mode, S_IWUSR|S_IRUGO, show_stackmode, set_stackmode, 0); static SENSOR_DEVICE_ATTR(operation_command, S_IWUSR|S_IRUGO, show_operationcommand, set_operationcommand, 0); #if ENABLE_SIMULATE static SENSOR_DEVICE_ATTR(sim_buffer, S_IWUSR|S_IRUGO, show_simdump, set_simbuffer, 0); #endif static SENSOR_DEVICE_ATTR(bios_cs, S_IWUSR|S_IRUGO, show_bios_cs, set_bios_cs, 0); static struct attribute *cpld_attributes[] = { &sensor_dev_attr_info.dev_attr.attr, &sensor_dev_attr_diag.dev_attr.attr, &sensor_dev_attr_stacking_led.dev_attr.attr, &sensor_dev_attr_fan_led.dev_attr.attr, &sensor_dev_attr_power_led.dev_attr.attr, &sensor_dev_attr_service_led.dev_attr.attr, &sensor_dev_attr_interrupt.dev_attr.attr, &sensor_dev_attr_psu1.dev_attr.attr, &sensor_dev_attr_psu2.dev_attr.attr, &sensor_dev_attr_power_status.dev_attr.attr, &sensor_dev_attr_reset_cause.dev_attr.attr, &sensor_dev_attr_resetbutton_status.dev_attr.attr, &sensor_dev_attr_panic_code.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm4.dev_attr.attr, &sensor_dev_attr_pwm5.dev_attr.attr, &sensor_dev_attr_fanmodule1_type.dev_attr.attr, &sensor_dev_attr_fanmodule2_type.dev_attr.attr, &sensor_dev_attr_fanmodule3_type.dev_attr.attr, &sensor_dev_attr_fanmodule4_type.dev_attr.attr, &sensor_dev_attr_fanmodule5_type.dev_attr.attr, &sensor_dev_attr_fanmodule1_led.dev_attr.attr, &sensor_dev_attr_fanmodule2_led.dev_attr.attr, &sensor_dev_attr_fanmodule3_led.dev_attr.attr, &sensor_dev_attr_fanmodule4_led.dev_attr.attr, &sensor_dev_attr_fanmodule5_led.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan4_input.dev_attr.attr, &sensor_dev_attr_fan5_input.dev_attr.attr, &sensor_dev_attr_fan6_input.dev_attr.attr, &sensor_dev_attr_fan7_input.dev_attr.attr, &sensor_dev_attr_fan8_input.dev_attr.attr, &sensor_dev_attr_fan9_input.dev_attr.attr, &sensor_dev_attr_fan10_input.dev_attr.attr, &sensor_dev_attr_watchdog_feed.dev_attr.attr, &sensor_dev_attr_watchdog_enable.dev_attr.attr, &sensor_dev_attr_watchdog_config.dev_attr.attr, &sensor_dev_attr_watchdog_counter.dev_attr.attr, &sensor_dev_attr_stack_mode.dev_attr.attr, &sensor_dev_attr_operation_command.dev_attr.attr, &sensor_dev_attr_bios_cs.dev_attr.attr, #if ENABLE_SIMULATE &sensor_dev_attr_sim_buffer.dev_attr.attr, #endif NULL }; struct i2c_client *client_notifier; static const struct attribute_group cpld_group = { .attrs = cpld_attributes, }; static int log_reboot_to_cpld (struct notifier_block *self, unsigned long event, void *ptr) { u8 byte=0; if (event == SYS_DOWN || event == SYS_HALT) { cpld_i2c_read(client_notifier, &byte, CPLD_RSTCAUSE_OFFSET, 1); byte |= 0x04; cpld_i2c_write(client_notifier, &byte, CPLD_RSTCAUSE_OFFSET, 1); } return NOTIFY_DONE; } static int log_panic_to_cpld(struct notifier_block *self, unsigned long event, void *ptr) { #if 1 // Can't use SMBus if smp is stopped (need patch the panic.c) if (client_notifier != NULL) { u8 byte=0; cpld_i2c_read(client_notifier, &byte, CPLD_RSTCAUSE_OFFSET, 1); byte |= 0x10; cpld_i2c_write(client_notifier, &byte, CPLD_RSTCAUSE_OFFSET, 1); } #endif return NOTIFY_DONE; } static struct notifier_block reboot_notifier = { .notifier_call = log_reboot_to_cpld, }; static struct notifier_block panic_notifier = { .notifier_call = log_panic_to_cpld, }; #if ENABLE_AUTOFAN #define SWITCH_ADDRESS 3-004e #define ENV_ADDRESS 3-004a #define CPU_ADDRESS 3-0048 #define PSU1_ADDRESS 2-0058 #define PSU2_ADDRESS 2-0059 #define PSU1_ADDRESS_DVT 2-005a #define PSU2_ADDRESS_DVT 2-005b #define _STR(s) #s #define __STR(s) _STR(s) #define __File_input(__file) __STR(/sys/bus/i2c/devices/__file/hwmon/hwmon%d/temp1_input) #define __File_max(__file) __STR(/sys/bus/i2c/devices/__file/hwmon/hwmon%d/temp1_max) #define __File_max_hyst(__file) __STR(/sys/bus/i2c/devices/__file/hwmon/hwmon%d/temp1_max_hyst) #define __File_pwm(__file) __STR(/sys/bus/i2c/devices/__file/hwmon/hwmon%d/pwm1) #define SWITCH_TEMPERATURE __File_input(SWITCH_ADDRESS) #define ENV_TEMPERATURE __File_input(ENV_ADDRESS) #define CPU_TEMPERATURE __File_input(CPU_ADDRESS) #define SWITCH_MAX __File_max(SWITCH_ADDRESS) #define ENV_MAX __File_max(ENV_ADDRESS) #define CPU_MAX __File_max(CPU_ADDRESS) #define SWITCH_MAX_HYST __File_max_hyst(SWITCH_ADDRESS) #define ENV_MAX_HYST __File_max_hyst(ENV_ADDRESS) #define CPU_MAX_HYST __File_max_hyst(CPU_ADDRESS) #define PSU1_PWM __File_pwm(PSU1_ADDRESS) #define PSU2_PWM __File_pwm(PSU2_ADDRESS) #define PSU1_PWM_DVT __File_pwm(PSU1_ADDRESS_DVT) #define PSU2_PWM_DVT __File_pwm(PSU2_ADDRESS_DVT) #define n_entries 9 #define temp_hysteresis 2 #define MAX_HWMON 9 //[Model:10G/SFP][FanDirection:R2F/F2R][Type:CPU/ASIC/ENV] u8 Thermaltrip[2][2][3] ={{{64,68,64},{64,68,64}},{{64,68,64},{64,68,64}}}; u8 FanTable[2][2][3][n_entries][2]={ {//10GBastT {//Rear-to-Front {//CPU { 45, 255 }, \ { 40, 201 }, \ { 38, 170 }, \ { 35, 130 }, \ { 31, 105 }, \ { 27, 85 }, \ { 24, 80 }, \ { 20, 70 }, \ { 18, 65 } } ,{//ASIC { 52, 255 }, \ { 47, 201 }, \ { 44, 170 }, \ { 42, 130 }, \ { 39, 105 }, \ { 36, 85 }, \ { 33, 80 }, \ { 30, 70 }, \ { 28, 65 } } ,{//ENV { 47, 255 }, \ { 43, 201 }, \ { 41, 170 }, \ { 39, 130 }, \ { 36, 105 }, \ { 32, 85 }, \ { 30, 80 }, \ { 28, 70 }, \ { 19, 65 } } }, {//Front-to-Rear {//CPU { 45, 255 }, \ { 40, 201 }, \ { 38, 170 }, \ { 35, 130 }, \ { 31, 105 }, \ { 27, 85 }, \ { 24, 80 }, \ { 20, 70 }, \ { 18, 65 } } ,{//ASIC { 52, 255 }, \ { 47, 201 }, \ { 44, 170 }, \ { 42, 130 }, \ { 39, 105 }, \ { 36, 85 }, \ { 33, 80 }, \ { 30, 70 }, \ { 28, 65 } } ,{//ENV { 47, 255 }, \ { 43, 201 }, \ { 41, 170 }, \ { 39, 130 }, \ { 36, 105 }, \ { 32, 85 }, \ { 30, 80 }, \ { 28, 70 }, \ { 19, 65 } } }, }, {//SFP28 {//Rear-to-Front {//CPU { 45, 255 }, \ { 40, 201 }, \ { 38, 170 }, \ { 35, 130 }, \ { 31, 105 }, \ { 27, 85 }, \ { 24, 80 }, \ { 20, 70 }, \ { 18, 65 } } ,{//ASIC { 52, 255 }, \ { 47, 201 }, \ { 44, 170 }, \ { 42, 130 }, \ { 39, 105 }, \ { 36, 85 }, \ { 33, 80 }, \ { 30, 70 }, \ { 28, 65 } } ,{//ENV { 47, 255 }, \ { 43, 201 }, \ { 41, 170 }, \ { 39, 130 }, \ { 36, 105 }, \ { 32, 85 }, \ { 30, 80 }, \ { 28, 70 }, \ { 19, 65 } } }, {//Front-to-Rear {//CPU { 45, 255 }, \ { 40, 201 }, \ { 38, 170 }, \ { 35, 130 }, \ { 31, 105 }, \ { 27, 85 }, \ { 24, 80 }, \ { 20, 70 }, \ { 18, 65 } } ,{//ASIC { 52, 255 }, \ { 47, 201 }, \ { 44, 170 }, \ { 42, 130 }, \ { 39, 105 }, \ { 36, 85 }, \ { 33, 80 }, \ { 30, 70 }, \ { 28, 65 } } ,{//ENV { 47, 255 }, \ { 43, 201 }, \ { 41, 170 }, \ { 39, 130 }, \ { 36, 105 }, \ { 32, 85 }, \ { 30, 80 }, \ { 28, 70 }, \ { 19, 65 } } } } }; //type 0:CPU,1:ASIC,2:ENV static u8 find_duty(u8 model, u8 fan_direction, u8 type, u8 temp) { static u8 fantable_index[3]={n_entries-1, n_entries-1, n_entries-1}; while(1) { if(fantable_index[type] >= 1 && temp > FanTable[model][fan_direction][type][fantable_index[type]-1][0]) fantable_index[type]--; else if(fantable_index[type] < (n_entries-1) && temp < (FanTable[model][fan_direction][type][fantable_index[type]][0]-temp_hysteresis)) fantable_index[type]++; else break; } return FanTable[model][fan_direction][type][fantable_index[type]][1]; } static u32 getvalue(char *path) { static struct file *f; mm_segment_t old_fs; u16 temp = 0; char temp_str[]={0,0,0,0,0,0,0,0,0}; loff_t pos = 0; f = filp_open(path,O_RDONLY,0644); if(IS_ERR(f)) return -1; old_fs = get_fs(); set_fs(KERNEL_DS); vfs_read(f, temp_str,8,&pos); temp = simple_strtoul(temp_str,NULL,10); filp_close(f,NULL); set_fs(old_fs); if(temp<0) temp=0; return temp; } static u8 setvalue(char *path, u32 value) { static struct file *f; mm_segment_t old_fs; char temp_str[]={0,0,0,0,0,0,0,0,0}; u8 len=0; loff_t pos = 0; f = filp_open(path,O_WRONLY,0644); if(IS_ERR(f)) return -1; len = sprintf(temp_str,"%d",value); old_fs = get_fs(); set_fs(KERNEL_DS); vfs_write(f, temp_str, len, &pos); filp_close(f,NULL); set_fs(old_fs); return 0; } static u8 probe_gettemp(char *path) { u8 temp_path[64],i; u32 value; for(i=0;imodel,data->fan_direction,0,temp_cpu); if(temp_switch!=0xff) duty_switch=find_duty(data->model,data->fan_direction,1,temp_switch); if(temp_env!=0xff) duty_env=find_duty(data->model,data->fan_direction,2,temp_env); if(temp_cpu==0xff && temp_switch==0xff && temp_env==0xff) { duty=PWM_DEFAULT; } else { duty=(duty>duty_cpu)?duty:duty_cpu; duty=(duty>duty_switch)?duty:duty_switch; duty=(duty>duty_env)?duty:duty_env; } memset(fanpwm,duty,FAN_NUM); for(i=0;i 200) psu_duty = 80; else if (duty <= 200 && duty > 150) if(data->fan_direction==0) psu_duty = 70; else psu_duty = 65; else if (duty <= 150 && duty > 100) if(data->fan_direction==0) psu_duty = 60; else psu_duty = 55; else if(data->fan_direction==0) psu_duty = 50; else psu_duty = 35; probe_setvalue(PSU1_PWM, psu_duty); probe_setvalue(PSU2_PWM, psu_duty); probe_setvalue(PSU1_PWM_DVT, psu_duty); probe_setvalue(PSU2_PWM_DVT, psu_duty); mutex_lock(&data->update_lock); cpld_i2c_write(client2,fanpwm,CPLD_PWM_OFFSET,FAN_NUM); mutex_unlock(&data->update_lock); } #endif /*-----------------------------------------------------------------------*/ static int cpld_polling(void *p) { struct i2c_client *client = p; struct cpld_data *data = i2c_get_clientdata(client); u8 fanrpm[22],fanled[2],frontled,i; u8 fandir=0,fanpresent=0,fanfront,fanrear,fanerror; u8 fanfail,fanturnoff,psufail,lastStack,retry; u8 psustatus=0; u8 initial_thermaltrip[3] = {0,0,0}; while (!kthread_should_stop()) { //initial tmp75's thermaltrip value if(initial_thermaltrip[0]==0) { if((probe_setvalue(CPU_MAX, Thermaltrip[data->model][data->fan_direction][0]*1000)!=0xff) && (probe_setvalue(CPU_MAX_HYST, (Thermaltrip[data->model][data->fan_direction][0]-temp_hysteresis)*1000)!=0xff)) initial_thermaltrip[0]=1; } if(initial_thermaltrip[1]==0) { if((probe_setvalue(SWITCH_MAX, Thermaltrip[data->model][data->fan_direction][1]*1000)!=0xff) && (probe_setvalue(SWITCH_MAX_HYST, (Thermaltrip[data->model][data->fan_direction][1]-temp_hysteresis)*1000)!=0xff)) initial_thermaltrip[1]=1; } if(initial_thermaltrip[2]==0) { if((probe_setvalue(ENV_MAX, Thermaltrip[data->model][data->fan_direction][2]*1000)!=0xff) && (probe_setvalue(ENV_MAX_HYST, (Thermaltrip[data->model][data->fan_direction][2]-temp_hysteresis)*1000)!=0xff)) initial_thermaltrip[2]=1; } //LED control if (data->diag==0 && i2c_smbus_read_byte_data(client, 0)>=0) { for(retry=0;retry<2;retry++) { fanfail=0; fanled[0]=0; //clean green led fanled[1]=0; //clean red led fanfront=0; fanrear=0; fanerror=0; fanturnoff=0; //------ Fan Check ------- cpld_i2c_read(client2, fanrpm, CPLD_RPM_OFFSET, 22); fandir=fanrpm[20]; fanpresent=fanrpm[21]; //Count the fan's direction for (i=0;i 23500) || (rpm2 < 3000 || rpm2 > 23500)) fanerror++; else if(fandir&(1<>i)&0x01)!=data->fan_direction) fanled[0] |= (1<stack_mode==0) frontled&=~(0x3); //0: Non-Stack member => off if(data->stack_mode==1) frontled|=0x2; //1: Stack Master => Flash green if(data->stack_mode==2) frontled|=0x1; //2: Stack Backup/Member => Steady green if(data->stack_mode==3) frontled|=lastStack;//3: Stack Error => last status lastStack=frontled; if(fanpresent!=0x00) //Fan LED in bit2 bit3 { frontled|=(0x02<<2); //Some fan unpresent => 0x02 } else { if(fanturnoff||fanfail) //some fan no speed => 0x03 frontled|=(0x03<<2); //some fan dir different => 0x03 else frontled|=(0x01<<2); //Normal => 0x01 } if(psustatus&0x22) //POW LED in bit4 bit5 frontled|=(0x02<<4); //not all psu plug-in => 0x02 else if (psustatus!=0x11) frontled|=(0x03<<4); //some power is not ok => 0x03 else frontled|=(0x01<<4); //Normal => 0x01 if(data->operation_command==1) //Service LED in bit6 bit7 frontled|=(0x01<<6); //Steady if operation else if(fanfail>0||psufail>0||data->stack_mode==3) frontled|=(0x02<<6); //Flash if any error else if(data->stack_mode==2) frontled|=(0x01<<6); //Steady if mode in Stack Backup/Member else frontled&=~(0x3<<6); //Off if mode in Non-Stack member or Stack Master with no operation and error cpld_i2c_write(client, &frontled, CPLD_LED_OFFSET, 1); } set_current_state(TASK_INTERRUPTIBLE); if(kthread_should_stop()) break; schedule_timeout(msecs_to_jiffies(CPLD_POLLING_PERIOD)); } return 0; } /*-----------------------------------------------------------------------*/ /* device probe and removal */ static int cpld_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct cpld_data *data; int status; u8 byte[5]={PWM_DEFAULT,PWM_DEFAULT,PWM_DEFAULT,PWM_DEFAULT,PWM_DEFAULT}; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); if (!data) return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); /* Register sysfs hooks */ status = sysfs_create_group(&client->dev.kobj, &cpld_group); if (status) goto exit_free; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { status = PTR_ERR(data->hwmon_dev); goto exit_remove; } //Check CPLD2 exist or not client2 = i2c_new_dummy(client->adapter, CPLD2_ADDRESS); if(!client2) { hasCPLD2 = 0; client2 = client; } else { status = i2c_smbus_read_byte_data(client2, CPLD_INFO_OFFSET); if(status<0) { i2c_unregister_device(client2); i2c_set_clientdata(client2, NULL); hasCPLD2 = 0; client2 = client; } } dev_info(&client->dev, "%s: sensor '%s'\n", dev_name(data->hwmon_dev), client->name); //initial PWM to 60% cpld_i2c_write(client2, byte, CPLD_PWM_OFFSET, 5); //Handle LED control by the driver byte[0]=0x01; cpld_i2c_write(client, byte, CPLD_CTL_OFFSET, 1); cpld_i2c_write(client2, byte, CPLD_CTL_OFFSET, 1); //Get Model type cpld_i2c_read(client, byte, CPLD_INFO_OFFSET, 1); data->model = (byte[0]>>5) & 0x01; data->fan_direction = (byte[0]>>7) & 0x01; //kernel panic notifier atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); //soft reboot notifier register_reboot_notifier(&reboot_notifier); client_notifier=client; data->cpld_thread = kthread_run(cpld_polling,client,"%s",dev_name(data->hwmon_dev)); return 0; exit_remove: sysfs_remove_group(&client->dev.kobj, &cpld_group); exit_free: i2c_set_clientdata(client, NULL); kfree(data); return status; } static int cpld_remove(struct i2c_client *client) { struct cpld_data *data = i2c_get_clientdata(client); //Return LED control to the CPLD u8 byte=0x00; cpld_i2c_write(client, &byte, CPLD_CTL_OFFSET, 1); cpld_i2c_write(client2, &byte, CPLD_CTL_OFFSET, 1); //unregister soft reboot notifier unregister_reboot_notifier(&reboot_notifier); //unregister kernel panic notifier atomic_notifier_chain_unregister(&panic_notifier_list, &panic_notifier); //stop cpld thread kthread_stop(data->cpld_thread); sysfs_remove_group(&client->dev.kobj, &cpld_group); hwmon_device_unregister(data->hwmon_dev); i2c_set_clientdata(client, NULL); if(hasCPLD2) { i2c_unregister_device(client2); i2c_set_clientdata(client2, NULL); } kfree(data); return 0; } static const struct i2c_device_id cpld_ids[] = { { "inv_cpld" , 0, }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, cpld_ids); static struct i2c_driver cpld_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "inv_cpld", }, .probe = cpld_probe, .remove = cpld_remove, .id_table = cpld_ids, }; /*-----------------------------------------------------------------------*/ /* module glue */ static int __init inv_cpld_init(void) { return i2c_add_driver(&cpld_driver); } static void __exit inv_cpld_exit(void) { i2c_del_driver(&cpld_driver); } MODULE_AUTHOR("jack.ting "); MODULE_DESCRIPTION("cpld driver"); MODULE_LICENSE("GPL"); module_init(inv_cpld_init); module_exit(inv_cpld_exit);