/* An hwmon driver for Cameo ESC602-32Q Innovium i2c Module */ #pragma GCC diagnostic ignored "-Wformat-zero-length" #include "x86-64-cameo-esc602-32q.h" #include "x86-64-cameo-esc602-32q-common.h" /* Addresses scanned */ static const unsigned short normal_i2c[] = { 0x30, 0x31, 0x32, I2C_CLIENT_END }; static int debug = 0; /* i2c_client Declaration */ struct i2c_client *Cameo_CPLD_30_client; //0x30 for SYS CPLD struct i2c_client *Cameo_CPLD_31_client; //0x31 for Port 01-16 struct i2c_client *Cameo_CPLD_32_client; //0x32 for Port 17-32 struct i2c_client *Cameo_CPLD_23_client; //0x23 for Fan CPLD struct i2c_client *Cameo_CPLD_35_client; //0x35 for Power CPLD struct i2c_client *Cameo_BMC_14_client; //0x14 for BMC slave /* end of i2c_client Declaration */ /* register offset define */ #define BMC_EN_REG 0xA4 /* end of register offset define */ /* common function */ int bmc_enable(void) { if ((i2c_smbus_read_byte_data(Cameo_CPLD_30_client, BMC_EN_REG) & BIT_0_MASK) == 0x01) { return ENABLE; } else { return DISABLE; } } int read_8bit_temp(u8 sign,u8 value) { int result = 0; if(sign) { //printf("read_8bit_temp UP %d\n", value & 0x80); value = ~(value)+1; result = value; return result; } else { //printf("read_8bit_temp DOWN %d\n", value & 0x80); result = value; return result; } } /* end of common function*/ static int Cameo_i2c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { struct Cameo_i2c_data *Cameo_CPLD_30_data; struct Cameo_i2c_data *Cameo_CPLD_31_data; struct Cameo_i2c_data *Cameo_CPLD_32_data; struct Cameo_i2c_data *Cameo_CPLD_23_data; struct Cameo_i2c_data *Cameo_CPLD_35_data; struct Cameo_i2c_data *Cameo_BMC_14_data; int status; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) { status = -EIO; goto exit; } Cameo_CPLD_30_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_CPLD_30_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } Cameo_CPLD_31_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_CPLD_31_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } Cameo_CPLD_32_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_CPLD_32_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } Cameo_CPLD_23_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_CPLD_23_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } Cameo_CPLD_35_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_CPLD_35_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } Cameo_BMC_14_data = kzalloc(sizeof(struct Cameo_i2c_data), GFP_KERNEL); if (!Cameo_BMC_14_data) { printk(KERN_ALERT "kzalloc fail\n"); status = -ENOMEM; goto exit; } i2c_set_clientdata(Cameo_CPLD_30_client, Cameo_CPLD_30_data); i2c_set_clientdata(Cameo_CPLD_31_client, Cameo_CPLD_31_data); i2c_set_clientdata(Cameo_CPLD_32_client, Cameo_CPLD_32_data); i2c_set_clientdata(Cameo_CPLD_23_client, Cameo_CPLD_23_data); i2c_set_clientdata(Cameo_CPLD_35_client, Cameo_CPLD_35_data); i2c_set_clientdata(Cameo_BMC_14_client , Cameo_BMC_14_data); mutex_init(&Cameo_CPLD_30_data->update_lock); mutex_init(&Cameo_CPLD_31_data->update_lock); mutex_init(&Cameo_CPLD_32_data->update_lock); mutex_init(&Cameo_CPLD_23_data->update_lock); mutex_init(&Cameo_CPLD_35_data->update_lock); mutex_init(&Cameo_BMC_14_data->update_lock); Cameo_CPLD_30_data->valid = 0; mutex_init(&Cameo_CPLD_30_data->update_lock); dev_info(&client->dev, "chip found\n"); /* Register sysfs hooks */ status = sysfs_create_group(&client->dev.kobj, &ESC602_SYS_group); if (status) { goto exit_free; } status = sysfs_create_group(&client->dev.kobj, &ESC602_LED_group); if (status) { goto exit_free; } status = sysfs_create_group(&client->dev.kobj, &ESC602_FAN_group); if (status) { goto exit_free; } status = sysfs_create_group(&client->dev.kobj, &ESC602_THERMAL_group); if (status) { goto exit_free; } status = sysfs_create_group(&client->dev.kobj, &ESC602_POWER_group); if (status) { goto exit_free; } status = sysfs_create_group(&client->dev.kobj, &ESC602_QSFP_group); if (status) { goto exit_free; } Cameo_CPLD_30_data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(Cameo_CPLD_30_data->hwmon_dev)) { status = PTR_ERR(Cameo_CPLD_30_data->hwmon_dev); goto exit_remove; } dev_info(&client->dev, "%s: '%s'\n", dev_name(Cameo_CPLD_30_data->hwmon_dev), client->name); return 0; exit_remove: sysfs_remove_group(&client->dev.kobj, &ESC602_SYS_group); sysfs_remove_group(&client->dev.kobj, &ESC602_LED_group); sysfs_remove_group(&client->dev.kobj, &ESC602_FAN_group); sysfs_remove_group(&client->dev.kobj, &ESC602_THERMAL_group); sysfs_remove_group(&client->dev.kobj, &ESC602_POWER_group); sysfs_remove_group(&client->dev.kobj, &ESC602_QSFP_group); exit_free: kfree(Cameo_CPLD_30_data); exit: return status; } static int Cameo_i2c_remove(struct i2c_client *client) { struct Cameo_i2c_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &ESC602_SYS_group); sysfs_remove_group(&client->dev.kobj, &ESC602_LED_group); sysfs_remove_group(&client->dev.kobj, &ESC602_FAN_group); sysfs_remove_group(&client->dev.kobj, &ESC602_THERMAL_group); sysfs_remove_group(&client->dev.kobj, &ESC602_POWER_group); sysfs_remove_group(&client->dev.kobj, &ESC602_QSFP_group); kfree(data); return 0; } static const struct i2c_device_id Cameo_i2c_id[] = { { "Cameo_CPLD_30", 0 }, {}, }; MODULE_DEVICE_TABLE(i2c, Cameo_i2c_id); static struct i2c_driver Cameo_i2c_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "ESC_602_i2c", }, .probe = Cameo_i2c_probe, .remove = Cameo_i2c_remove, .id_table = Cameo_i2c_id, .address_list = normal_i2c, }; /*For main Switch board*/ static struct i2c_board_info Cameo_CPLD_30_info[] __initdata = { { I2C_BOARD_INFO("Cameo_CPLD_30", 0x30), .platform_data = NULL, }, }; /*For QSFP Port 01 - 16*/ static struct i2c_board_info Cameo_CPLD_31_info[] __initdata = { { I2C_BOARD_INFO("Cameo_CPLD_31", 0x31), .platform_data = NULL, }, }; /*For QSFP Port 17 - 32*/ static struct i2c_board_info Cameo_CPLD_32_info[] __initdata = { { I2C_BOARD_INFO("Cameo_CPLD_32", 0x32), .platform_data = NULL, }, }; /*For Fan status*/ static struct i2c_board_info Cameo_CPLD_23_info[] __initdata = { { I2C_BOARD_INFO("Cameo_CPLD_23", 0x23), .platform_data = NULL, }, }; /*For Power status*/ static struct i2c_board_info Cameo_CPLD_35_info[] __initdata = { { I2C_BOARD_INFO("Cameo_CPLD_35", 0x35), .platform_data = NULL, }, }; /*For BMC Slave*/ static struct i2c_board_info Cameo_BMC_14_info[] __initdata = { { I2C_BOARD_INFO("Cameo_BMC_14", 0x14), .platform_data = NULL, }, }; static int __init Cameo_i2c_init(void) { int ret; int cmp; char keyword[] = "SMBus I801"; char buf1[128]; struct i2c_adapter *i2c_adap; struct file *fp; mm_segment_t fs; loff_t pos; printk("Open file...\n"); fp = filp_open("/sys/class/i2c-dev/i2c-0/name", O_RDONLY , 0644); if (IS_ERR(fp)) { printk("Open file FAILED\n"); return -1; } fs = get_fs(); set_fs(KERNEL_DS); pos = 0; vfs_read(fp, buf1, sizeof(buf1), &pos); printk("Detect %s\n", buf1); cmp = strncmp(keyword, buf1, sizeof(keyword)-1); set_fs(fs); filp_close(fp, NULL); if(cmp == 0) { i2c_adap = i2c_get_adapter(0); printk("SMBus I801 is at bus 0\n"); } else { i2c_adap = i2c_get_adapter(1); printk("SMBus I801 is at bus 1\n"); } if (i2c_adap == NULL) { printk("ERROR: i2c_get_adapter FAILED!\n"); return -1; } Cameo_CPLD_30_client = i2c_new_device(i2c_adap, &Cameo_CPLD_30_info[0]); Cameo_CPLD_31_client = i2c_new_device(i2c_adap, &Cameo_CPLD_31_info[0]); Cameo_CPLD_32_client = i2c_new_device(i2c_adap, &Cameo_CPLD_32_info[0]); Cameo_CPLD_23_client = i2c_new_device(i2c_adap, &Cameo_CPLD_23_info[0]); Cameo_CPLD_35_client = i2c_new_device(i2c_adap, &Cameo_CPLD_35_info[0]); Cameo_BMC_14_client = i2c_new_device(i2c_adap, &Cameo_BMC_14_info[0]); if (Cameo_CPLD_30_info == NULL || Cameo_CPLD_31_info == NULL || Cameo_CPLD_32_info == NULL || Cameo_CPLD_23_info == NULL || Cameo_CPLD_35_info == NULL || Cameo_BMC_14_info == NULL) { printk("ERROR: i2c_new_device FAILED!\n"); return -1; } i2c_put_adapter(i2c_adap); ret = i2c_add_driver(&Cameo_i2c_driver); printk(KERN_ALERT "ESC602-32Q i2c Driver Version: %s\n", DRIVER_VERSION); printk(KERN_ALERT "ESC602-32Q i2c Driver INSTALL SUCCESS\n"); return ret; } static void __exit Cameo_i2c_exit(void) { i2c_unregister_device(Cameo_CPLD_30_client); i2c_unregister_device(Cameo_CPLD_31_client); i2c_unregister_device(Cameo_CPLD_32_client); i2c_unregister_device(Cameo_CPLD_23_client); i2c_unregister_device(Cameo_CPLD_35_client); i2c_unregister_device(Cameo_BMC_14_client); i2c_del_driver(&Cameo_i2c_driver); printk(KERN_ALERT "ESC602-32Q i2c Driver UNINSTALL SUCCESS\n"); } MODULE_AUTHOR("Cameo Inc."); MODULE_DESCRIPTION("Cameo ESC602-32Q i2c Driver"); MODULE_LICENSE("GPL"); module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Enable debugging (0-1)"); module_init(Cameo_i2c_init); module_exit(Cameo_i2c_exit);