sonic-buildimage/platform/broadcom/sonic-platform-modules-inventec/d7032q28b/modules/inv_pthread.c

1306 lines
36 KiB
C
Raw Normal View History

/*****************************
Redwood platform
******************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/uaccess.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#define SHOW_ATTR_WARNING ("N/A")
#define SHOW_ATTR_NOTPRINT ("Not Available")
#define SHOW_ATTR_NOTSUPPORT ("Not Support")
#define INV_HWMID_MAX (10)
#define INV_HWMID_INIT (-1)
/*access userspace data to kernel space*/
#define ACC_R (0)
#define ACC_W (1)
#define TINY_BUF_SIZE (8)
#define MAX_PATH_SIZE (64)
#define MIN_ACC_SIZE (32)
#define MAX_ACC_SIZE (256)
/*
* LED definitions
*/
#define STATUS_LED_MODE_AUTO 0
#define STATUS_LED_MODE_DIAG 1
#define STATUS_LED_MODE_MANU 2
#define STATUS_LED_GRN0 10 // 0 - 000: off
#define STATUS_LED_GRN1 11 // 1 - 001: 0.5hz
#define STATUS_LED_GRN2 12 // 2 - 010: 1 hz
#define STATUS_LED_GRN3 13 // 3 - 011: 2 hz
#define STATUS_LED_GRN7 17 // 7 - 111: on
#define STATUS_LED_RED0 20 // 0 - 000: off
#define STATUS_LED_RED1 21 // 1 - 001: 0.5hz
#define STATUS_LED_RED2 22 // 2 - 010: 1 hz
#define STATUS_LED_RED3 23 // 3 - 011: 2 hz
#define STATUS_LED_RED7 27 // 7 - 111: on
#define STATUS_LED_INVALID 0 // Invalid
ssize_t status_led_change(const char *path1, const char *tmp1, const char *path2, const char *tmp2);
ssize_t status_led_grn(const char *freq);
ssize_t status_led_red(const char *freq);
ssize_t status_led_diag_mode_enable(void);
ssize_t status_led_diag_mode_disable(void);
int status_led_check_color(void);
int status_led_check_diag_mode(void);
#if 1
/* For timestamps in SYSFS_LOG */
#define SYSFS_LOG printk
#else
//#define SYSFS_LOG(fmt, args...) printk(KERN_WARNING "[SYSFS] %s/%d: " fmt, __func__, __LINE__, ##args)
#define SYSFS_LOG(fmt, args...) printk(KERN_WARNING "[p_thread] " fmt, ##args)
#endif
#define INV_PTHREAD_KERNEL_MODULE (1)
#define SWITCH_HEALTH_LED_CHANGE_VIA_GPIO (1)
/* inventec_class *********************************/
static struct kobject *status_kobj;
static struct kset *status_kset;
static struct mutex rw_lock;
static int hwm_psoc = INV_HWMID_INIT;
static int hwm_cpld = INV_HWMID_INIT;
int get_hwm_psoc(void)
{
return hwm_psoc;
}
int get_hwm_cpld(void)
{
return hwm_cpld;
}
static ssize_t access_user_space(const char *name, int mode, char *buf, size_t len, loff_t offset)
{
struct file *fp;
mm_segment_t fs;
loff_t pos = offset;
char *mark = NULL;
ssize_t vfs_ret = 0;
if (mode == ACC_R) {
fp = filp_open(name, O_RDONLY, S_IRUGO);
if (IS_ERR(fp))
return -ENODEV;
fs = get_fs();
set_fs(KERNEL_DS);
vfs_ret = vfs_read(fp, buf, len, &pos);
mark = strpbrk(buf, "\n");
if (mark)
*mark = '\0';
filp_close(fp, NULL);
set_fs(fs);
} else if (mode == ACC_W) {
fp = filp_open(name, O_WRONLY, S_IWUSR | S_IRUGO);
if (IS_ERR(fp))
return -ENODEV;
fs = get_fs();
set_fs(KERNEL_DS);
vfs_ret = vfs_write(fp, buf, len, &pos);
filp_close(fp, NULL);
set_fs(fs);
}
return vfs_ret;
}
int inventec_strtol(const char *sbufp, char **endp, unsigned int base)
{
char *endptr;
int value = simple_strtol(sbufp, &endptr, base);
if (value == 0 && sbufp == endptr) {
*endp = NULL;
return value;
}
*endp = (char*)1;
return value;
}
int inventec_singlechar_to_int(const char c)
{
if ((c >= '0') && (c <= '9')) {
return (c - '0');
}
else
if ((c >= 'a') && (c <= 'f')) {
return (c - 'a' + 10);
}
else
if ((c >= 'A') && (c <= 'F')) {
return (c - 'A' + 10);
}
return -1;
}
int inventec_store_input(char *inputp, int count)
{
int i = 0;
while(inputp[i] != '\n' && inputp[i] != '\0' && i < count) {
i++;
}
inputp[i] = '\0';
return strlen(inputp);
}
#if 0
/*
* Time stamps for kernel log on yocto
*/
#include <linux/rtc.h>
void SYSFS_LOG(char *fmt, ...)
{
char buf[80], ts[32];
va_list args;
int hlen;
inventec_tmstmp(&ts[0]);
hlen = sprintf(buf, "[SYSFS] %s ", ts); // Do not edit this line
va_start(args, fmt);
vsprintf(&buf[hlen-1], fmt, args);
va_end(args);
printk(KERN_WARNING "[p_thread] %s\n", buf);
}
#endif
ssize_t
inventec_show_attr(char *buf_p, const char *invdevp)
{
int inv_len = MAX_ACC_SIZE; /* INV driver return max length */
char tmp_buf[MAX_ACC_SIZE];
char *str_negative = "-", *mark = NULL;
/* [Step2] Get data by uaccess */
memset(tmp_buf, 0, sizeof(tmp_buf));
mutex_lock(&rw_lock);
if (access_user_space(invdevp, ACC_R, tmp_buf, inv_len, 0) < 0) {
/* u_access fail */
mutex_unlock(&rw_lock);
return sprintf(buf_p, "%s\n", SHOW_ATTR_WARNING);
}
mutex_unlock(&rw_lock);
/* [Step3] Check return value
* - Ex: When transceiver not plugged
* => SWPS return error code "-202"
* => Pic8 need return "NA" (assume)
*/
if (strcspn(tmp_buf, str_negative) == 0) {
/* error case: <ex> "-202" */
return sprintf(buf_p, "%s\n", SHOW_ATTR_WARNING);
}
/* OK case:*/
mark = strpbrk(tmp_buf, "\n");
if (mark) { *mark = '\0'; }
return sprintf(buf_p, "%s\n", tmp_buf);
}
ssize_t
inventec_store_attr(const char *buf_p, size_t count, const char *invdevp)
{
ssize_t ret = 0;
/* [Step2] Get data by uaccess */
mutex_lock(&rw_lock);
if ((ret = access_user_space(invdevp, ACC_W, (char*)buf_p, count, 0)) < 0) {
/* u_access fail */
mutex_unlock(&rw_lock);
return -EINVAL;
}
mutex_unlock(&rw_lock);
/* OK case:*/
return ret;
}
int sysfs_detect_hwmon_index(void)
{
char hwmon_buf[MAX_ACC_SIZE];
char hwmon_path[MAX_PATH_SIZE];
int hwid = 0;
for (hwid = 0;
hwid < INV_HWMID_MAX && (hwm_psoc == INV_HWMID_INIT || hwm_cpld == INV_HWMID_INIT);
hwid++) {
memset(hwmon_buf, 0, sizeof(hwmon_buf));
sprintf(hwmon_path, "/sys/class/hwmon/hwmon%d/device/name", hwid);
inventec_show_attr(hwmon_buf, hwmon_path);
if (strncmp(hwmon_buf, "inv_psoc", 8) == 0) {
hwm_psoc = hwid;
}
else
if (strncmp(hwmon_buf, "inv_bmc", 7) == 0) {
hwm_psoc = hwid;
}
else
if (strncmp(hwmon_buf, "inv_cpld", 8) == 0) {
hwm_cpld = hwid;
}
}
if (hwid >= INV_HWMID_MAX) {
printk(KERN_ERR "[p_thread] detect hwmon index failed, psoc = %d, cpld = %d\n", hwm_psoc, hwm_cpld);
return -1;
}
printk(KERN_INFO "[p_thread] detect hwmon index success, psoc = %d, cpld = %d\n", hwm_psoc, hwm_cpld);
return 0;
}
static int __init
inventec_class_init(void)
{
mutex_init(&rw_lock);
#ifdef INV_PTHREAD_KERNEL_MODULE
if (sysfs_detect_hwmon_index() < 0) {
return -1;
}
#endif
printk(KERN_INFO "[p_thread] [%s/%d] Module initial success.\n",__func__,__LINE__);
return 0;
}
static void __exit
inventec_class_exit(void)
{
printk(KERN_INFO "[p_thread] [%s/%d] Remove module.\n",__func__,__LINE__);
}
/* fan device *************************************/
#define FAN_DEV_PATH_STATE "/sys/class/hwmon/hwmon%d/device/fan_gpi"
#define FAN_DEV_PATH_FAN1_INPUT "/sys/class/hwmon/hwmon%d/device/fan1_input"
#define FAN_DEV_PATH_FAN2_INPUT "/sys/class/hwmon/hwmon%d/device/fan2_input"
#define FAN_DEV_PATH_FAN3_INPUT "/sys/class/hwmon/hwmon%d/device/fan3_input"
#define FAN_DEV_PATH_FAN4_INPUT "/sys/class/hwmon/hwmon%d/device/fan4_input"
#define FAN_DEV_PATH_FAN5_INPUT "/sys/class/hwmon/hwmon%d/device/fan5_input"
#define FAN_DEV_PATH_FAN6_INPUT "/sys/class/hwmon/hwmon%d/device/fan6_input"
#define FAN_DEV_PATH_FAN7_INPUT "/sys/class/hwmon/hwmon%d/device/fan7_input"
#define FAN_DEV_PATH_FAN8_INPUT "/sys/class/hwmon/hwmon%d/device/fan8_input"
static char fan_dev_path_state[MAX_PATH_SIZE];
static char fan_dev_path_fan1_input[MAX_PATH_SIZE];
static char fan_dev_path_fan2_input[MAX_PATH_SIZE];
static char fan_dev_path_fan3_input[MAX_PATH_SIZE];
static char fan_dev_path_fan4_input[MAX_PATH_SIZE];
static char fan_dev_path_fan5_input[MAX_PATH_SIZE];
static char fan_dev_path_fan6_input[MAX_PATH_SIZE];
static char fan_dev_path_fan7_input[MAX_PATH_SIZE];
static char fan_dev_path_fan8_input[MAX_PATH_SIZE];
void sysfs_fan_path_init(void)
{
sprintf(&fan_dev_path_state[0], FAN_DEV_PATH_STATE, get_hwm_psoc());
sprintf(&fan_dev_path_fan1_input[0],FAN_DEV_PATH_FAN1_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan2_input[0],FAN_DEV_PATH_FAN2_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan3_input[0],FAN_DEV_PATH_FAN3_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan4_input[0],FAN_DEV_PATH_FAN4_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan5_input[0],FAN_DEV_PATH_FAN5_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan6_input[0],FAN_DEV_PATH_FAN6_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan7_input[0],FAN_DEV_PATH_FAN7_INPUT, get_hwm_psoc());
sprintf(&fan_dev_path_fan8_input[0],FAN_DEV_PATH_FAN8_INPUT, get_hwm_psoc());
}
#define FAN_STATE_NORMAL "normal"
#define FAN_STATE_FAULTY "faulty"
#define FAN_STATE_UNINSTALLED "uninstalled"
#define FAN_STATE_UNKNOW "unknown state"
#define FAN_STATE_INVALID "Invalid state value"
#define FAN_STATE_READ_ERROR "state read error"
#define FAN_LOG_UNINSTALLED "removed"
#define FAN_LOG_NORMAL "inserted"
//#define FAN_STATE_BIT_NORMAL 0
#define FAN_STATE_BIT_FAULTY 0
#define FAN_STATE_BIT_UNINSTALLED 1
#define FAN_STATE_BIT_UNKNOW 2
#define FAN_STATE_BIT_INVALID 3
#define FAN_STATE_BIT_READ_ERROR 4
static struct fans_tbl_s {
char *fan_name;
char *fan_front;
char *fan_rear;
unsigned int fan_state;
} fans_tbl[] = {
{"fan1", fan_dev_path_fan1_input,
fan_dev_path_fan2_input, 0},
{"fan2", fan_dev_path_fan3_input,
fan_dev_path_fan4_input, 0},
{"fan3", fan_dev_path_fan5_input,
fan_dev_path_fan6_input, 0},
{"fan4", fan_dev_path_fan7_input,
fan_dev_path_fan8_input, 0},
};
#define FAN_TBL_TOTAL ( sizeof(fans_tbl)/ sizeof(const struct fans_tbl_s) )
#define FAN_STATE_CHECK(i,b) (fans_tbl[i].fan_state & (1<<b))
#define FAN_STATE_SET(i,b) (fans_tbl[i].fan_state |= (1<<b))
#define FAN_STATE_CLEAR(i,b) (fans_tbl[i].fan_state &= ~(1<<b))
#define FAN_STATE_INIT(i) (fans_tbl[i].fan_state = 0)
static int
fans_faulty_log(int fan_id)
{
char buf[MAX_ACC_SIZE];
int pwm;
memset(&buf[0], 0, MAX_ACC_SIZE);
if (inventec_show_attr(buf, fans_tbl[fan_id].fan_front) < 0) {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_READ_ERROR)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_READ_ERROR);
SYSFS_LOG("[p_thread] %s: front %s\n",fans_tbl[fan_id].fan_name,FAN_STATE_READ_ERROR);
}
return 1;
}
pwm = simple_strtol(buf, NULL, 10);
if (pwm <= 0) {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_FAULTY)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_FAULTY);
//SYSFS_LOG("[p_thread] %s: %s\n",fans_tbl[fan_id].fan_name,FAN_STATE_FAULTY);
}
return 1;
}
memset(&buf[0], 0, MAX_ACC_SIZE);
if (inventec_show_attr(buf, fans_tbl[fan_id].fan_rear) < 0) {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_READ_ERROR)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_READ_ERROR);
SYSFS_LOG("[p_thread] %s: rear %s\n",fans_tbl[fan_id].fan_name,FAN_STATE_READ_ERROR);
}
return 1;
}
pwm = simple_strtol(buf, NULL, 10);
if (pwm <= 0) {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_FAULTY)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_FAULTY);
//SYSFS_LOG("[p_thread] %s: %s\n",fans_tbl[fan_id].fan_name,FAN_STATE_FAULTY);
}
return 1;
}
if(fans_tbl[fan_id].fan_state != 0) {
fans_tbl[fan_id].fan_state = 0;
SYSFS_LOG("[p_thread] %s: %s\n",fans_tbl[fan_id].fan_name,FAN_LOG_NORMAL);
}
return 0;
}
/* INV drivers mapping */
static int
fans_control_log(int fan_id)
{
char buf[MAX_ACC_SIZE];
unsigned int statebit2, statebit3, bitshift;
if (inventec_show_attr(buf, fan_dev_path_state) < 0) {
SYSFS_LOG("[p_thread] read fan_gpi failed\n");
return 1;
}
if (buf[0] != '0' || (buf[1] != 'x' && buf[1] != 'X')) {
SYSFS_LOG("[p_thread] %s/%d: %s %s\n",__func__,__LINE__,FAN_STATE_INVALID, buf);
return 1;
}
if ((statebit2 = inventec_singlechar_to_int(buf[2])) == -1) {
SYSFS_LOG("[p_thread] Error value read from %s\n", fan_dev_path_state);
return 1;
}
if (buf[2] == 'f' || buf[2] == 'F') statebit2 = 0;
if ((statebit3 = inventec_singlechar_to_int(buf[3])) == -1) {
SYSFS_LOG("[p_thread] Error value read from %s\n", fan_dev_path_state);
return 1;
}
bitshift = fan_id;
//SYSFS_LOG("[p_thread] 1: statebit2 = 0x%x statebit3 = 0x%x bitshift = 0x%x\n",statebit2,statebit3,bitshift);
if ((statebit2 & 1<<bitshift) && (statebit3 & 1<<bitshift)) {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_UNINSTALLED)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_UNINSTALLED);
SYSFS_LOG("[p_thread] %s: %s\n",fans_tbl[fan_id].fan_name,FAN_LOG_UNINSTALLED);
}
return 1;
}
else
if (!(statebit2 & 1<<bitshift) && !(statebit3 & 1<<bitshift)) {
return fans_faulty_log(fan_id);
}
else {
if(!FAN_STATE_CHECK(fan_id, FAN_STATE_BIT_FAULTY)) {
FAN_STATE_SET(fan_id, FAN_STATE_BIT_FAULTY);
SYSFS_LOG("[p_thread] %s: %s\n",fans_tbl[fan_id].fan_name,FAN_STATE_FAULTY);
}
return 1;
}
return 0;
}
int fans_control(void)
{
int i, ret = 0;
static int cd_shutdown = 5;
for (i = 0; i < FAN_TBL_TOTAL; i++) {
if(fans_control_log(i) == 1) {
ret++;
}
}
if (0 < ret && ret < FAN_TBL_TOTAL) {
status_led_red("2"); //1Hz
cd_shutdown = 5; //reset count down
}
if (ret == FAN_TBL_TOTAL) {
status_led_red("3"); //4Hz
if (cd_shutdown == 0) {
kobject_uevent(status_kobj, KOBJ_REMOVE);
}
else if (cd_shutdown > 0)
{
printk(KERN_ERR "[p_thread] All fans failed.\n");
printk(KERN_ERR "[p_thread] System shutdown immediately in %d seconds.\n", cd_shutdown);
}
cd_shutdown -= 1;
}
return ret;
}
/* End of faninfo_device */
static int __init
fan_device_init(void)
{
#ifdef INV_PTHREAD_KERNEL_MODULE
sysfs_fan_path_init();
#endif
return 0;
}
static void __exit
fan_device_exit(void)
{
printk(KERN_INFO "[p_thread] Remove fan module.\n");
}
/* psu device *************************************/
static unsigned int psu_voltin = 0;
#define PSU_VOLTIN_ACDC (70000)
/*
* normal/unpower/uninstall/fault are PSU states output from driver level
* checkpsu/error are defined by sysfs
*/
#define PSU_STATE_VAL_NORMAL (0)
#define PSU_STATE_VAL_UNPOWER (2)
#define PSU_STATE_VAL_FAULT (4)
#define PSU_STATE_VAL_UNINSTALL (7)
#define PSU_STATE_VAL_CHECKPSU (8)
#define PSU_STATE_VAL_ERROR (9)
#define PSU_STATE_NORMAL ("0 : normal")
#define PSU_STATE_UNPOWER ("2 : unpowered")
#define PSU_STATE_FAULT ("4 : fault")
#define PSU_STATE_UNINSTALL ("7 : not installed")
#define PSU_STATE_CHECKPSU ("8 : check psu")
#define PSU_STATE_ERROR ("9 : state error")
#define PSU_STATE_LEN_NORMAL (strlen(PSU_STATE_NORMAL))
#define PSU_STATE_LEN_UNPOWER (strlen(PSU_STATE_UNPOWER))
#define PSU_STATE_LEN_FAULT (strlen(PSU_STATE_FAULT))
#define PSU_STATE_LEN_UNINSTALL (strlen(PSU_STATE_UNINSTALL))
#define PSU_STATE_LEN_CHECKPSU (strlen(PSU_STATE_CHECKPSU))
typedef struct {
char *inv_dev_attrp;
char *inv_dev_pathp;
} psu_dev_t;
typedef struct {
const char *psu_name;
int psu_major;
dev_t psu_devt;
struct device *psu_dev_p;
psu_dev_t *psu_dev_namep;
int psu_dev_total;
char *psu_inv_pathp;
void *psu_tracking;
char *psu_currentin;
char *psu_currentout;
char *psu_powerin;
char *psu_powerout;
char *psu_voltin;
char *psu_voltout;
} psu_dev_group_t;
#define PSU_DEV_PATH_TEMPLATE "/sys/class/hwmon/hwmon%d/device/%s"
static char psu_dev_path_state[MAX_PATH_SIZE];
static char psu_dev_path_psu_voltin[MAX_PATH_SIZE];
#if 0
static char psu_dev_path_vendor[MAX_PATH_SIZE];
static char psu_dev_path_version[MAX_PATH_SIZE];
static char psu_dev_path_sn[MAX_PATH_SIZE];
static char psu_dev_path_temperature[MAX_PATH_SIZE];
static char psu_dev_path_fan_speed[MAX_PATH_SIZE];
static char psu_dev_path_fan_pwm[MAX_PATH_SIZE];
static char psu_dev_path_fan_faulty[MAX_PATH_SIZE];
static char psu_dev_path_psu_currentin[MAX_PATH_SIZE];
static char psu_dev_path_psu_currentout[MAX_PATH_SIZE];
static char psu_dev_path_psu_powerin[MAX_PATH_SIZE];
static char psu_dev_path_psu_powerout[MAX_PATH_SIZE];
static char psu_dev_path_psu_voltout[MAX_PATH_SIZE];
static char psu_dev_path_psu_pwm[MAX_PATH_SIZE];
static char psu_dev_path_psu_rpm[MAX_PATH_SIZE];
#endif
void sysfs_psu_path_init(void)
{
sprintf(&psu_dev_path_state[0], PSU_DEV_PATH_TEMPLATE, get_hwm_cpld(), "\%s" );
sprintf(&psu_dev_path_psu_voltin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
#if 0
sprintf(&psu_dev_path_vendor[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_version[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_sn[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_temperature[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_fan_speed[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_fan_pwm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_fan_faulty[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_currentin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_currentout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_powerin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_powerout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_voltout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_pwm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
sprintf(&psu_dev_path_psu_rpm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" );
#endif
}
static psu_dev_t psu_dev_name[] = {
{ "state", psu_dev_path_state }, // Using cpld
{ "psu_voltin", psu_dev_path_psu_voltin },
#if 0
{ "vendor", psu_dev_path_vendor },
{ "version", psu_dev_path_version },
{ "sn", psu_dev_path_sn },
{ "temperature", psu_dev_path_temperature },
{ "fan_speed", psu_dev_path_fan_speed },
{ "fan_pwm", psu_dev_path_fan_pwm },
{ "fan_faulty", psu_dev_path_fan_faulty },
{ "psu_currentin", psu_dev_path_psu_currentin },
{ "psu_currentout", psu_dev_path_psu_currentout },
{ "psu_powerin", psu_dev_path_psu_powerin },
{ "psu_powerout", psu_dev_path_psu_powerout },
{ "psu_voltout", psu_dev_path_psu_voltout },
{ "psu_pwm", psu_dev_path_psu_pwm },
{ "psu_rpm", psu_dev_path_psu_rpm },
#endif
};
#define PSU_DEV_NAME_TOTAL ( sizeof(psu_dev_name) / sizeof(const psu_dev_t) )
static psu_dev_group_t psu_dev_group[] = {
{
.psu_name = "psu1",
.psu_dev_namep = &psu_dev_name[0],
.psu_dev_total = sizeof(psu_dev_name) / sizeof(const psu_dev_t),
},
{
.psu_name = "psu2",
.psu_dev_namep = &psu_dev_name[0],
.psu_dev_total = sizeof(psu_dev_name) / sizeof(const psu_dev_t),
},
};
#define PSU_DEV_GROUP_TOTAL ( sizeof(psu_dev_group)/ sizeof(const psu_dev_group_t) )
static char psu_state[4][MIN_ACC_SIZE];
static struct psu_wire_tbl_s {
char *psu_attr;
char *psu_name;
char *psu_wire;
char *psu_state;
} psu_wire_tbl[] = {
{ "state", "psu1", "psu0", psu_state[0] }, // Using cpld
{ "state", "psu2", "psu1", psu_state[1] },
{ "psu_voltin", "psu1", "psoc_psu1_vin", psu_state[2] },
{ "psu_voltin", "psu2", "psoc_psu2_vin", psu_state[3] },
};
#define PSU_WIRE_TBL_TOTAL ( sizeof(psu_wire_tbl)/ sizeof(const struct psu_wire_tbl_s) )
static char *
psu_attr_get_wirep(const char *psu_attrp, const char *psu_namep, char **psu_statepp)
{
int i;
for (i = 0; i < PSU_WIRE_TBL_TOTAL; i++) {
if (strncmp(psu_wire_tbl[i].psu_attr, psu_attrp, strlen(psu_attrp)) == 0 &&
strncmp(psu_wire_tbl[i].psu_name, psu_namep, strlen(psu_namep)) == 0) {
if (psu_statepp) {
*psu_statepp = psu_wire_tbl[i].psu_state;
}
return psu_wire_tbl[i].psu_wire;
}
}
return NULL;
}
int psu_check_state_normal(char *statep)
{
if (strstr(statep, "normal")) {
return 1;
}
return 0;
}
#define PSU_ATTR_VOLTIN ("psu_voltin")
#define PSU_ATTR_VOLTIN_LEN (10)
/* Get PSU voltin for determon AC(110v) or DC(48v) */
void psu_get_voltin(void)
{
char acc_path[MAX_PATH_SIZE], volt[MIN_ACC_SIZE];
psu_dev_t *devnamep;
unsigned int voltin;
char *invwirep;
int i, j;
for (i = 0; i < PSU_DEV_GROUP_TOTAL; i++) {
//psu_dev_group[i].psu_name;
devnamep = psu_dev_group[i].psu_dev_namep;
for (j = 0; j < psu_dev_group[i].psu_dev_total; j++, devnamep++) {
if (strncmp(devnamep->inv_dev_attrp, PSU_ATTR_VOLTIN, PSU_ATTR_VOLTIN_LEN) == 0) {
invwirep = psu_attr_get_wirep(PSU_ATTR_VOLTIN, psu_dev_group[i].psu_name, NULL);
if (invwirep == NULL) {
printk(KERN_DEBUG "[p_thread] Invalid psuname: %s\n", psu_dev_group[i].psu_name);
continue;
}
sprintf(acc_path, devnamep->inv_dev_pathp, invwirep);
//printk(KERN_DEBUG "[p_thread] RYU: %s/%d: acc_path = %s\n",__func__,__LINE__,acc_path);
if (inventec_show_attr(volt, acc_path) <= 0) {
printk(KERN_DEBUG "[p_thread] Read %s failed\n", acc_path);
continue;
}
else {
voltin = simple_strtol(&volt[0], NULL, 10);
printk(KERN_DEBUG "[p_thread] Read %s %s = %u\n",acc_path,volt,voltin);
if (voltin > psu_voltin) {
psu_voltin = voltin;
}
}
}
}
}
SYSFS_LOG("[p_thread] PSU voltin = %u\n", psu_voltin);
}
#define PSU_ATTR_STATE ("state")
#define PSU_ATTR_STATE_LEN (5)
/* psus_control() by inv_thread */
int psus_control(int log_only)
{
char acc_path[MAX_PATH_SIZE], state[MIN_ACC_SIZE];
psu_dev_t *devnamep = NULL;
char *invwirep = NULL;
char *psu_statep = NULL;
int i, j, flag = 0;
for (i = 0; i < PSU_DEV_GROUP_TOTAL; i++) {
devnamep = psu_dev_group[i].psu_dev_namep;
for (j = 0; j < psu_dev_group[i].psu_dev_total; j++, devnamep++) {
if (strncmp(devnamep->inv_dev_attrp, PSU_ATTR_STATE, PSU_ATTR_STATE_LEN) == 0) {
invwirep = psu_attr_get_wirep(PSU_ATTR_STATE, psu_dev_group[i].psu_name, &psu_statep);
if (invwirep == NULL) {
printk(KERN_DEBUG "[p_thread] Invalid psuname: %s\n", psu_dev_group[i].psu_name);
continue;
}
sprintf(acc_path, devnamep->inv_dev_pathp, invwirep);
//printk(KERN_INFO "[p_thread] RYU: %s/%d: acc_path = %s\n",__func__,__LINE__,acc_path);
if (inventec_show_attr(state, acc_path) <= 0) {
printk(KERN_DEBUG "[p_thread] Read %s failed\n", acc_path);
if (strncmp(psu_statep, PSU_STATE_ERROR, strlen(PSU_STATE_ERROR)) != 0) {
strcpy(psu_statep, PSU_STATE_ERROR);
SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,PSU_STATE_ERROR);
}
flag = 1;
}
else
if (strstr(state, "normal")) {
//printk(KERN_INFO "[p_thread] %s: %s\n", psu_dev_group[i].psu_name, state);
if (strncmp(psu_statep, state, strlen(state)) != 0) {
strcpy(psu_statep, state);
SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,state);
}
}
else
if (psu_voltin > PSU_VOLTIN_ACDC) { /* AC PSUS */
//printk(KERN_INFO "[p_thread] RYU: %s: %s\n", psu_dev_group[i].psu_name, state);
if (strncmp(psu_statep, state, strlen(state)) != 0) {
strcpy(psu_statep, state);
SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,state);
}
flag = 1;
}
else { /* DC PSUS */
if (strncmp(psu_statep, PSU_STATE_CHECKPSU, PSU_STATE_LEN_CHECKPSU) != 0) {
strcpy(psu_statep, PSU_STATE_CHECKPSU);
SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,PSU_STATE_CHECKPSU);
}
flag = 1;
}
}
}
}
if (log_only) {
return 0;
}
//SYSFS_LOG("[p_thread] RYU: %s: flag = %d\n",psu_wire_tbl[i].psu_name,flag);
if (flag == 1) {
status_led_grn("3");
return 1;
}
return 0;
}
/* End of psuinfo_device */
static int __init
psu_device_init(void)
{
#ifdef INV_PTHREAD_KERNEL_MODULE
sysfs_psu_path_init();
#endif
return 0;
}
static void __exit
psu_device_exit(void)
{
printk(KERN_INFO "[p_thread] Remove psu module.\n");
}
/* led device *************************************/
#define STATUS_LED_GRN_PATH "/sys/class/hwmon/hwmon%d/device/grn_led"
#define STATUS_LED_RED_PATH "/sys/class/hwmon/hwmon%d/device/red_led"
#define FAN_LED_GRN1_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn1"
#define FAN_LED_GRN2_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn2"
#define FAN_LED_GRN3_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn3"
#define FAN_LED_GRN4_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn4"
#define FAN_LED_RED1_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red1"
#define FAN_LED_RED2_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red2"
#define FAN_LED_RED3_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red3"
#define FAN_LED_RED4_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red4"
#define HWMON_DEVICE_DIAG_PATH "/sys/class/hwmon/hwmon%d/device/diag"
#define HWMON_DEVICE_CTRL_PATH "/sys/class/hwmon/hwmon%d/device/ctl"
static char status_led_grn_path[MAX_PATH_SIZE];
static char status_led_red_path[MAX_PATH_SIZE];
static char fan_led_grn1_path[MAX_PATH_SIZE];
static char fan_led_grn2_path[MAX_PATH_SIZE];
static char fan_led_grn3_path[MAX_PATH_SIZE];
static char fan_led_grn4_path[MAX_PATH_SIZE];
static char fan_led_red1_path[MAX_PATH_SIZE];
static char fan_led_red2_path[MAX_PATH_SIZE];
static char fan_led_red3_path[MAX_PATH_SIZE];
static char fan_led_red4_path[MAX_PATH_SIZE];
static char hwmon_device_diag_path[MAX_PATH_SIZE];
static char hwmon_device_ctrl_path[MAX_PATH_SIZE];
void sysfs_led_path_init(void)
{
sprintf(&status_led_grn_path[0], STATUS_LED_GRN_PATH, get_hwm_cpld());
sprintf(&status_led_red_path[0], STATUS_LED_RED_PATH, get_hwm_cpld());
sprintf(&fan_led_grn1_path[0], FAN_LED_GRN1_PATH, get_hwm_psoc());
sprintf(&fan_led_grn2_path[0], FAN_LED_GRN2_PATH, get_hwm_psoc());
sprintf(&fan_led_grn3_path[0], FAN_LED_GRN3_PATH, get_hwm_psoc());
sprintf(&fan_led_grn4_path[0], FAN_LED_GRN4_PATH, get_hwm_psoc());
sprintf(&fan_led_red1_path[0], FAN_LED_RED1_PATH, get_hwm_psoc());
sprintf(&fan_led_red2_path[0], FAN_LED_RED2_PATH, get_hwm_psoc());
sprintf(&fan_led_red3_path[0], FAN_LED_RED3_PATH, get_hwm_psoc());
sprintf(&fan_led_red4_path[0], FAN_LED_RED4_PATH, get_hwm_psoc());
sprintf(&hwmon_device_diag_path[0], HWMON_DEVICE_DIAG_PATH, get_hwm_psoc());
sprintf(&hwmon_device_ctrl_path[0], HWMON_DEVICE_CTRL_PATH, get_hwm_cpld());
}
/* return 0/off 1/green 2/red */
int
status_led_check_color(void)
{
char tmpbuf[MIN_ACC_SIZE];
int ret = STATUS_LED_INVALID;
if (inventec_show_attr(&tmpbuf[0], status_led_grn_path) > 0) {
if (tmpbuf[0] == '0') {
ret = STATUS_LED_GRN0;
}
if (tmpbuf[0] == '1') {
ret = STATUS_LED_GRN1;
}
if (tmpbuf[0] == '2') {
ret = STATUS_LED_GRN2;
}
if (tmpbuf[0] == '3') {
ret = STATUS_LED_GRN3;
}
if (tmpbuf[0] == '7') {
ret = STATUS_LED_GRN7;
}
return ret;
}
if (inventec_show_attr(&tmpbuf[0], status_led_red_path) > 0) {
if (tmpbuf[0] == '0') {
ret = STATUS_LED_RED0;
}
if (tmpbuf[0] == '1') {
ret = STATUS_LED_RED1;
}
if (tmpbuf[0] == '2') {
ret = STATUS_LED_RED2;
}
if (tmpbuf[0] == '3') {
ret = STATUS_LED_RED3;
}
if (tmpbuf[0] == '7') {
ret = STATUS_LED_RED7;
}
return ret;
}
return ret;
}
/*
* Store attr Section
*/
static DEFINE_MUTEX(diag_mutex);
ssize_t status_led_diag_mode_enable(void)
{
char tmp[MIN_ACC_SIZE];
ssize_t ret;
ret = inventec_show_attr(&tmp[0], hwmon_device_diag_path);
if (ret <= 0) {
return ret;
}
if (tmp[0] == '0') {
mutex_lock(&diag_mutex);
ret = inventec_store_attr("1", 1, hwmon_device_diag_path);
if (ret < 0) {
mutex_unlock(&diag_mutex);
return ret;
}
ret = inventec_store_attr("1", 1, hwmon_device_ctrl_path);
if (ret < 0) {
mutex_unlock(&diag_mutex);
return ret;
}
mutex_unlock(&diag_mutex);
}
return ret;
}
ssize_t status_led_diag_mode_disable(void)
{
char tmp[MIN_ACC_SIZE];
ssize_t ret;
ret = inventec_show_attr(&tmp[0], hwmon_device_diag_path);
if (ret <= 0) {
return ret;
}
if (tmp[0] == '1') {
mutex_lock(&diag_mutex);
ret = inventec_store_attr("0", 1, hwmon_device_diag_path);
if (ret < 0) {
mutex_unlock(&diag_mutex);
return 1;
}
ret = inventec_store_attr("1", 1, hwmon_device_ctrl_path);
if (ret < 0) {
mutex_unlock(&diag_mutex);
return 1;
}
mutex_unlock(&diag_mutex);
}
return 1;
}
ssize_t
status_led_change(const char *path1, const char *tmp1, const char *path2, const char *tmp2)
{
ssize_t ret;
ret = inventec_store_attr(tmp1, strlen(tmp1), path1);
if (ret < 0) {
return ret;
}
ret = inventec_store_attr(tmp2, strlen(tmp2), path2);
if (ret < 0) {
return ret;
}
if ((ret = status_led_diag_mode_enable()) <= 0) {
return ret;
}
ssleep(1);
if ((ret = status_led_diag_mode_disable()) <= 0) {
return ret;
}
return ret;
}
ssize_t
status_led_red(const char *freq)
{
ssize_t ret;
ret = inventec_store_attr("0", 1, &status_led_grn_path[0]);
if (ret < 0) {
return ret;
}
ret = inventec_store_attr(freq, strlen(freq), &status_led_red_path[0]);
if (ret < 0) {
return ret;
}
if ((ret = status_led_diag_mode_enable()) <= 0) {
return ret;
}
ssleep(1);
if ((ret = status_led_diag_mode_disable()) <= 0) {
return ret;
}
return ret;
}
ssize_t
status_led_grn(const char *freq)
{
ssize_t ret;
ret = inventec_store_attr("0", 1, &status_led_red_path[0]);
if (ret < 0) {
return ret;
}
ret = inventec_store_attr(freq, strlen(freq), &status_led_grn_path[0]);
if (ret < 0) {
return ret;
}
if ((ret = status_led_diag_mode_enable()) <= 0) {
return ret;
}
ssleep(1);
if ((ret = status_led_diag_mode_disable()) <= 0) {
return ret;
}
return ret;
}
static int status_led_diag_mode = STATUS_LED_MODE_AUTO;
int status_led_check_diag_mode(void)
{
return status_led_diag_mode;
}
/* End of ledinfo_device */
static int __init
led_device_init(void)
{
#ifdef INV_PTHREAD_KERNEL_MODULE
sysfs_led_path_init();
#endif
return 0;
}
static void __exit
led_device_exit(void)
{
printk(KERN_INFO "[p_thread] Remove led module.\n");
}
/* sensor device **********************************/
#define SENSOR_DEV_PATH_SWITCH_TEMP "/sys/class/hwmon/hwmon%d/device/switch_tmp"
static char sensor_dev_path_switch_temp[MAX_PATH_SIZE];
void sysfs_sensor_path_init(void)
{
sprintf(&sensor_dev_path_switch_temp[0], SENSOR_DEV_PATH_SWITCH_TEMP, get_hwm_psoc());
}
void switch_temp_update(void)
{
char buf[MIN_ACC_SIZE];
ssize_t count = inventec_show_attr(&buf[0], "proc/switch/temp");
if (count > 0) {
//printk(KERN_ERR "[p_thread] [STEMP] Switch temperature is out of range: %d\n", stemp);
inventec_store_attr(&buf[0], count, sensor_dev_path_switch_temp);
}
}
/**************************************************/
/* From system_device */
static int inv_pthread_control = 1;
int thread_control(void)
{
return inv_pthread_control;
}
void thread_control_set(int val)
{
inv_pthread_control = val;
}
/* End system_device */
#define THREAD_SLEEP_MINS (3)
#define THREAD_DELAY_MINS (THREAD_SLEEP_MINS + THREAD_SLEEP_MINS + 1)
extern void psu_get_voltin(void);
static struct task_struct *thread_st;
static int thread_data;
#ifdef SWITCH_HEALTH_LED_CHANGE_VIA_GPIO
void led_set_gpio_to_change_status_led(void)
{
ssize_t ret = inventec_store_attr("253", 3, "/sys/class/gpio/export");
if (ret < 0) {
SYSFS_LOG("[p_thread] Write 253 to /sys/class/gpio/export failed\n");
return;
}
printk("[p_thread] Write 253 to /sys/class/gpio/export\n");
ret = inventec_store_attr("out", 3, "/sys/class/gpio/gpio253/direction");
if (ret < 0) {
SYSFS_LOG("[p_thread] Write low to /sys/class/gpio/gpio253/direction failed\n");
return;
}
//pull high and then low
ret = inventec_store_attr("1", 1, "sys/class/gpio/gpio253/value");
if (ret < 0) {
SYSFS_LOG("[p_thread] Write 1 to sys/class/gpio/gpio253/value failed\n");
}
//pull low
ret = inventec_store_attr("0", 1, "sys/class/gpio/gpio253/value");
if (ret < 0) {
SYSFS_LOG("[p_thread] Write 0 to sys/class/gpio/gpio253/value failed\n");
}
SYSFS_LOG("[p_thread] Set gpio to support status led change successfully\n");
}
#endif
// Function executed by kernel thread
static int thread_fn(void *unused)
{
/* Delay for guarantee HW ready */
ssleep(THREAD_DELAY_MINS);
#ifndef INV_PTHREAD_KERNEL_MODULE
sysfs_led_path_init();
sysfs_fan_path_init();
sysfs_psu_path_init();
#endif
//sysfs_sensor_path_init();
/* Default status init */
status_led_grn("7");
psu_get_voltin();
#ifdef SWITCH_HEALTH_LED_CHANGE_VIA_GPIO
led_set_gpio_to_change_status_led();
#endif
while (1)
{
ssleep(THREAD_SLEEP_MINS);
if (thread_control() == 0) {
printk(KERN_INFO "[p_thread] %s/%d: Thread Stop by inv_pthread control\n",__func__,__LINE__);
break;
}
if (status_led_check_diag_mode() == STATUS_LED_MODE_MANU) {
/* status led in change color/freq mode, higher priority. Ignore fans sttaus */
continue;
}
// switch_temp_update();
if (fans_control() > 0) {
psus_control(1);
continue;
}
else
if (psus_control(0) > 0) {
continue;
}
if (status_led_check_color() != STATUS_LED_GRN7) { /* status led red, change it to green */
status_led_grn("7");
}
}
#ifndef INV_PTHREAD_KERNEL_MODULE
err_inv_pthread_fn_1:
#endif
do_exit(0);
printk(KERN_INFO "[p_thread] %s/%d: Thread Stopped\n",__func__,__LINE__);
return 0;
}
static ssize_t s_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
int fan_absence;
size_t count;
fan_absence = fans_control();
count += sprintf(&buf[count], "%d\n", fan_absence);
return count;
}
static ssize_t s_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
return count;
}
static struct attribute status_att = {
.name = "fan_absence",
.mode = 0777,
};
static const struct sysfs_ops status_ops = {
.show = s_show,
.store = s_store,
};
static struct kobj_type status_ktype = {
.sysfs_ops = &status_ops,
};
static int __init inv_pthread_init(void)
{
int retval;
status_kobj = kzalloc(sizeof(*status_kobj), GFP_KERNEL);
if(!status_kobj)
return PTR_ERR(status_kobj);
status_kset = kset_create_and_add("platform_status", NULL, kernel_kobj);
if(!status_kset)
return -1;
status_kobj->kset = status_kset;
retval = kobject_init_and_add(status_kobj, &status_ktype, NULL, "fan");
if(retval)
return retval;
retval = sysfs_create_file(status_kobj, &status_att);
inventec_class_init();
fan_device_init();
psu_device_init();
led_device_init();
thread_control_set(1);
printk(KERN_INFO "[p_thread] %s/%d: Creating Thread\n",__func__,__LINE__);
//Create the kernel thread with name 'inv_pthread'
thread_st = kthread_run(thread_fn, (void*)&thread_data, "inv_pthread");
if (thread_st)
printk(KERN_INFO "[p_thread] inv_pthread Created successfully\n");
else
printk(KERN_ERR "[p_thread] inv_pthread creation failed\n");
return retval;
}
static void __exit inv_pthread_exit(void)
{
thread_control_set(0);
/* Delay for guarantee thread exit */
ssleep(THREAD_DELAY_MINS);
fan_device_exit();
psu_device_exit();
led_device_exit();
inventec_class_exit();
sysfs_remove_file(status_kobj, &status_att);
kset_unregister(status_kset);
kobject_del(status_kobj);
printk(KERN_INFO "[p_thread] inv_pthread cleaning Up\n");
}
module_init(inv_pthread_init);
module_exit(inv_pthread_exit);
MODULE_AUTHOR("Robert <yu.robertxk@inventec.com>");
MODULE_DESCRIPTION("Inventec Platform Management Thread");
MODULE_VERSION("version 1.0");
MODULE_LICENSE("GPL");