/* * A i2c cpld driver for the ufispace_s9300_32d * * Copyright (C) 2017-2019 UfiSpace Technology Corporation. * Jason Tsai * * Based on ad7414.c * Copyright 2006 Stefan Roese , DENX Software Engineering * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include "x86-64-ufispace-s9300-32d-cpld.h" #ifdef DEBUG #define DEBUG_PRINT(fmt, args...) \ printk(KERN_INFO "%s:%s[%d]: " fmt "\r\n", \ __FILE__, __func__, __LINE__, ##args) #else #define DEBUG_PRINT(fmt, args...) #endif #define BSP_LOG_R(fmt, args...) \ _bsp_log (LOG_READ, KERN_INFO "%s:%s[%d]: " fmt "\r\n", \ __FILE__, __func__, __LINE__, ##args) #define BSP_LOG_W(fmt, args...) \ _bsp_log (LOG_WRITE, KERN_INFO "%s:%s[%d]: " fmt "\r\n", \ __FILE__, __func__, __LINE__, ##args) #define I2C_READ_BYTE_DATA(ret, lock, i2c_client, reg) \ { \ mutex_lock(lock); \ ret = i2c_smbus_read_byte_data(i2c_client, reg); \ mutex_unlock(lock); \ BSP_LOG_R("cpld[%d], reg=0x%03x, reg_val=0x%02x", data->index, reg, ret); \ } #define I2C_WRITE_BYTE_DATA(ret, lock, i2c_client, reg, val) \ { \ mutex_lock(lock); \ ret = i2c_smbus_write_byte_data(i2c_client, reg, val); \ mutex_unlock(lock); \ BSP_LOG_W("cpld[%d], reg=0x%03x, reg_val=0x%02x", data->index, reg, val); \ } /* CPLD sysfs attributes index */ enum s9300_cpld_sysfs_attributes { /* CPLD1 */ CPLD_ACCESS_REG, CPLD_REGISTER_VAL, CPLD_SKU_ID, CPLD_HW_REV, CPLD_DEPH_REV, CPLD_BUILD_REV, CPLD_ID_TYPE, CPLD_MAJOR_VER, CPLD_MINOR_VER, CPLD_BUILD_VER, CPLD_VERION_H, CPLD_ID, CPLD_MAC_INTR, CPLD_10G_PHY_INTR, CPLD_CPLD_FRU_INTR, CPLD_THERMAL_ALERT_INTR, CPLD_MISC_INTR, CPLD_SYSTEM_INTR, CPLD_MAC_INTR_MASK, CPLD_10G_PHY_INTR_MASK, CPLD_CPLD_FRU_INTR_MASK, CPLD_THERMAL_ALERT_INTR_MASK, CPLD_MISC_INTR_MASK, CPLD_MAC_INTR_EVENT, CPLD_10G_PHY_INTR_EVENT, CPLD_CPLD_FRU_INTR_EVENT, CPLD_THERMAL_ALERT_INTR_EVENT, CPLD_MISC_INTR_EVENT, CPLD_MAC_RST, CPLD_10G_PHY_RST, CPLD_BMC_RST, CPLD_USB_RST, CPLD_MUX_RST, CPLD_MISC_RST, CPLD_BMC_WATCHDOG, CPLD_DAU_BD_PRES, CPLD_PSU_STATUS, CPLD_SYS_PW_STATUS, CPLD_MISC, CPLD_MUX_CTRL, CPLD_MAC_QSFP_SEL_CTRL, CPLD_SYS_LED_CTRL_1, CPLD_SYS_LED_CTRL_2, CPLD_BEACON_LED_CTRL, CPLD_PORT_LED_CLR_CTRL, CPLD_EVENT_DETECT_CTRL, /* CPLD2 */ CPLD_QSFPDD_MOD_INT_G0, CPLD_QSFPDD_MOD_INT_G1, CPLD_QSFPDD_MOD_INT_G2, CPLD_QSFPDD_MOD_INT_G3, CPLD_QSFPDD_PRES_G0, CPLD_QSFPDD_PRES_G1, CPLD_QSFPDD_PRES_G2, CPLD_QSFPDD_PRES_G3, CPLD_QSFPDD_FUSE_INT_G0, CPLD_QSFPDD_FUSE_INT_G1, CPLD_QSFPDD_FUSE_INT_G2, CPLD_QSFPDD_FUSE_INT_G3, CPLD_SFP_TXFAULT, CPLD_SFP_ABS, CPLD_SFP_RXLOS, CPLD_QSFPDD_MOD_INT_MASK_G0, CPLD_QSFPDD_MOD_INT_MASK_G1, CPLD_QSFPDD_MOD_INT_MASK_G2, CPLD_QSFPDD_MOD_INT_MASK_G3, CPLD_QSFPDD_PRES_MASK_G0, CPLD_QSFPDD_PRES_MASK_G1, CPLD_QSFPDD_PRES_MASK_G2, CPLD_QSFPDD_PRES_MASK_G3, CPLD_QSFPDD_FUSE_INT_MASK_G0, CPLD_QSFPDD_FUSE_INT_MASK_G1, CPLD_QSFPDD_FUSE_INT_MASK_G2, CPLD_QSFPDD_FUSE_INT_MASK_G3, CPLD_SFP_TXFAULT_MASK, CPLD_SFP_ABS_MASK, CPLD_SFP_RXLOS_MASK, CPLD_QSFPDD_MOD_INT_EVENT_G0, CPLD_QSFPDD_MOD_INT_EVENT_G1, CPLD_QSFPDD_MOD_INT_EVENT_G2, CPLD_QSFPDD_MOD_INT_EVENT_G3, CPLD_QSFPDD_PRES_EVENT_G0, CPLD_QSFPDD_PRES_EVENT_G1, CPLD_QSFPDD_PRES_EVENT_G2, CPLD_QSFPDD_PRES_EVENT_G3, CPLD_QSFPDD_FUSE_INT_EVENT_G0, CPLD_QSFPDD_FUSE_INT_EVENT_G1, CPLD_QSFPDD_FUSE_INT_EVENT_G2, CPLD_QSFPDD_FUSE_INT_EVENT_G3, CPLD_SFP_TXFAULT_EVENT, CPLD_SFP_ABS_EVENT, CPLD_SFP_RXLOS_EVENT, CPLD_QSFPDD_RESET_CTRL_G0, CPLD_QSFPDD_RESET_CTRL_G1, CPLD_QSFPDD_RESET_CTRL_G2, CPLD_QSFPDD_RESET_CTRL_G3, CPLD_QSFPDD_LP_MODE_G0, CPLD_QSFPDD_LP_MODE_G1, CPLD_QSFPDD_LP_MODE_G2, CPLD_QSFPDD_LP_MODE_G3, CPLD_SFP_TX_DIS, CPLD_SFP_RS, CPLD_SFP_TS, CPLD_PORT_INT_STATUS, //BSP DEBUG BSP_DEBUG }; enum bsp_log_types { LOG_NONE, LOG_RW, LOG_READ, LOG_WRITE }; enum bsp_log_ctrl { LOG_DISABLE, LOG_ENABLE }; /* CPLD sysfs attributes hook functions */ static ssize_t read_access_register(struct device *dev, struct device_attribute *da, char *buf); static ssize_t write_access_register(struct device *dev, struct device_attribute *da, const char *buf, size_t count); static ssize_t read_register_value(struct device *dev, struct device_attribute *da, char *buf); static ssize_t write_register_value(struct device *dev, struct device_attribute *da, const char *buf, size_t count); static ssize_t read_hw_rev_cb(struct device *dev, struct device_attribute *da, char *buf); static ssize_t read_cpld_version_cb(struct device *dev, struct device_attribute *da, char *buf); static ssize_t read_cpld_callback(struct device *dev, struct device_attribute *da, char *buf); static ssize_t write_cpld_callback(struct device *dev, struct device_attribute *da, const char *buf, size_t count); static ssize_t read_cpld_version_h_cb(struct device *dev, struct device_attribute *da, char *buf); // cpld access api static ssize_t read_cpld_reg(struct device *dev, char *buf, u8 reg); static ssize_t write_cpld_reg(struct device *dev, const char *buf, size_t count, u8 reg); static bool read_cpld_reg_raw_byte(struct device *dev, u8 reg, u8 *val, int *errno); static bool read_cpld_reg_raw_int(struct device *dev, u8 reg, int *val); // bsp debug api static ssize_t read_bsp(char *buf, char *str); static ssize_t write_bsp(const char *buf, char *str, size_t str_len, size_t count); static ssize_t read_bsp_callback(struct device *dev, struct device_attribute *da, char *buf); static ssize_t write_bsp_callback(struct device *dev, struct device_attribute *da, const char *buf, size_t count); static LIST_HEAD(cpld_client_list); /* client list for cpld */ static struct mutex list_lock; /* mutex for client list */ struct cpld_client_node { struct i2c_client *client; struct list_head list; }; struct cpld_data { int index; /* CPLD index */ struct mutex access_lock; /* mutex for cpld access */ u8 access_reg; /* register to access */ }; /* CPLD device id and data */ static const struct i2c_device_id s9300_cpld_id[] = { { "s9300_32d_cpld1", cpld1 }, { "s9300_32d_cpld2", cpld2 }, { "s9300_32d_cpld3", cpld3 }, {} }; char bsp_debug[2]="0"; u8 enable_log_read=LOG_DISABLE; u8 enable_log_write=LOG_DISABLE; /* Addresses scanned for s9300_cpld */ static const unsigned short cpld_i2c_addr[] = { 0x30, 0x31, 0x32, I2C_CLIENT_END }; /* define all support register access of cpld in attribute */ /* CPLD1 */ static SENSOR_DEVICE_ATTR(cpld_access_register, S_IWUSR | S_IRUGO, \ read_access_register, write_access_register, CPLD_ACCESS_REG); static SENSOR_DEVICE_ATTR(cpld_register_value, S_IWUSR | S_IRUGO, \ read_register_value, write_register_value, CPLD_REGISTER_VAL); static SENSOR_DEVICE_ATTR(cpld_sku_id, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SKU_ID); static SENSOR_DEVICE_ATTR(cpld_hw_rev, S_IRUGO, \ read_hw_rev_cb, NULL, CPLD_HW_REV); static SENSOR_DEVICE_ATTR(cpld_deph_rev, S_IRUGO, \ read_hw_rev_cb, NULL, CPLD_DEPH_REV); static SENSOR_DEVICE_ATTR(cpld_build_rev, S_IRUGO, \ read_hw_rev_cb, NULL, CPLD_BUILD_REV); static SENSOR_DEVICE_ATTR(cpld_id_type, S_IRUGO, \ read_hw_rev_cb, NULL, CPLD_ID_TYPE); static SENSOR_DEVICE_ATTR(cpld_major_ver, S_IRUGO, \ read_cpld_version_cb, NULL, CPLD_MAJOR_VER); static SENSOR_DEVICE_ATTR(cpld_minor_ver, S_IRUGO, \ read_cpld_version_cb, NULL, CPLD_MINOR_VER); static SENSOR_DEVICE_ATTR(cpld_build_ver, S_IRUGO, \ read_cpld_callback, NULL, CPLD_BUILD_VER); static SENSOR_DEVICE_ATTR(cpld_version_h, S_IRUGO, \ read_cpld_version_h_cb, NULL, CPLD_VERION_H); static SENSOR_DEVICE_ATTR(cpld_id, S_IRUGO, \ read_cpld_callback, NULL, CPLD_ID); static SENSOR_DEVICE_ATTR(cpld_mac_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_MAC_INTR); static SENSOR_DEVICE_ATTR(cpld_10g_phy_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_10G_PHY_INTR); static SENSOR_DEVICE_ATTR(cpld_cpld_fru_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_CPLD_FRU_INTR); static SENSOR_DEVICE_ATTR(cpld_thermal_alert_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_THERMAL_ALERT_INTR); static SENSOR_DEVICE_ATTR(cpld_misc_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_MISC_INTR); static SENSOR_DEVICE_ATTR(cpld_system_intr, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SYSTEM_INTR); static SENSOR_DEVICE_ATTR(cpld_mac_intr_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MAC_INTR_MASK); static SENSOR_DEVICE_ATTR(cpld_10g_phy_intr_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_10G_PHY_INTR_MASK); static SENSOR_DEVICE_ATTR(cpld_cpld_fru_intr_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_CPLD_FRU_INTR_MASK); static SENSOR_DEVICE_ATTR(cpld_thermal_alert_intr_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_THERMAL_ALERT_INTR_MASK); static SENSOR_DEVICE_ATTR(cpld_misc_intr_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MISC_INTR_MASK); static SENSOR_DEVICE_ATTR(cpld_mac_intr_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_MAC_INTR_EVENT); static SENSOR_DEVICE_ATTR(cpld_10g_phy_intr_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_10G_PHY_INTR_EVENT); static SENSOR_DEVICE_ATTR(cpld_cpld_fru_intr_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_CPLD_FRU_INTR_EVENT); static SENSOR_DEVICE_ATTR(cpld_thermal_alert_intr_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_THERMAL_ALERT_INTR_EVENT); static SENSOR_DEVICE_ATTR(cpld_misc_intr_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_MISC_INTR_EVENT); static SENSOR_DEVICE_ATTR(cpld_mac_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MAC_RST); static SENSOR_DEVICE_ATTR(cpld_10g_phy_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_10G_PHY_RST); static SENSOR_DEVICE_ATTR(cpld_bmc_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_BMC_RST); static SENSOR_DEVICE_ATTR(cpld_usb_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_USB_RST); static SENSOR_DEVICE_ATTR(cpld_mux_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MUX_RST); static SENSOR_DEVICE_ATTR(cpld_misc_rst, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MISC_RST); static SENSOR_DEVICE_ATTR(cpld_bmc_watchdog, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_BMC_WATCHDOG); static SENSOR_DEVICE_ATTR(cpld_dau_bd_pres, S_IRUGO, \ read_cpld_callback, NULL, CPLD_DAU_BD_PRES); static SENSOR_DEVICE_ATTR(cpld_psu_status, S_IRUGO, \ read_cpld_callback, NULL, CPLD_PSU_STATUS); static SENSOR_DEVICE_ATTR(cpld_sys_pw_status, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SYS_PW_STATUS); static SENSOR_DEVICE_ATTR(cpld_misc, S_IRUGO, \ read_cpld_callback, NULL, CPLD_MISC); static SENSOR_DEVICE_ATTR(cpld_mux_ctrl, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MUX_CTRL); static SENSOR_DEVICE_ATTR(cpld_mac_qsfp_sel_ctrl, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_MAC_QSFP_SEL_CTRL); static SENSOR_DEVICE_ATTR(cpld_sys_led_ctrl_1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SYS_LED_CTRL_1); static SENSOR_DEVICE_ATTR(cpld_sys_led_ctrl_2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SYS_LED_CTRL_2); static SENSOR_DEVICE_ATTR(cpld_beacon_led_ctrl, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_BEACON_LED_CTRL); static SENSOR_DEVICE_ATTR(cpld_port_led_clr_ctrl, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_PORT_LED_CLR_CTRL); static SENSOR_DEVICE_ATTR(cpld_event_detect_ctrl, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_EVENT_DETECT_CTRL); /* CPLD2 */ static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_G3); static SENSOR_DEVICE_ATTR(cpld_sfp_txfault, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_TXFAULT); static SENSOR_DEVICE_ATTR(cpld_sfp_abs, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_ABS); static SENSOR_DEVICE_ATTR(cpld_sfp_rxlos, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_RXLOS); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_mask_g0, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_MOD_INT_MASK_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_mask_g1, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_MOD_INT_MASK_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_mask_g2, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_MOD_INT_MASK_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_mask_g3, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_MOD_INT_MASK_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_mask_g0, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_PRES_MASK_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_mask_g1, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_PRES_MASK_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_mask_g2, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_PRES_MASK_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_mask_g3, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_PRES_MASK_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_mask_g0, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_FUSE_INT_MASK_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_mask_g1, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_FUSE_INT_MASK_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_mask_g2, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_FUSE_INT_MASK_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_mask_g3, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_QSFPDD_FUSE_INT_MASK_G3); static SENSOR_DEVICE_ATTR(cpld_sfp_txfault_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_TXFAULT_MASK); static SENSOR_DEVICE_ATTR(cpld_sfp_abs_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_ABS_MASK); static SENSOR_DEVICE_ATTR(cpld_sfp_rxlos_mask, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_RXLOS_MASK); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_event_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_EVENT_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_event_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_EVENT_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_event_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_EVENT_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_mod_int_event_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_MOD_INT_EVENT_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_event_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_EVENT_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_event_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_EVENT_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_event_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_EVENT_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_pres_event_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_PRES_EVENT_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_event_g0, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_EVENT_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_event_g1, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_EVENT_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_event_g2, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_EVENT_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_fuse_int_event_g3, S_IRUGO, \ read_cpld_callback, NULL, CPLD_QSFPDD_FUSE_INT_EVENT_G3); static SENSOR_DEVICE_ATTR(cpld_sfp_txfault_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_TXFAULT_EVENT); static SENSOR_DEVICE_ATTR(cpld_sfp_abs_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_ABS_EVENT); static SENSOR_DEVICE_ATTR(cpld_sfp_rxlos_event, S_IRUGO, \ read_cpld_callback, NULL, CPLD_SFP_RXLOS_EVENT); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_reset_ctrl_g0, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_RESET_CTRL_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_reset_ctrl_g1, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_RESET_CTRL_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_reset_ctrl_g2, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_RESET_CTRL_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_reset_ctrl_g3, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_RESET_CTRL_G3); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_lp_mode_g0, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_LP_MODE_G0); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_lp_mode_g1, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_LP_MODE_G1); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_lp_mode_g2, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_LP_MODE_G2); static SENSOR_DEVICE_ATTR(cpld_qsfpdd_lp_mode_g3, \ S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, \ CPLD_QSFPDD_LP_MODE_G3); static SENSOR_DEVICE_ATTR(cpld_sfp_tx_dis, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_TX_DIS); static SENSOR_DEVICE_ATTR(cpld_sfp_rs, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_RS); static SENSOR_DEVICE_ATTR(cpld_sfp_ts, S_IWUSR | S_IRUGO, \ read_cpld_callback, write_cpld_callback, CPLD_SFP_TS); static SENSOR_DEVICE_ATTR(cpld_port_int_status, S_IRUGO, \ read_cpld_callback, NULL, CPLD_PORT_INT_STATUS); //BSP DEBUG static SENSOR_DEVICE_ATTR(bsp_debug, S_IRUGO | S_IWUSR, \ read_bsp_callback, write_bsp_callback, BSP_DEBUG); /* define support attributes of cpldx , total 3 */ /* cpld 1 */ static struct attribute *s9300_cpld1_attributes[] = { &sensor_dev_attr_cpld_access_register.dev_attr.attr, &sensor_dev_attr_cpld_register_value.dev_attr.attr, &sensor_dev_attr_cpld_sku_id.dev_attr.attr, &sensor_dev_attr_cpld_hw_rev.dev_attr.attr, &sensor_dev_attr_cpld_deph_rev.dev_attr.attr, &sensor_dev_attr_cpld_build_rev.dev_attr.attr, &sensor_dev_attr_cpld_id_type.dev_attr.attr, &sensor_dev_attr_cpld_major_ver.dev_attr.attr, &sensor_dev_attr_cpld_minor_ver.dev_attr.attr, &sensor_dev_attr_cpld_build_ver.dev_attr.attr, &sensor_dev_attr_cpld_version_h.dev_attr.attr, &sensor_dev_attr_cpld_id.dev_attr.attr, &sensor_dev_attr_cpld_mac_intr.dev_attr.attr, &sensor_dev_attr_cpld_10g_phy_intr.dev_attr.attr, &sensor_dev_attr_cpld_cpld_fru_intr.dev_attr.attr, &sensor_dev_attr_cpld_thermal_alert_intr.dev_attr.attr, &sensor_dev_attr_cpld_misc_intr.dev_attr.attr, &sensor_dev_attr_cpld_system_intr.dev_attr.attr, &sensor_dev_attr_cpld_mac_intr_mask.dev_attr.attr, &sensor_dev_attr_cpld_10g_phy_intr_mask.dev_attr.attr, &sensor_dev_attr_cpld_cpld_fru_intr_mask.dev_attr.attr, &sensor_dev_attr_cpld_thermal_alert_intr_mask.dev_attr.attr, &sensor_dev_attr_cpld_misc_intr_mask.dev_attr.attr, &sensor_dev_attr_cpld_mac_intr_event.dev_attr.attr, &sensor_dev_attr_cpld_10g_phy_intr_event.dev_attr.attr, &sensor_dev_attr_cpld_cpld_fru_intr_event.dev_attr.attr, &sensor_dev_attr_cpld_thermal_alert_intr_event.dev_attr.attr, &sensor_dev_attr_cpld_misc_intr_event.dev_attr.attr, &sensor_dev_attr_cpld_mac_rst.dev_attr.attr, &sensor_dev_attr_cpld_10g_phy_rst.dev_attr.attr, &sensor_dev_attr_cpld_bmc_rst.dev_attr.attr, &sensor_dev_attr_cpld_usb_rst.dev_attr.attr, &sensor_dev_attr_cpld_mux_rst.dev_attr.attr, &sensor_dev_attr_cpld_misc_rst.dev_attr.attr, &sensor_dev_attr_cpld_bmc_watchdog.dev_attr.attr, &sensor_dev_attr_cpld_dau_bd_pres.dev_attr.attr, &sensor_dev_attr_cpld_psu_status.dev_attr.attr, &sensor_dev_attr_cpld_sys_pw_status.dev_attr.attr, &sensor_dev_attr_cpld_misc.dev_attr.attr, &sensor_dev_attr_cpld_mux_ctrl.dev_attr.attr, &sensor_dev_attr_cpld_mac_qsfp_sel_ctrl.dev_attr.attr, &sensor_dev_attr_cpld_sys_led_ctrl_1.dev_attr.attr, &sensor_dev_attr_cpld_sys_led_ctrl_2.dev_attr.attr, &sensor_dev_attr_cpld_beacon_led_ctrl.dev_attr.attr, &sensor_dev_attr_cpld_port_led_clr_ctrl.dev_attr.attr, &sensor_dev_attr_cpld_event_detect_ctrl.dev_attr.attr, &sensor_dev_attr_bsp_debug.dev_attr.attr, NULL }; /* cpld 2 */ static struct attribute *s9300_cpld2_attributes[] = { &sensor_dev_attr_cpld_access_register.dev_attr.attr, &sensor_dev_attr_cpld_register_value.dev_attr.attr, &sensor_dev_attr_cpld_major_ver.dev_attr.attr, &sensor_dev_attr_cpld_minor_ver.dev_attr.attr, &sensor_dev_attr_cpld_build_ver.dev_attr.attr, &sensor_dev_attr_cpld_version_h.dev_attr.attr, &sensor_dev_attr_cpld_id.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_g3.dev_attr.attr, &sensor_dev_attr_cpld_sfp_txfault.dev_attr.attr, &sensor_dev_attr_cpld_sfp_abs.dev_attr.attr, &sensor_dev_attr_cpld_sfp_rxlos.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_mask_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_mask_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_mask_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_mask_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_mask_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_mask_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_mask_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_mask_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_mask_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_mask_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_mask_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_mask_g3.dev_attr.attr, &sensor_dev_attr_cpld_sfp_txfault_mask.dev_attr.attr, &sensor_dev_attr_cpld_sfp_abs_mask.dev_attr.attr, &sensor_dev_attr_cpld_sfp_rxlos_mask.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_event_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_event_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_event_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_mod_int_event_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_event_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_event_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_event_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_pres_event_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_event_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_event_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_event_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_fuse_int_event_g3.dev_attr.attr, &sensor_dev_attr_cpld_sfp_txfault_event.dev_attr.attr, &sensor_dev_attr_cpld_sfp_abs_event.dev_attr.attr, &sensor_dev_attr_cpld_sfp_rxlos_event.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_reset_ctrl_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_reset_ctrl_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_reset_ctrl_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_reset_ctrl_g3.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_lp_mode_g0.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_lp_mode_g1.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_lp_mode_g2.dev_attr.attr, &sensor_dev_attr_cpld_qsfpdd_lp_mode_g3.dev_attr.attr, &sensor_dev_attr_cpld_sfp_tx_dis.dev_attr.attr, &sensor_dev_attr_cpld_sfp_rs.dev_attr.attr, &sensor_dev_attr_cpld_sfp_ts.dev_attr.attr, &sensor_dev_attr_cpld_port_int_status.dev_attr.attr, NULL }; /* cpld 3 */ static struct attribute *s9300_cpld3_attributes[] = { &sensor_dev_attr_cpld_access_register.dev_attr.attr, &sensor_dev_attr_cpld_register_value.dev_attr.attr, &sensor_dev_attr_cpld_major_ver.dev_attr.attr, &sensor_dev_attr_cpld_minor_ver.dev_attr.attr, &sensor_dev_attr_cpld_build_ver.dev_attr.attr, &sensor_dev_attr_cpld_version_h.dev_attr.attr, &sensor_dev_attr_cpld_id.dev_attr.attr, NULL }; /* cpld 1 attributes group */ static const struct attribute_group s9300_cpld1_group = { .attrs = s9300_cpld1_attributes, }; /* cpld 2 attributes group */ static const struct attribute_group s9300_cpld2_group = { .attrs = s9300_cpld2_attributes, }; /* cpld 3 attributes group */ static const struct attribute_group s9300_cpld3_group = { .attrs = s9300_cpld3_attributes, }; static int _bsp_log(u8 log_type, char *fmt, ...) { if ((log_type==LOG_READ && enable_log_read) || (log_type==LOG_WRITE && enable_log_write)) { va_list args; int r; va_start(args, fmt); r = vprintk(fmt, args); va_end(args); return r; } else { return 0; } } static int _config_bsp_log(u8 log_type) { switch(log_type) { case LOG_NONE: enable_log_read = LOG_DISABLE; enable_log_write = LOG_DISABLE; break; case LOG_RW: enable_log_read = LOG_ENABLE; enable_log_write = LOG_ENABLE; break; case LOG_READ: enable_log_read = LOG_ENABLE; enable_log_write = LOG_DISABLE; break; case LOG_WRITE: enable_log_read = LOG_DISABLE; enable_log_write = LOG_ENABLE; break; default: return -EINVAL; } return 0; } /* get bsp value */ static ssize_t read_bsp(char *buf, char *str) { ssize_t len=0; len=sprintf(buf, "%s", str); BSP_LOG_R("reg_val=%s", str); return len; } /* set bsp value */ static ssize_t write_bsp(const char *buf, char *str, size_t str_len, size_t count) { snprintf(str, str_len, "%s", buf); BSP_LOG_W("reg_val=%s", str); return count; } /* get bsp parameter value */ static ssize_t read_bsp_callback(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int str_len=0; char *str=NULL; switch (attr->index) { case BSP_DEBUG: str = bsp_debug; str_len = sizeof(bsp_debug); break; default: return -EINVAL; } return read_bsp(buf, str); } /* set bsp parameter value */ static ssize_t write_bsp_callback(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int str_len=0; char *str=NULL; ssize_t ret = 0; u8 bsp_debug_u8 = 0; switch (attr->index) { case BSP_DEBUG: str = bsp_debug; str_len = sizeof(str); ret = write_bsp(buf, str, str_len, count); if (kstrtou8(buf, 0, &bsp_debug_u8) < 0) { return -EINVAL; } else if (_config_bsp_log(bsp_debug_u8) < 0) { return -EINVAL; } return ret; default: return -EINVAL; } return 0; } /* read access register from cpld data */ static ssize_t read_access_register(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 reg = data->access_reg; return sprintf(buf, "0x%x\n", reg); } /* write access register to cpld data */ static ssize_t write_access_register(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 reg; if (kstrtou8(buf, 0, ®) < 0) return -EINVAL; data->access_reg = reg; return count; } /* read the value of access register in cpld data */ static ssize_t read_register_value(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 reg = data->access_reg; int reg_val; I2C_READ_BYTE_DATA(reg_val, &data->access_lock, client, reg); if (reg_val < 0) return reg_val; return sprintf(buf, "0x%x\n", reg_val); } /* wrtie the value to access register in cpld data */ static ssize_t write_register_value(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); int ret = -EIO; u8 reg = data->access_reg; u8 reg_val; if (kstrtou8(buf, 0, ®_val) < 0) return -EINVAL; I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); if (unlikely(ret < 0)) { dev_err(dev, "I2C_WRITE_BYTE_DATA error, return=%d\n", ret); return ret; } return count; } /* get cpld register value */ static ssize_t read_cpld_reg(struct device *dev, char *buf, u8 reg) { int reg_val; if (read_cpld_reg_raw_int(dev, reg, ®_val)) return sprintf(buf, "0x%02x\n", reg_val); else return reg_val; } static bool read_cpld_reg_raw_int(struct device *dev, u8 reg, int *val) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); I2C_READ_BYTE_DATA(*val, &data->access_lock, client, reg); if (unlikely(*val < 0)) { dev_err(dev, "read_cpld_reg_raw_int() error, return=%d\n", *val); return false; } return true; } static bool read_cpld_reg_raw_byte(struct device *dev, u8 reg, u8 *val, int *errno) { int reg_val; if (read_cpld_reg_raw_int(dev, reg, ®_val)) { *val = (u8)reg_val; return true; } else { *errno = reg_val; return false; } } /* handle read for attributes */ static ssize_t read_cpld_callback(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); u8 reg = 0; switch (attr->index) { case CPLD_SKU_ID: reg = CPLD_SKU_ID_REG; break; case CPLD_ID: reg = CPLD_ID_REG; break; case CPLD_BUILD_VER: reg = CPLD_BUILD_VER_REG; break; case CPLD_MAC_INTR: reg = CPLD_MAC_INTR_REG; break; case CPLD_10G_PHY_INTR: reg = CPLD_10G_PHY_INTR_REG; break; case CPLD_CPLD_FRU_INTR: reg = CPLD_CPLD_FRU_INTR_REG; break; case CPLD_THERMAL_ALERT_INTR: reg = CPLD_THERMAL_ALERT_INTR_REG; break; case CPLD_MISC_INTR: reg = CPLD_MISC_INTR_REG; break; case CPLD_SYSTEM_INTR: reg = CPLD_SYSTEM_INTR_REG; break; case CPLD_MAC_INTR_MASK: reg = CPLD_MAC_INTR_MASK_REG; break; case CPLD_10G_PHY_INTR_MASK: reg = CPLD_10G_PHY_INTR_MASK_REG; break; case CPLD_CPLD_FRU_INTR_MASK: reg = CPLD_CPLD_FRU_INTR_MASK_REG; break; case CPLD_THERMAL_ALERT_INTR_MASK: reg = CPLD_THERMAL_ALERT_INTR_MASK_REG; break; case CPLD_MISC_INTR_MASK: reg = CPLD_MISC_INTR_MASK_REG; break; case CPLD_MAC_INTR_EVENT: reg = CPLD_MAC_INTR_EVENT_REG; break; case CPLD_10G_PHY_INTR_EVENT: reg = CPLD_10G_PHY_INTR_EVENT_REG; break; case CPLD_CPLD_FRU_INTR_EVENT: reg = CPLD_CPLD_FRU_INTR_EVENT_REG; break; case CPLD_THERMAL_ALERT_INTR_EVENT: reg = CPLD_THERMAL_ALERT_INTR_EVENT_REG; break; case CPLD_MISC_INTR_EVENT: reg = CPLD_MISC_INTR_EVENT_REG; break; case CPLD_MAC_RST: reg = CPLD_MAC_RST_REG; break; case CPLD_10G_PHY_RST: reg = CPLD_10G_PHY_RST_REG; break; case CPLD_BMC_RST: reg = CPLD_BMC_RST_REG; break; case CPLD_USB_RST: reg = CPLD_USB_RST_REG; break; case CPLD_MUX_RST: reg = CPLD_MUX_RST_REG; break; case CPLD_MISC_RST: reg = CPLD_MISC_RST_REG; break; case CPLD_BMC_WATCHDOG: reg = CPLD_BMC_WATCHDOG_REG; break; case CPLD_DAU_BD_PRES: reg = CPLD_DAU_BD_PRES_REG; break; case CPLD_PSU_STATUS: reg = CPLD_PSU_STATUS_REG; break; case CPLD_SYS_PW_STATUS: reg = CPLD_SYS_PW_STATUS_REG; break; case CPLD_MISC: reg = CPLD_MISC_REG; break; case CPLD_MUX_CTRL: reg = CPLD_MUX_CTRL_REG; break; case CPLD_MAC_QSFP_SEL_CTRL: reg = CPLD_MAC_QSFP_SEL_CTRL_REG; break; case CPLD_SYS_LED_CTRL_1: reg = CPLD_SYS_LED_CTRL_1_REG; break; case CPLD_SYS_LED_CTRL_2: reg = CPLD_SYS_LED_CTRL_2_REG; break; case CPLD_BEACON_LED_CTRL: reg = CPLD_BEACON_LED_CTRL_REG; break; case CPLD_PORT_LED_CLR_CTRL: reg = CPLD_PORT_LED_CLR_CTRL_REG; break; case CPLD_EVENT_DETECT_CTRL: reg = CPLD_EVENT_DETECT_CTRL_REG; break; case CPLD_QSFPDD_MOD_INT_G0: reg = CPLD_QSFPDD_MOD_INT_G0_REG; break; case CPLD_QSFPDD_MOD_INT_G1: reg = CPLD_QSFPDD_MOD_INT_G1_REG; break; case CPLD_QSFPDD_MOD_INT_G2: reg = CPLD_QSFPDD_MOD_INT_G2_REG; break; case CPLD_QSFPDD_MOD_INT_G3: reg = CPLD_QSFPDD_MOD_INT_G3_REG; break; case CPLD_QSFPDD_PRES_G0: reg = CPLD_QSFPDD_PRES_G0_REG; break; case CPLD_QSFPDD_PRES_G1: reg = CPLD_QSFPDD_PRES_G1_REG; break; case CPLD_QSFPDD_PRES_G2: reg = CPLD_QSFPDD_PRES_G2_REG; break; case CPLD_QSFPDD_PRES_G3: reg = CPLD_QSFPDD_PRES_G3_REG; break; case CPLD_QSFPDD_FUSE_INT_G0: reg = CPLD_QSFPDD_FUSE_INT_G0_REG; break; case CPLD_QSFPDD_FUSE_INT_G1: reg = CPLD_QSFPDD_FUSE_INT_G1_REG; break; case CPLD_QSFPDD_FUSE_INT_G2: reg = CPLD_QSFPDD_FUSE_INT_G2_REG; break; case CPLD_QSFPDD_FUSE_INT_G3: reg = CPLD_QSFPDD_FUSE_INT_G3_REG; break; case CPLD_SFP_TXFAULT: reg = CPLD_SFP_TXFAULT_REG; break; case CPLD_SFP_ABS: reg = CPLD_SFP_ABS_REG; break; case CPLD_SFP_RXLOS: reg = CPLD_SFP_RXLOS_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G0: reg = CPLD_QSFPDD_MOD_INT_MASK_G0_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G1: reg = CPLD_QSFPDD_MOD_INT_MASK_G1_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G2: reg = CPLD_QSFPDD_MOD_INT_MASK_G2_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G3: reg = CPLD_QSFPDD_MOD_INT_MASK_G3_REG; break; case CPLD_QSFPDD_PRES_MASK_G0: reg = CPLD_QSFPDD_PRES_MASK_G0_REG; break; case CPLD_QSFPDD_PRES_MASK_G1: reg = CPLD_QSFPDD_PRES_MASK_G1_REG; break; case CPLD_QSFPDD_PRES_MASK_G2: reg = CPLD_QSFPDD_PRES_MASK_G2_REG; break; case CPLD_QSFPDD_PRES_MASK_G3: reg = CPLD_QSFPDD_PRES_MASK_G3_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G0: reg = CPLD_QSFPDD_FUSE_INT_MASK_G0_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G1: reg = CPLD_QSFPDD_FUSE_INT_MASK_G1_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G2: reg = CPLD_QSFPDD_FUSE_INT_MASK_G2_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G3: reg = CPLD_QSFPDD_FUSE_INT_MASK_G3_REG; break; case CPLD_SFP_TXFAULT_MASK: reg = CPLD_SFP_TXFAULT_MASK_REG; break; case CPLD_SFP_ABS_MASK: reg = CPLD_SFP_ABS_MASK_REG; break; case CPLD_SFP_RXLOS_MASK: reg = CPLD_SFP_RXLOS_MASK_REG; break; case CPLD_QSFPDD_MOD_INT_EVENT_G0: reg = CPLD_QSFPDD_MOD_INT_EVENT_G0_REG; break; case CPLD_QSFPDD_MOD_INT_EVENT_G1: reg = CPLD_QSFPDD_MOD_INT_EVENT_G1_REG; break; case CPLD_QSFPDD_MOD_INT_EVENT_G2: reg = CPLD_QSFPDD_MOD_INT_EVENT_G2_REG; break; case CPLD_QSFPDD_MOD_INT_EVENT_G3: reg = CPLD_QSFPDD_MOD_INT_EVENT_G3_REG; break; case CPLD_QSFPDD_PRES_EVENT_G0: reg = CPLD_QSFPDD_PRES_EVENT_G0_REG; break; case CPLD_QSFPDD_PRES_EVENT_G1: reg = CPLD_QSFPDD_PRES_EVENT_G1_REG; break; case CPLD_QSFPDD_PRES_EVENT_G2: reg = CPLD_QSFPDD_PRES_EVENT_G2_REG; break; case CPLD_QSFPDD_PRES_EVENT_G3: reg = CPLD_QSFPDD_PRES_EVENT_G3_REG; break; case CPLD_QSFPDD_FUSE_INT_EVENT_G0: reg = CPLD_QSFPDD_FUSE_INT_EVENT_G0_REG; break; case CPLD_QSFPDD_FUSE_INT_EVENT_G1: reg = CPLD_QSFPDD_FUSE_INT_EVENT_G1_REG; break; case CPLD_QSFPDD_FUSE_INT_EVENT_G2: reg = CPLD_QSFPDD_FUSE_INT_EVENT_G2_REG; break; case CPLD_QSFPDD_FUSE_INT_EVENT_G3: reg = CPLD_QSFPDD_FUSE_INT_EVENT_G3_REG; break; case CPLD_SFP_TXFAULT_EVENT: reg = CPLD_SFP_TXFAULT_EVENT_REG; break; case CPLD_SFP_ABS_EVENT: reg = CPLD_SFP_ABS_EVENT_REG; break; case CPLD_SFP_RXLOS_EVENT: reg = CPLD_SFP_RXLOS_EVENT_REG; break; case CPLD_QSFPDD_RESET_CTRL_G0: reg = CPLD_QSFPDD_RESET_CTRL_G0_REG; break; case CPLD_QSFPDD_RESET_CTRL_G1: reg = CPLD_QSFPDD_RESET_CTRL_G1_REG; break; case CPLD_QSFPDD_RESET_CTRL_G2: reg = CPLD_QSFPDD_RESET_CTRL_G2_REG; break; case CPLD_QSFPDD_RESET_CTRL_G3: reg = CPLD_QSFPDD_RESET_CTRL_G3_REG; break; case CPLD_QSFPDD_LP_MODE_G0: reg = CPLD_QSFPDD_LP_MODE_G0_REG; break; case CPLD_QSFPDD_LP_MODE_G1: reg = CPLD_QSFPDD_LP_MODE_G1_REG; break; case CPLD_QSFPDD_LP_MODE_G2: reg = CPLD_QSFPDD_LP_MODE_G2_REG; break; case CPLD_QSFPDD_LP_MODE_G3: reg = CPLD_QSFPDD_LP_MODE_G3_REG; break; case CPLD_SFP_TX_DIS: reg = CPLD_SFP_TX_DIS_REG; break; case CPLD_SFP_RS: reg = CPLD_SFP_RS_REG; break; case CPLD_SFP_TS: reg = CPLD_SFP_TS_REG; break; case CPLD_PORT_INT_STATUS: reg = CPLD_PORT_INT_STATUS_REG; break; default: return -EINVAL; } return read_cpld_reg(dev, buf, reg); } /* handle read for hw_rev attributes */ static ssize_t read_hw_rev_cb(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); u8 reg = CPLD_HW_REV_REG; u8 reg_val = 0; int errno = 0; u8 res; if (!read_cpld_reg_raw_byte(dev, reg, ®_val, &errno)) return errno; switch (attr->index) { case CPLD_HW_REV: HW_REV_GET(reg_val, res); break; case CPLD_DEPH_REV: DEPH_REV_GET(reg_val, res); break; case CPLD_BUILD_REV: BUILD_REV_GET(reg_val, res); break; case CPLD_ID_TYPE: ID_TYPE_GET(reg_val, res); break; default: return -EINVAL; } return sprintf(buf, "0x%02x\n", res); } /* handle read for cpld_version attributes */ static ssize_t read_cpld_version_cb(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); u8 reg = CPLD_VERSION_REG; u8 reg_val = 0; int errno = 0; u8 res; if (!read_cpld_reg_raw_byte(dev, reg, ®_val, &errno)) return errno; switch (attr->index) { case CPLD_MAJOR_VER: CPLD_MAJOR_VERSION_GET(reg_val, res); break; case CPLD_MINOR_VER: CPLD_MINOR_VERSION_GET(reg_val, res); break; default: return -EINVAL; } return sprintf(buf, "0x%02x\n", res); } /* handle read human-readable string for cpld_version attributes */ static ssize_t read_cpld_version_h_cb(struct device *dev, struct device_attribute *da, char *buf) { u8 reg = CPLD_VERSION_REG; u8 reg_val = 0; int errno = 0; u8 major, minor, build; //get major/minor register value if(!read_cpld_reg_raw_byte(dev, reg, ®_val, &errno)) return errno; CPLD_MAJOR_VERSION_GET(reg_val, major); CPLD_MINOR_VERSION_GET(reg_val, minor); //get build register value reg = CPLD_BUILD_VER_REG; if(!read_cpld_reg_raw_byte(dev, reg, &build, &errno)) return errno; //version string format : xx.xx.xxx return sprintf(buf, "%d.%02d.%03d\n", major, minor, build); } /* handle write for attributes */ static ssize_t write_cpld_callback(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); u8 reg = 0; switch (attr->index) { case CPLD_MAC_INTR_MASK: reg = CPLD_MAC_INTR_MASK_REG; break; case CPLD_10G_PHY_INTR_MASK: reg = CPLD_10G_PHY_INTR_MASK_REG; break; case CPLD_CPLD_FRU_INTR_MASK: reg = CPLD_CPLD_FRU_INTR_MASK_REG; break; case CPLD_THERMAL_ALERT_INTR_MASK: reg = CPLD_THERMAL_ALERT_INTR_MASK_REG; break; case CPLD_MISC_INTR_MASK: reg = CPLD_MISC_INTR_MASK_REG; break; case CPLD_MAC_RST: reg = CPLD_MAC_RST_REG; break; case CPLD_10G_PHY_RST: reg = CPLD_10G_PHY_RST_REG; break; case CPLD_BMC_RST: reg = CPLD_BMC_RST_REG; break; case CPLD_USB_RST: reg = CPLD_USB_RST_REG; break; case CPLD_MUX_RST: reg = CPLD_MUX_RST_REG; break; case CPLD_MISC_RST: reg = CPLD_MISC_RST_REG; break; case CPLD_BMC_WATCHDOG: reg = CPLD_BMC_WATCHDOG_REG; break; case CPLD_MUX_CTRL: reg = CPLD_MUX_CTRL_REG; break; case CPLD_MAC_QSFP_SEL_CTRL: reg = CPLD_MAC_QSFP_SEL_CTRL_REG; break; case CPLD_BEACON_LED_CTRL: reg = CPLD_BEACON_LED_CTRL_REG; break; case CPLD_PORT_LED_CLR_CTRL: reg = CPLD_PORT_LED_CLR_CTRL_REG; break; case CPLD_EVENT_DETECT_CTRL: reg = CPLD_EVENT_DETECT_CTRL_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G0: reg = CPLD_QSFPDD_MOD_INT_MASK_G0_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G1: reg = CPLD_QSFPDD_MOD_INT_MASK_G1_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G2: reg = CPLD_QSFPDD_MOD_INT_MASK_G2_REG; break; case CPLD_QSFPDD_MOD_INT_MASK_G3: reg = CPLD_QSFPDD_MOD_INT_MASK_G3_REG; break; case CPLD_QSFPDD_PRES_MASK_G0: reg = CPLD_QSFPDD_PRES_MASK_G0_REG; break; case CPLD_QSFPDD_PRES_MASK_G1: reg = CPLD_QSFPDD_PRES_MASK_G1_REG; break; case CPLD_QSFPDD_PRES_MASK_G2: reg = CPLD_QSFPDD_PRES_MASK_G2_REG; break; case CPLD_QSFPDD_PRES_MASK_G3: reg = CPLD_QSFPDD_PRES_MASK_G3_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G0: reg = CPLD_QSFPDD_FUSE_INT_MASK_G0_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G1: reg = CPLD_QSFPDD_FUSE_INT_MASK_G1_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G2: reg = CPLD_QSFPDD_FUSE_INT_MASK_G2_REG; break; case CPLD_QSFPDD_FUSE_INT_MASK_G3: reg = CPLD_QSFPDD_FUSE_INT_MASK_G3_REG; break; case CPLD_SFP_TXFAULT_MASK: reg = CPLD_SFP_TXFAULT_MASK_REG; break; case CPLD_SFP_ABS_MASK: reg = CPLD_SFP_ABS_MASK_REG; break; case CPLD_SFP_RXLOS_MASK: reg = CPLD_SFP_RXLOS_MASK_REG; break; case CPLD_QSFPDD_RESET_CTRL_G0: reg = CPLD_QSFPDD_RESET_CTRL_G0_REG; break; case CPLD_QSFPDD_RESET_CTRL_G1: reg = CPLD_QSFPDD_RESET_CTRL_G1_REG; break; case CPLD_QSFPDD_RESET_CTRL_G2: reg = CPLD_QSFPDD_RESET_CTRL_G2_REG; break; case CPLD_QSFPDD_RESET_CTRL_G3: reg = CPLD_QSFPDD_RESET_CTRL_G3_REG; break; case CPLD_QSFPDD_LP_MODE_G0: reg = CPLD_QSFPDD_LP_MODE_G0_REG; break; case CPLD_QSFPDD_LP_MODE_G1: reg = CPLD_QSFPDD_LP_MODE_G1_REG; break; case CPLD_QSFPDD_LP_MODE_G2: reg = CPLD_QSFPDD_LP_MODE_G2_REG; break; case CPLD_QSFPDD_LP_MODE_G3: reg = CPLD_QSFPDD_LP_MODE_G3_REG; break; case CPLD_SFP_TX_DIS: reg = CPLD_SFP_TX_DIS_REG; break; case CPLD_SFP_RS: reg = CPLD_SFP_RS_REG; break; case CPLD_SFP_TS: reg = CPLD_SFP_TS_REG; break; default: return -EINVAL; } return write_cpld_reg(dev, buf, count, reg); } /* set cpld register value */ static ssize_t write_cpld_reg(struct device *dev, const char *buf, size_t count, u8 reg) { struct i2c_client *client = to_i2c_client(dev); struct cpld_data *data = i2c_get_clientdata(client); u8 reg_val; int ret; if (kstrtou8(buf, 0, ®_val) < 0) return -EINVAL; I2C_WRITE_BYTE_DATA(ret, &data->access_lock, client, reg, reg_val); if (unlikely(ret < 0)) { dev_err(dev, "I2C_WRITE_BYTE_DATA error, return=%d\n", ret); return ret; } return count; } /* add valid cpld client to list */ static void s9300_cpld_add_client(struct i2c_client *client) { struct cpld_client_node *node = NULL; node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); if (!node) { dev_info(&client->dev, "Can't allocate cpld_client_node for index %d\n", client->addr); return; } node->client = client; mutex_lock(&list_lock); list_add(&node->list, &cpld_client_list); mutex_unlock(&list_lock); } /* remove exist cpld client in list */ static void s9300_cpld_remove_client(struct i2c_client *client) { struct list_head *list_node = NULL; struct cpld_client_node *cpld_node = NULL; int found = 0; mutex_lock(&list_lock); list_for_each(list_node, &cpld_client_list) { cpld_node = list_entry(list_node, struct cpld_client_node, list); if (cpld_node->client == client) { found = 1; break; } } if (found) { list_del(list_node); kfree(cpld_node); } mutex_unlock(&list_lock); } /* cpld drvier probe */ static int s9300_cpld_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { int status; struct cpld_data *data = NULL; int ret = -EPERM; int idx; data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); if (!data) return -ENOMEM; /* init cpld data for client */ i2c_set_clientdata(client, data); mutex_init(&data->access_lock); if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_info(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); status = -EIO; goto exit; } /* get cpld id from device */ ret = i2c_smbus_read_byte_data(client, CPLD_ID_REG); if (ret < 0) { dev_info(&client->dev, "fail to get cpld id (0x%x) at addr (0x%x)\n", CPLD_ID_REG, client->addr); status = -EIO; goto exit; } CPLD_ID_ID_GET(ret, idx); if (INVALID(idx, cpld1, cpld3)) { dev_info(&client->dev, "cpld id %d(device) not valid\n", idx); //status = -EPERM; //goto exit; } data->index = dev_id->driver_data; /* register sysfs hooks for different cpld group */ dev_info(&client->dev, "probe cpld with index %d\n", data->index); switch (data->index) { case cpld1: status = sysfs_create_group(&client->dev.kobj, &s9300_cpld1_group); break; case cpld2: status = sysfs_create_group(&client->dev.kobj, &s9300_cpld2_group); break; case cpld3: status = sysfs_create_group(&client->dev.kobj, &s9300_cpld3_group); break; default: status = -EINVAL; } if (status) goto exit; dev_info(&client->dev, "chip found\n"); /* add probe chip to client list */ s9300_cpld_add_client(client); return 0; exit: switch (data->index) { case cpld1: sysfs_remove_group(&client->dev.kobj, &s9300_cpld1_group); break; case cpld2: sysfs_remove_group(&client->dev.kobj, &s9300_cpld2_group); break; case cpld3: sysfs_remove_group(&client->dev.kobj, &s9300_cpld3_group); break; default: break; } return status; } /* cpld drvier remove */ static int s9300_cpld_remove(struct i2c_client *client) { struct cpld_data *data = i2c_get_clientdata(client); switch (data->index) { case cpld1: sysfs_remove_group(&client->dev.kobj, &s9300_cpld1_group); break; case cpld2: sysfs_remove_group(&client->dev.kobj, &s9300_cpld2_group); break; case cpld3: sysfs_remove_group(&client->dev.kobj, &s9300_cpld3_group); break; } s9300_cpld_remove_client(client); return 0; } MODULE_DEVICE_TABLE(i2c, s9300_cpld_id); static struct i2c_driver s9300_cpld_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "x86_64_ufispace_s9300_32d_cpld", }, .probe = s9300_cpld_probe, .remove = s9300_cpld_remove, .id_table = s9300_cpld_id, .address_list = cpld_i2c_addr, }; /* provide cpld register read */ /* cpld_idx indicate the index of cpld device */ int s9300_cpld_read(u8 cpld_idx, u8 reg) { struct list_head *list_node = NULL; struct cpld_client_node *cpld_node = NULL; int ret = -EPERM; struct cpld_data *data; list_for_each(list_node, &cpld_client_list) { cpld_node = list_entry(list_node, struct cpld_client_node, list); data = i2c_get_clientdata(cpld_node->client); if (data->index == cpld_idx) { DEBUG_PRINT("cpld_idx=%d, read reg 0x%02x", cpld_idx, reg); I2C_READ_BYTE_DATA(ret, &data->access_lock, cpld_node->client, reg); DEBUG_PRINT("cpld_idx=%d, read reg 0x%02x = 0x%02x", cpld_idx, reg, ret); break; } } return ret; } EXPORT_SYMBOL(s9300_cpld_read); /* provide cpld register write */ /* cpld_idx indicate the index of cpld device */ int s9300_cpld_write(u8 cpld_idx, u8 reg, u8 value) { struct list_head *list_node = NULL; struct cpld_client_node *cpld_node = NULL; int ret = -EIO; struct cpld_data *data; list_for_each(list_node, &cpld_client_list) { cpld_node = list_entry(list_node, struct cpld_client_node, list); data = i2c_get_clientdata(cpld_node->client); if (data->index == cpld_idx) { I2C_WRITE_BYTE_DATA(ret, &data->access_lock, cpld_node->client, reg, value); DEBUG_PRINT("cpld_idx=%d, write reg 0x%02x val 0x%02x, ret=%d", cpld_idx, reg, value, ret); break; } } return ret; } EXPORT_SYMBOL(s9300_cpld_write); static int __init s9300_cpld_init(void) { mutex_init(&list_lock); return i2c_add_driver(&s9300_cpld_driver); } static void __exit s9300_cpld_exit(void) { i2c_del_driver(&s9300_cpld_driver); } MODULE_AUTHOR("Leo Lin "); MODULE_DESCRIPTION("x86_64_ufispace_s9300_cpld driver"); MODULE_LICENSE("GPL"); module_init(s9300_cpld_init); module_exit(s9300_cpld_exit);