diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/Makefile b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/Makefile new file mode 100644 index 00000000..003238cf --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/PKG.yml b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/PKG.yml new file mode 100644 index 00000000..44e45136 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules.yml PLATFORM=x86-64-accton-as6812-32x ARCH=amd64 KERNELS="onl-kernel-3.16-lts-x86-64-all:amd64" diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/.gitignore b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/.gitignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/Makefile b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/Makefile new file mode 100644 index 00000000..81d692dd --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/Makefile @@ -0,0 +1,5 @@ +KERNELS := onl-kernel-3.16-lts-x86-64-all:amd64 +KMODULES := $(wildcard *.c) +PLATFORM := x86-64-accton-as6812-32x +ARCH := x86_64 +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-cpld.c b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-cpld.c new file mode 100644 index 00000000..dafa5e1b --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-cpld.c @@ -0,0 +1,390 @@ +/* + * I2C multiplexer for accton as6812 CPLD + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as6812_32x CPLD1/CPLD2/CPLD3 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct dmi_system_id as6812_dmi_table[] = { + { + .ident = "Accton AS6812", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS6812"), + }, + }, + { + .ident = "Accton AS6812", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Accton"), + DMI_MATCH(DMI_PRODUCT_NAME, "AS6812"), + }, + }, +}; + +int platform_accton_as6812_32x(void) +{ + return dmi_check_system(as6812_dmi_table); +} +EXPORT_SYMBOL(platform_accton_as6812_32x); + +#define NUM_OF_CPLD1_CHANS 0x0 +#define NUM_OF_CPLD2_CHANS 0x10 +#define NUM_OF_CPLD3_CHANS 0x10 +#define NUM_OF_ALL_CPLD_CHANS (NUM_OF_CPLD2_CHANS + NUM_OF_CPLD3_CHANS) +#define ACCTON_I2C_CPLD_MUX_MAX_NCHANS NUM_OF_CPLD3_CHANS + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +enum cpld_mux_type { + as6812_32x_cpld2, + as6812_32x_cpld3, + as6812_32x_cpld1 +}; + +struct accton_i2c_cpld_mux { + enum cpld_mux_type type; + struct i2c_adapter *virt_adaps[ACCTON_I2C_CPLD_MUX_MAX_NCHANS]; + u8 last_chan; /* last register value */ +}; + +struct chip_desc { + u8 nchans; + u8 deselectChan; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [as6812_32x_cpld1] = { + .nchans = NUM_OF_CPLD1_CHANS, + .deselectChan = NUM_OF_CPLD1_CHANS, + }, + [as6812_32x_cpld2] = { + .nchans = NUM_OF_CPLD2_CHANS, + .deselectChan = NUM_OF_CPLD2_CHANS, + }, + [as6812_32x_cpld3] = { + .nchans = NUM_OF_CPLD3_CHANS, + .deselectChan = NUM_OF_CPLD3_CHANS, + } +}; + +static const struct i2c_device_id accton_i2c_cpld_mux_id[] = { + { "as6812_32x_cpld1", as6812_32x_cpld1 }, + { "as6812_32x_cpld2", as6812_32x_cpld2 }, + { "as6812_32x_cpld3", as6812_32x_cpld3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_mux_id); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int accton_i2c_cpld_mux_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + unsigned long orig_jiffies; + unsigned short flags; + union i2c_smbus_data data; + int try; + s32 res = -EIO; + + data.byte = val; + flags = client->flags; + flags &= I2C_M_TEN | I2C_CLIENT_PEC; + + if (adap->algo->smbus_xfer) { + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adap->retries; try++) { + res = adap->algo->smbus_xfer(adap, client->addr, flags, + I2C_SMBUS_WRITE, 0x2, + I2C_SMBUS_BYTE_DATA, &data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adap->timeout)) + break; + } + } + + return res; +} + +static int accton_i2c_cpld_mux_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct accton_i2c_cpld_mux *data = i2c_get_clientdata(client); + u8 regval; + int ret = 0; + regval = chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = accton_i2c_cpld_mux_reg_write(adap, client, regval); + data->last_chan = regval; + } + + return ret; +} + +static int accton_i2c_cpld_mux_deselect_mux(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct accton_i2c_cpld_mux *data = i2c_get_clientdata(client); + + /* Deselect active channel */ + data->last_chan = chips[data->type].deselectChan; + + return accton_i2c_cpld_mux_reg_write(adap, client, data->last_chan); +} + +static void accton_i2c_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void accton_i2c_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); +} + +static ssize_t show_cpld_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 reg = 0x1; + struct i2c_client *client; + int len; + + client = to_i2c_client(dev); + len = sprintf(buf, "%d", i2c_smbus_read_byte_data(client, reg)); + + return len; +} + +static struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL); + +/* + * I2C init/probing/exit functions + */ +static int accton_i2c_cpld_mux_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + int chan=0; + struct accton_i2c_cpld_mux *data; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto err; + + data = kzalloc(sizeof(struct accton_i2c_cpld_mux), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + data->type = id->driver_data; + + if (data->type == as6812_32x_cpld2 || data->type == as6812_32x_cpld3) { + data->last_chan = chips[data->type].deselectChan; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (chan = 0; chan < chips[data->type].nchans; chan++) { + data->virt_adaps[chan] = i2c_add_mux_adapter(adap, &client->dev, client, 0, chan, + I2C_CLASS_HWMON | I2C_CLASS_SPD, + accton_i2c_cpld_mux_select_chan, + accton_i2c_cpld_mux_deselect_mux); + + if (data->virt_adaps[chan] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, "failed to register multiplexed adapter %d\n", chan); + goto virt_reg_failed; + } + } + + dev_info(&client->dev, "registered %d multiplexed busses for I2C mux %s\n", + chan, client->name); + } + + accton_i2c_cpld_add_client(client); + + ret = sysfs_create_file(&client->dev.kobj, &ver.attr); + if (ret) + goto virt_reg_failed; + + return 0; + +virt_reg_failed: + for (chan--; chan >= 0; chan--) { + i2c_del_mux_adapter(data->virt_adaps[chan]); + } + kfree(data); +err: + return ret; +} + +static int accton_i2c_cpld_mux_remove(struct i2c_client *client) +{ + struct accton_i2c_cpld_mux *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + int chan; + + sysfs_remove_file(&client->dev.kobj, &ver.attr); + + for (chan = 0; chan < chip->nchans; ++chan) { + if (data->virt_adaps[chan]) { + i2c_del_mux_adapter(data->virt_adaps[chan]); + data->virt_adaps[chan] = NULL; + } + } + + kfree(data); + accton_i2c_cpld_remove_client(client); + + return 0; +} + +int as6812_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + 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->addr == cpld_addr) { + ret = i2c_smbus_read_byte_data(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as6812_32x_i2c_cpld_read); + +int as6812_32x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + 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->addr == cpld_addr) { + ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as6812_32x_i2c_cpld_write); + +static struct i2c_driver accton_i2c_cpld_mux_driver = { + .driver = { + .name = "as6812_32x_cpld", + .owner = THIS_MODULE, + }, + .probe = accton_i2c_cpld_mux_probe, + .remove = accton_i2c_cpld_mux_remove, + .id_table = accton_i2c_cpld_mux_id, +}; + +static int __init accton_i2c_cpld_mux_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&accton_i2c_cpld_mux_driver); +} + +static void __exit accton_i2c_cpld_mux_exit(void) +{ + i2c_del_driver(&accton_i2c_cpld_mux_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("Accton I2C CPLD mux driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_i2c_cpld_mux_init); +module_exit(accton_i2c_cpld_mux_exit); diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-fan.c b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-fan.c new file mode 100644 index 00000000..f0555674 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-fan.c @@ -0,0 +1,434 @@ +/* + * A hwmon driver for the Accton as6812 32x fan contrl + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * 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 +#include +#include +#include + +#define FAN_MAX_NUMBER 5 +#define FAN_SPEED_CPLD_TO_RPM_STEP 150 +#define FAN_SPEED_PRECENT_TO_CPLD_STEP 5 +#define FAN_DUTY_CYCLE_MIN 0 +#define FAN_DUTY_CYCLE_MAX 100 /* 100% */ + +#define CPLD_REG_FAN_STATUS_OFFSET 0xC +#define CPLD_REG_FANR_STATUS_OFFSET 0x17 +#define CPLD_REG_FAN_DIRECTION_OFFSET 0x1E + +#define CPLD_FAN1_REG_SPEED_OFFSET 0x10 +#define CPLD_FAN2_REG_SPEED_OFFSET 0x11 +#define CPLD_FAN3_REG_SPEED_OFFSET 0x12 +#define CPLD_FAN4_REG_SPEED_OFFSET 0x13 +#define CPLD_FAN5_REG_SPEED_OFFSET 0x14 + +#define CPLD_FANR1_REG_SPEED_OFFSET 0x18 +#define CPLD_FANR2_REG_SPEED_OFFSET 0x19 +#define CPLD_FANR3_REG_SPEED_OFFSET 0x1A +#define CPLD_FANR4_REG_SPEED_OFFSET 0x1B +#define CPLD_FANR5_REG_SPEED_OFFSET 0x1C + +#define CPLD_REG_FAN_PWM_CYCLE_OFFSET 0xD + +#define CPLD_FAN1_INFO_BIT_MASK 0x1 +#define CPLD_FAN2_INFO_BIT_MASK 0x2 +#define CPLD_FAN3_INFO_BIT_MASK 0x4 +#define CPLD_FAN4_INFO_BIT_MASK 0x8 +#define CPLD_FAN5_INFO_BIT_MASK 0x10 + +#define PROJECT_NAME + +#define DEBUG_MODE 0 + +#if (DEBUG_MODE == 1) + #define DEBUG_PRINT(format, ...) printk(format, __VA_ARGS__) +#else + #define DEBUG_PRINT(format, ...) +#endif + +static struct accton_as6812_32x_fan *fan_data = NULL; + +struct accton_as6812_32x_fan { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[FAN_MAX_NUMBER]; /* inner first fan status */ + u32 speed[FAN_MAX_NUMBER]; /* inner first fan speed */ + u8 direction[FAN_MAX_NUMBER]; /* reconrd the direction of inner first and second fans */ + u32 duty_cycle[FAN_MAX_NUMBER]; /* control the speed of inner first and second fans */ + u8 r_status[FAN_MAX_NUMBER]; /* inner second fan status */ + u32 r_speed[FAN_MAX_NUMBER]; /* inner second fan speed */ +}; + +/*******************/ +#define MAKE_FAN_MASK_OR_REG(name,type) \ + CPLD_FAN##type##1_##name, \ + CPLD_FAN##type##2_##name, \ + CPLD_FAN##type##3_##name, \ + CPLD_FAN##type##4_##name, \ + CPLD_FAN##type##5_##name, + +/* fan related data + */ +static const u8 fan_info_mask[] = { + MAKE_FAN_MASK_OR_REG(INFO_BIT_MASK,) +}; + +static const u8 fan_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,) +}; + +static const u8 fanr_speed_reg[] = { + MAKE_FAN_MASK_OR_REG(REG_SPEED_OFFSET,R) +}; + +/*******************/ +#define DEF_FAN_SET(id) \ + FAN##id##_FAULT, \ + FAN##id##_SPEED, \ + FAN##id##_DUTY_CYCLE, \ + FAN##id##_DIRECTION, \ + FANR##id##_FAULT, \ + FANR##id##_SPEED, + +enum sysfs_fan_attributes { + DEF_FAN_SET(1) + DEF_FAN_SET(2) + DEF_FAN_SET(3) + DEF_FAN_SET(4) + DEF_FAN_SET(5) +}; +/*******************/ +static void accton_as6812_32x_fan_update_device(struct device *dev); +static int accton_as6812_32x_fan_read_value(u8 reg); +static int accton_as6812_32x_fan_write_value(u8 reg, u8 value); + +static ssize_t fan_set_duty_cycle(struct device *dev, + struct device_attribute *da,const char *buf, size_t count); +static ssize_t fan_show_value(struct device *dev, + struct device_attribute *da, char *buf); + +extern int as6812_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as6812_32x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + + +/*******************/ +#define _MAKE_SENSOR_DEVICE_ATTR(prj, id) \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_fault, S_IRUGO, fan_show_value, NULL, FAN##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##id##_SPEED); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, \ + fan_set_duty_cycle, FAN##id##_DUTY_CYCLE); \ + static SENSOR_DEVICE_ATTR(prj##fan##id##_direction, S_IRUGO, fan_show_value, NULL, FAN##id##_DIRECTION); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_fault, S_IRUGO, fan_show_value, NULL, FANR##id##_FAULT); \ + static SENSOR_DEVICE_ATTR(prj##fanr##id##_speed_rpm, S_IRUGO, fan_show_value, NULL, FANR##id##_SPEED); + +#define MAKE_SENSOR_DEVICE_ATTR(prj,id) _MAKE_SENSOR_DEVICE_ATTR(prj,id) + +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 1) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 2) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 3) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 4) +MAKE_SENSOR_DEVICE_ATTR(PROJECT_NAME, 5) +/*******************/ + +#define _MAKE_FAN_ATTR(prj, id) \ + &sensor_dev_attr_##prj##fan##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_speed_rpm.dev_attr.attr, \ + &sensor_dev_attr_##prj##fan##id##_duty_cycle_percentage.dev_attr.attr,\ + &sensor_dev_attr_##prj##fan##id##_direction.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_fault.dev_attr.attr, \ + &sensor_dev_attr_##prj##fanr##id##_speed_rpm.dev_attr.attr, + +#define MAKE_FAN_ATTR(prj, id) _MAKE_FAN_ATTR(prj, id) + +static struct attribute *accton_as6812_32x_fan_attributes[] = { + /* fan related attributes */ + MAKE_FAN_ATTR(PROJECT_NAME,1) + MAKE_FAN_ATTR(PROJECT_NAME,2) + MAKE_FAN_ATTR(PROJECT_NAME,3) + MAKE_FAN_ATTR(PROJECT_NAME,4) + MAKE_FAN_ATTR(PROJECT_NAME,5) + NULL +}; +/*******************/ + +/* fan related functions + */ +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + ssize_t ret = 0; + int data_index, type_index; + + accton_as6812_32x_fan_update_device(dev); + + if (fan_data->valid == 0) { + return ret; + } + + type_index = attr->index%FAN2_FAULT; + data_index = attr->index/FAN2_FAULT; + + switch (type_index) { + case FAN1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->status[data_index]); + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->speed[data_index]); + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DUTY_CYCLE: + ret = sprintf(buf, "%d\n", fan_data->duty_cycle[data_index]); + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FAN1_DIRECTION: + ret = sprintf(buf, "%d\n", fan_data->direction[data_index]); /* presnet, need to modify*/ + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_FAULT: + ret = sprintf(buf, "%d\n", fan_data->r_status[data_index]); + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + case FANR1_SPEED: + ret = sprintf(buf, "%d\n", fan_data->r_speed[data_index]); + DEBUG_PRINT("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); + break; + default: + DEBUG_PRINT("[Check !!][%s][%d] \n", __FUNCTION__, __LINE__); + break; + } + + return ret; +} +/*******************/ +static ssize_t fan_set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) { + + int error, value; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + if (value < FAN_DUTY_CYCLE_MIN || value > FAN_DUTY_CYCLE_MAX) + return -EINVAL; + + accton_as6812_32x_fan_write_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET, value/FAN_SPEED_PRECENT_TO_CPLD_STEP); + + fan_data->valid = 0; + + return count; +} + +static const struct attribute_group accton_as6812_32x_fan_group = { + .attrs = accton_as6812_32x_fan_attributes, +}; + +static int accton_as6812_32x_fan_read_value(u8 reg) +{ + return as6812_32x_i2c_cpld_read(0x60, reg); +} + +static int accton_as6812_32x_fan_write_value(u8 reg, u8 value) +{ + return as6812_32x_i2c_cpld_write(0x60, reg, value); +} + +static void accton_as6812_32x_fan_update_device(struct device *dev) +{ + int speed, r_speed, fault, r_fault, direction, ctrl_speed; + int i; + + mutex_lock(&fan_data->update_lock); + + DEBUG_PRINT("Starting accton_as6812_32x_fan update \n"); + + if (!(time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) || !fan_data->valid)) { + /* do nothing */ + goto _exit; + } + + fan_data->valid = 0; + + DEBUG_PRINT("Starting accton_as6812_32x_fan update 2 \n"); + + fault = accton_as6812_32x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); + r_fault = accton_as6812_32x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); + direction = accton_as6812_32x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); + ctrl_speed = accton_as6812_32x_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); + + if ( (fault < 0) || (r_fault < 0) || (ctrl_speed < 0) ) + { + DEBUG_PRINT("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + DEBUG_PRINT("[fan:] fault:%d, r_fault=%d, ctrl_speed=%d \n",fault, r_fault, ctrl_speed); + + for (i = 0; i < FAN_MAX_NUMBER; i++) + { + /* Update fan data + */ + + /* fan fault + * 0: normal, 1:abnormal + * Each FAN-tray module has two fans. + */ + fan_data->status[i] = (fault & fan_info_mask[i]) >> i; + DEBUG_PRINT("[fan%d:] fail=%d \n",i, fan_data->status[i]); + + fan_data->r_status[i] = (r_fault & fan_info_mask[i]) >> i; + fan_data->direction[i] = (direction & fan_info_mask[i]) >> i; + fan_data->duty_cycle[i] = ctrl_speed * FAN_SPEED_PRECENT_TO_CPLD_STEP; + + /* fan speed + */ + speed = accton_as6812_32x_fan_read_value(fan_speed_reg[i]); + r_speed = accton_as6812_32x_fan_read_value(fanr_speed_reg[i]); + if ( (speed < 0) || (r_speed < 0) ) + { + DEBUG_PRINT("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); + goto _exit; /* error */ + } + + DEBUG_PRINT("[fan%d:] speed:%d, r_speed=%d \n", i, speed, r_speed); + + fan_data->speed[i] = speed * FAN_SPEED_CPLD_TO_RPM_STEP; + fan_data->r_speed[i] = r_speed * FAN_SPEED_CPLD_TO_RPM_STEP; + } + + /* finish to update */ + fan_data->last_updated = jiffies; + fan_data->valid = 1; + +_exit: + mutex_unlock(&fan_data->update_lock); +} + +static int accton_as6812_32x_fan_probe(struct platform_device *pdev) +{ + int status = -1; + + /* Register sysfs hooks */ + status = sysfs_create_group(&pdev->dev.kobj, &accton_as6812_32x_fan_group); + if (status) { + goto exit; + + } + + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + status = PTR_ERR(fan_data->hwmon_dev); + goto exit_remove; + } + + dev_info(&pdev->dev, "accton_as6812_32x_fan\n"); + + return 0; + +exit_remove: + sysfs_remove_group(&pdev->dev.kobj, &accton_as6812_32x_fan_group); +exit: + return status; +} + +static int accton_as6812_32x_fan_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(fan_data->hwmon_dev); + sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as6812_32x_fan_group); + + return 0; +} + +#define DRVNAME "as6812_32x_fan" + +static struct platform_driver accton_as6812_32x_fan_driver = { + .probe = accton_as6812_32x_fan_probe, + .remove = accton_as6812_32x_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as6812_32x_fan_init(void) +{ + int ret; + + extern int platform_accton_as6812_32x(void); + if(!platform_accton_as6812_32x()) { + return -ENODEV; + } + + ret = platform_driver_register(&accton_as6812_32x_fan_driver); + if (ret < 0) { + goto exit; + } + + fan_data = kzalloc(sizeof(struct accton_as6812_32x_fan), GFP_KERNEL); + if (!fan_data) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as6812_32x_fan_driver); + goto exit; + } + + mutex_init(&fan_data->update_lock); + fan_data->valid = 0; + + fan_data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(fan_data->pdev)) { + ret = PTR_ERR(fan_data->pdev); + platform_driver_unregister(&accton_as6812_32x_fan_driver); + kfree(fan_data); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as6812_32x_fan_exit(void) +{ + platform_device_unregister(fan_data->pdev); + platform_driver_unregister(&accton_as6812_32x_fan_driver); + kfree(fan_data); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as6812_32x_fan driver"); +MODULE_LICENSE("GPL"); + +module_init(accton_as6812_32x_fan_init); +module_exit(accton_as6812_32x_fan_exit); + diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-leds.c b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-leds.c new file mode 100644 index 00000000..fd54ce06 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-leds.c @@ -0,0 +1,617 @@ +/* + * A LED driver for the accton_as6812_32x_led + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include + +extern int as6812_32x_i2c_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as6812_32x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern void led_classdev_resume(struct led_classdev *led_cdev); +extern void led_classdev_suspend(struct led_classdev *led_cdev); + +#define DRVNAME "as6812_32x_led" + +struct accton_as6812_32x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[4]; /* Register value, 0 = LOC/DIAG/FAN LED + 1 = PSU1/PSU2 LED + 2 = FAN1-4 LED + 3 = FAN5-6 LED */ +}; + +static struct accton_as6812_32x_led_data *ledctl = NULL; + +/* LED related data + */ +#define LED_TYPE_PSU1_REG_MASK 0x03 +#define LED_MODE_PSU1_GREEN_MASK 0x02 +#define LED_MODE_PSU1_AMBER_MASK 0x01 +#define LED_MODE_PSU1_OFF_MASK 0x03 +#define LED_MODE_PSU1_AUTO_MASK 0x00 + +#define LED_TYPE_PSU2_REG_MASK 0x0C +#define LED_MODE_PSU2_GREEN_MASK 0x08 +#define LED_MODE_PSU2_AMBER_MASK 0x04 +#define LED_MODE_PSU2_OFF_MASK 0x0C +#define LED_MODE_PSU2_AUTO_MASK 0x00 + +#define LED_TYPE_DIAG_REG_MASK 0x0C +#define LED_MODE_DIAG_GREEN_MASK 0x08 +#define LED_MODE_DIAG_AMBER_MASK 0x04 +#define LED_MODE_DIAG_OFF_MASK 0x0C +#define LED_MODE_DIAG_BLINK_MASK 0x48 + +#define LED_TYPE_FAN_REG_MASK 0x03 +#define LED_MODE_FAN_GREEN_MASK 0x02 +#define LED_MODE_FAN_AMBER_MASK 0x01 +#define LED_MODE_FAN_OFF_MASK 0x03 +#define LED_MODE_FAN_AUTO_MASK 0x00 + +#define LED_TYPE_FAN1_REG_MASK 0x03 +#define LED_TYPE_FAN2_REG_MASK 0xC0 +#define LED_TYPE_FAN3_REG_MASK 0x30 +#define LED_TYPE_FAN4_REG_MASK 0x0C +#define LED_TYPE_FAN5_REG_MASK 0x03 + +#define LED_MODE_FANX_GREEN_MASK 0x01 +#define LED_MODE_FANX_RED_MASK 0x02 +#define LED_MODE_FANX_OFF_MASK 0x00 + +#define LED_TYPE_LOC_REG_MASK 0x30 +#define LED_MODE_LOC_ON_MASK 0x00 +#define LED_MODE_LOC_OFF_MASK 0x10 +#define LED_MODE_LOC_BLINK_MASK 0x20 + +static const u8 led_reg[] = { + 0xA, /* LOC/DIAG/FAN LED*/ + 0xB, /* PSU1/PSU2 LED */ + 0xE, /* FAN2-5 LED */ + 0xF, /* FAN1 LED */ +}; + +enum led_type { + LED_TYPE_PSU1, + LED_TYPE_PSU2, + LED_TYPE_DIAG, + LED_TYPE_FAN, + LED_TYPE_FAN1, + LED_TYPE_FAN2, + LED_TYPE_FAN3, + LED_TYPE_FAN4, + LED_TYPE_FAN5, + LED_TYPE_LOC +}; + +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_GREEN, + LED_MODE_AMBER, + LED_MODE_RED, + LED_MODE_GREEN_BLINK, + LED_MODE_AMBER_BLINK, + LED_MODE_RED_BLINK, + LED_MODE_AUTO, +}; + +struct led_type_mode { + enum led_type type; + int type_mask; + enum led_light_mode mode; + int mode_mask; +}; + +static struct led_type_mode led_type_mode_data[] = { +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU1_GREEN_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU1_AMBER_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU1_AUTO_MASK}, +{LED_TYPE_PSU1, LED_TYPE_PSU1_REG_MASK, LED_MODE_OFF, LED_MODE_PSU1_OFF_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_GREEN, LED_MODE_PSU2_GREEN_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AMBER, LED_MODE_PSU2_AMBER_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_AUTO, LED_MODE_PSU2_AUTO_MASK}, +{LED_TYPE_PSU2, LED_TYPE_PSU2_REG_MASK, LED_MODE_OFF, LED_MODE_PSU2_OFF_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_GREEN, LED_MODE_FAN_GREEN_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AMBER, LED_MODE_FAN_AMBER_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_AUTO, LED_MODE_FAN_AUTO_MASK}, +{LED_TYPE_FAN, LED_TYPE_FAN_REG_MASK, LED_MODE_OFF, LED_MODE_FAN_OFF_MASK}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN1, LED_TYPE_FAN1_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 6}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, +{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 4}, +{LED_TYPE_FAN3, LED_TYPE_FAN3_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 4}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 2}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, +{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_GREEN, LED_MODE_FANX_GREEN_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 0}, +{LED_TYPE_FAN5, LED_TYPE_FAN5_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 0}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_GREEN, LED_MODE_DIAG_GREEN_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_AMBER, LED_MODE_DIAG_AMBER_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_OFF, LED_MODE_DIAG_OFF_MASK}, +{LED_TYPE_DIAG, LED_TYPE_DIAG_REG_MASK, LED_MODE_GREEN_BLINK, LED_MODE_DIAG_BLINK_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER, LED_MODE_LOC_ON_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_OFF, LED_MODE_LOC_OFF_MASK}, +{LED_TYPE_LOC, LED_TYPE_LOC_REG_MASK, LED_MODE_AMBER_BLINK, LED_MODE_LOC_BLINK_MASK} +}; + + +struct fanx_info_s { + u8 cname; /* device name */ + enum led_type type; + u8 reg_id; /* map to led_reg & reg_val */ +}; + +static struct fanx_info_s fanx_info[] = { + {'1', LED_TYPE_FAN1, 3}, + {'2', LED_TYPE_FAN2, 2}, + {'3', LED_TYPE_FAN3, 2}, + {'4', LED_TYPE_FAN4, 2}, + {'5', LED_TYPE_FAN5, 2}, +}; + +static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + + if (type != led_type_mode_data[i].type) + continue; + + if (type == LED_TYPE_DIAG) + { /* special case : bit 6 - meaning blinking */ + if (0x40 & reg_val) + return LED_MODE_GREEN_BLINK; + } + if ((led_type_mode_data[i].type_mask & reg_val) == + led_type_mode_data[i].mode_mask) + { + return led_type_mode_data[i].mode; + } + } + + return 0; +} + +static u8 led_light_mode_to_reg_val(enum led_type type, + enum led_light_mode mode, u8 reg_val) { + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) { + if (type != led_type_mode_data[i].type) + continue; + + if (mode != led_type_mode_data[i].mode) + continue; + + if (type == LED_TYPE_DIAG) + { + if (mode == LED_MODE_GREEN_BLINK) + { /* special case : bit 6 - meaning blinking */ + reg_val = 0x48 | (reg_val & ~0x4C); + break; + } + else + { /* for diag led, other case must cancel bit 6 first */ + reg_val = reg_val & ~0x40; + } + } + reg_val = led_type_mode_data[i].mode_mask | + (reg_val & (~led_type_mode_data[i].type_mask)); + break; + } + + return reg_val; +} + +static int accton_as6812_32x_led_read_value(u8 reg) +{ + return as6812_32x_i2c_cpld_read(0x60, reg); +} + +static int accton_as6812_32x_led_write_value(u8 reg, u8 value) +{ + return as6812_32x_i2c_cpld_write(0x60, reg, value); +} + +static void accton_as6812_32x_led_update(void) +{ + mutex_lock(&ledctl->update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as6812_32x_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as6812_32x_led_read_value(led_reg[i]); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg[i], status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as6812_32x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + u8 reg, enum led_type type) +{ + int reg_val; + + mutex_lock(&ledctl->update_lock); + + reg_val = accton_as6812_32x_led_read_value(reg); + + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as6812_32x_led_write_value(reg, reg_val); + + /* to prevent the slow-update issue */ + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as6812_32x_led_psu_1_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); +} + +static enum led_brightness accton_as6812_32x_led_psu_1_get(struct led_classdev *cdev) +{ + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); +} + +static void accton_as6812_32x_led_psu_2_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); +} + +static enum led_brightness accton_as6812_32x_led_psu_2_get(struct led_classdev *cdev) +{ + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); +} + +static void accton_as6812_32x_led_fan_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); +} + +static enum led_brightness accton_as6812_32x_led_fan_get(struct led_classdev *cdev) +{ + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); +} + + +static void accton_as6812_32x_led_fanx_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (led_cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); + return; + } + } +} + + +static enum led_brightness accton_as6812_32x_led_fanx_get(struct led_classdev *cdev) +{ + enum led_type led_type1; + int reg_id; + int i, nsize; + int ncount = sizeof(fanx_info)/sizeof(struct fanx_info_s); + + for(i=0;iname); + + if (cdev->name[nsize-1] == fanx_info[i].cname) + { + led_type1 = fanx_info[i].type; + reg_id = fanx_info[i].reg_id; + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(led_type1, ledctl->reg_val[reg_id]); + } + } + + + return led_reg_val_to_light_mode(LED_TYPE_FAN1, ledctl->reg_val[2]); +} + + +static void accton_as6812_32x_led_diag_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); +} + +static enum led_brightness accton_as6812_32x_led_diag_get(struct led_classdev *cdev) +{ + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); +} + +static void accton_as6812_32x_led_loc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as6812_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); +} + +static enum led_brightness accton_as6812_32x_led_loc_get(struct led_classdev *cdev) +{ + accton_as6812_32x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); +} + +static struct led_classdev accton_as6812_32x_leds[] = { + [LED_TYPE_PSU1] = { + .name = "accton_as6812_32x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_psu_1_set, + .brightness_get = accton_as6812_32x_led_psu_1_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as6812_32x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_psu_2_set, + .brightness_get = accton_as6812_32x_led_psu_2_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN] = { + .name = "accton_as6812_32x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fan_set, + .brightness_get = accton_as6812_32x_led_fan_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN1] = { + .name = "accton_as6812_32x_led::fan1", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fanx_set, + .brightness_get = accton_as6812_32x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN2] = { + .name = "accton_as6812_32x_led::fan2", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fanx_set, + .brightness_get = accton_as6812_32x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN3] = { + .name = "accton_as6812_32x_led::fan3", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fanx_set, + .brightness_get = accton_as6812_32x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN4] = { + .name = "accton_as6812_32x_led::fan4", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fanx_set, + .brightness_get = accton_as6812_32x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_FAN5] = { + .name = "accton_as6812_32x_led::fan5", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_fanx_set, + .brightness_get = accton_as6812_32x_led_fanx_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_DIAG] = { + .name = "accton_as6812_32x_led::diag", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_diag_set, + .brightness_get = accton_as6812_32x_led_diag_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_LOC] = { + .name = "accton_as6812_32x_led::loc", + .default_trigger = "unused", + .brightness_set = accton_as6812_32x_led_loc_set, + .brightness_get = accton_as6812_32x_led_loc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as6812_32x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as6812_32x_leds); i++) { + led_classdev_suspend(&accton_as6812_32x_leds[i]); + } + + return 0; +} + +static int accton_as6812_32x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as6812_32x_leds); i++) { + led_classdev_resume(&accton_as6812_32x_leds[i]); + } + + return 0; +} + +static int accton_as6812_32x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as6812_32x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as6812_32x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as6812_32x_leds)){ + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as6812_32x_leds[i]); + } + } + + return ret; +} + +static int accton_as6812_32x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as6812_32x_leds); i++) { + led_classdev_unregister(&accton_as6812_32x_leds[i]); + } + + return 0; +} + +static struct platform_driver accton_as6812_32x_led_driver = { + .probe = accton_as6812_32x_led_probe, + .remove = accton_as6812_32x_led_remove, + .suspend = accton_as6812_32x_led_suspend, + .resume = accton_as6812_32x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as6812_32x_led_init(void) +{ + int ret; + + extern int platform_accton_as6812_32x(void); + if(!platform_accton_as6812_32x()) { + return -ENODEV; + } + + ret = platform_driver_register(&accton_as6812_32x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as6812_32x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as6812_32x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as6812_32x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as6812_32x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as6812_32x_led_driver); + kfree(ledctl); +} + +module_init(accton_as6812_32x_led_init); +module_exit(accton_as6812_32x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as6812_32x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-psu.c b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-psu.c new file mode 100644 index 00000000..dfee68b1 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-psu.c @@ -0,0 +1,305 @@ +/* + * An hwmon driver for accton as6812_32x Power Module + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * 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 + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as6812_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int as6812_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as6812_32x_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[14]; /* Model name, read from eeprom */ +}; + +static struct as6812_32x_psu_data *as6812_32x_psu_update_device(struct device *dev); + +enum as6812_32x_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as6812_32x_psu_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_model_name.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + NULL +}; + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as6812_32x_psu_data *data = as6812_32x_psu_update_device(dev); + u8 status = 0; + + if (attr->index == PSU_PRESENT) { + status = !(data->status >> ((data->index-1)*4) & 0x1); + } + else { /* PSU_POWER_GOOD */ + status = data->status >> ((data->index-1)*4 + 1) & 0x1; + } + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as6812_32x_psu_data *data = as6812_32x_psu_update_device(dev); + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as6812_32x_psu_group = { + .attrs = as6812_32x_psu_attributes, +}; + +static int as6812_32x_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as6812_32x_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as6812_32x_psu_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as6812_32x_psu_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; + } + + /* Update PSU index */ + if (client->addr == 0x50 || client->addr == 0x38) { + data->index = 1; + } + else if (client->addr == 0x53 || client->addr == 0x3b) { + data->index = 2; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as6812_32x_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as6812_32x_psu_remove(struct i2c_client *client) +{ + struct as6812_32x_psu_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as6812_32x_psu_group); + kfree(data); + + return 0; +} + +static const struct i2c_device_id as6812_32x_psu_id[] = { + { "as6812_32x_psu", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as6812_32x_psu_id); + +static struct i2c_driver as6812_32x_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as6812_32x_psu", + }, + .probe = as6812_32x_psu_probe, + .remove = as6812_32x_psu_remove, + .id_table = as6812_32x_psu_id, + .address_list = normal_i2c, +}; + +static int as6812_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 5; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +static struct as6812_32x_psu_data *as6812_32x_psu_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as6812_32x_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status; + int present = 0; + + dev_dbg(&client->dev, "Starting as6812_32x update\n"); + + /* Read psu status */ + status = as6812_32x_i2c_cpld_read(0x60, 0x2); + + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); + } + else { + data->status = status; + } + + /* Read model name */ + memset(data->model_name, 0, sizeof(data->model_name)); + present = !(data->status >> ((data->index-1)*4) & 0x1); + + if (present) { + u8 command; + int model_name_len = 0; + + if (client->addr == 0x38 || client->addr == 0x3b) { + /* cpr_4011_4mxx AC power */ + command = 0x26; + model_name_len = 13; + } + else { /* 0x50 & 0x53 */ + /* ym2651 AC power */ + command = 0x20; + model_name_len = 8; + } + + status = as6812_32x_psu_read_block(client,command,data->model_name, + model_name_len); + + if (status < 0) { + data->model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); + } + else { + data->model_name[model_name_len] = '\0'; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as6812_32x_psu_init(void) +{ + extern int platform_accton_as6812_32x(void); + if(!platform_accton_as6812_32x()) { + return -ENODEV; + } + + return i2c_add_driver(&as6812_32x_psu_driver); +} + +static void __exit as6812_32x_psu_exit(void) +{ + i2c_del_driver(&as6812_32x_psu_driver); +} + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as6812_32x_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(as6812_32x_psu_init); +module_exit(as6812_32x_psu_exit); diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-sfp.c b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-sfp.c new file mode 100644 index 00000000..023e949b --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as6812-32x/modules/builds/x86-64-accton-as6812-32x-sfp.c @@ -0,0 +1,372 @@ +/* + * An hwmon driver for accton as6812_32x sfp + * + * Copyright (C) 2015 Accton Technology Corporation. + * Brandon Chuang + * + * 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 + +#define BIT_INDEX(i) (1ULL << (i)) + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as6812_32x_sfp_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int port; /* Front port index */ + char eeprom[256]; /* eeprom data */ + u64 is_present; /* present status */ +}; + +static struct as6812_32x_sfp_data *as6812_32x_sfp_update_device(struct device *dev, int update_eeprom); +static ssize_t show_present(struct device *dev, struct device_attribute *da,char *buf); +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf); +static int as6812_32x_sfp_read_byte(struct i2c_client *client, u8 command, u8 *data); +extern int as6812_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); +extern int as6812_32x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); +//extern int accton_i2c_cpld_mux_get_index(int adap_index); + +enum as6812_32x_sfp_sysfs_attributes { + SFP_IS_PRESENT, + SFP_EEPROM, + SFP_PORT_NUMBER, + SFP_IS_PRESENT_ALL +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, SFP_IS_PRESENT); +static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, SFP_IS_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); +static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); + +static struct attribute *as6812_32x_sfp_attributes[] = { + &sensor_dev_attr_sfp_is_present.dev_attr.attr, + &sensor_dev_attr_sfp_eeprom.dev_attr.attr, + &sensor_dev_attr_sfp_port_number.dev_attr.attr, + &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, + NULL +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as6812_32x_sfp_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->port+1); +} + +/* Error-check the CPLD read results. */ +#define VALIDATED_READ(_buf, _rv, _read_expr, _invert) \ +do { \ + _rv = (_read_expr); \ + if(_rv < 0) { \ + return sprintf(_buf, "READ ERROR\n"); \ + } \ + if(_invert) { \ + _rv = ~_rv; \ + } \ + _rv &= 0xFF; \ +} while(0) + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + if(attr->index == SFP_IS_PRESENT_ALL) { + int values[4]; + /* + * Report the SFP_PRESENCE status for all ports. + */ + + /* SFP_PRESENT Ports 1-8 */ + VALIDATED_READ(buf, values[0], as6812_32x_i2c_cpld_read(0x62, 0xA), 1); + /* SFP_PRESENT Ports 9-16 */ + VALIDATED_READ(buf, values[1], as6812_32x_i2c_cpld_read(0x62, 0xB), 1); + /* SFP_PRESENT Ports 17-24 */ + VALIDATED_READ(buf, values[2], as6812_32x_i2c_cpld_read(0x64, 0xA), 1); + /* SFP_PRESENT Ports 25-32 */ + VALIDATED_READ(buf, values[3], as6812_32x_i2c_cpld_read(0x64, 0xB), 1); + + /* Return values 1 -> 32 in order */ + return sprintf(buf, "%.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3]); + } + else { /* SFP_IS_PRESENT */ + u8 val; + struct as6812_32x_sfp_data *data = as6812_32x_sfp_update_device(dev, 0); + + if (!data->valid) { + return -EIO; + } + + val = (data->is_present & BIT_INDEX(data->port)) ? 0 : 1; + return sprintf(buf, "%d", val); + } +} + +static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as6812_32x_sfp_data *data = as6812_32x_sfp_update_device(dev, 1); + + if (!data->valid) { + return 0; + } + + if ((data->is_present & BIT_INDEX(data->port)) != 0) { + return 0; + } + + memcpy(buf, data->eeprom, sizeof(data->eeprom)); + + return sizeof(data->eeprom); +} + +static const struct attribute_group as6812_32x_sfp_group = { + .attrs = as6812_32x_sfp_attributes, +}; + +static int as6812_32x_sfp_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as6812_32x_sfp_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, /*I2C_FUNC_SMBUS_BYTE_DATA | */I2C_FUNC_SMBUS_WORD_DATA)) { + status = -EIO; + goto exit; + } + + data = kzalloc(sizeof(struct as6812_32x_sfp_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + mutex_init(&data->update_lock); + data->port = dev_id->driver_data; + i2c_set_clientdata(client, data); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as6812_32x_sfp_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; + } + + dev_info(&client->dev, "%s: sfp '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as6812_32x_sfp_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as6812_32x_sfp_remove(struct i2c_client *client) +{ + struct as6812_32x_sfp_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as6812_32x_sfp_group); + kfree(data); + + return 0; +} + +enum port_numbers { +as6812_32x_sfp1, as6812_32x_sfp2, as6812_32x_sfp3, as6812_32x_sfp4, +as6812_32x_sfp5, as6812_32x_sfp6, as6812_32x_sfp7, as6812_32x_sfp8, +as6812_32x_sfp9, as6812_32x_sfp10, as6812_32x_sfp11,as6812_32x_sfp12, +as6812_32x_sfp13, as6812_32x_sfp14, as6812_32x_sfp15,as6812_32x_sfp16, +as6812_32x_sfp17, as6812_32x_sfp18, as6812_32x_sfp19,as6812_32x_sfp20, +as6812_32x_sfp21, as6812_32x_sfp22, as6812_32x_sfp23,as6812_32x_sfp24, +as6812_32x_sfp25, as6812_32x_sfp26, as6812_32x_sfp27,as6812_32x_sfp28, +as6812_32x_sfp29, as6812_32x_sfp30, as6812_32x_sfp31,as6812_32x_sfp32 +}; + +static const struct i2c_device_id as6812_32x_sfp_id[] = { +{ "as6812_32x_sfp1", as6812_32x_sfp1 }, { "as6812_32x_sfp2", as6812_32x_sfp2 }, +{ "as6812_32x_sfp3", as6812_32x_sfp3 }, { "as6812_32x_sfp4", as6812_32x_sfp4 }, +{ "as6812_32x_sfp5", as6812_32x_sfp5 }, { "as6812_32x_sfp6", as6812_32x_sfp6 }, +{ "as6812_32x_sfp7", as6812_32x_sfp7 }, { "as6812_32x_sfp8", as6812_32x_sfp8 }, +{ "as6812_32x_sfp9", as6812_32x_sfp9 }, { "as6812_32x_sfp10", as6812_32x_sfp10 }, +{ "as6812_32x_sfp11", as6812_32x_sfp11 }, { "as6812_32x_sfp12", as6812_32x_sfp12 }, +{ "as6812_32x_sfp13", as6812_32x_sfp13 }, { "as6812_32x_sfp14", as6812_32x_sfp14 }, +{ "as6812_32x_sfp15", as6812_32x_sfp15 }, { "as6812_32x_sfp16", as6812_32x_sfp16 }, +{ "as6812_32x_sfp17", as6812_32x_sfp17 }, { "as6812_32x_sfp18", as6812_32x_sfp18 }, +{ "as6812_32x_sfp19", as6812_32x_sfp19 }, { "as6812_32x_sfp20", as6812_32x_sfp20 }, +{ "as6812_32x_sfp21", as6812_32x_sfp21 }, { "as6812_32x_sfp22", as6812_32x_sfp22 }, +{ "as6812_32x_sfp23", as6812_32x_sfp23 }, { "as6812_32x_sfp24", as6812_32x_sfp24 }, +{ "as6812_32x_sfp25", as6812_32x_sfp25 }, { "as6812_32x_sfp26", as6812_32x_sfp26 }, +{ "as6812_32x_sfp27", as6812_32x_sfp27 }, { "as6812_32x_sfp28", as6812_32x_sfp28 }, +{ "as6812_32x_sfp29", as6812_32x_sfp29 }, { "as6812_32x_sfp30", as6812_32x_sfp30 }, +{ "as6812_32x_sfp31", as6812_32x_sfp31 }, { "as6812_32x_sfp32", as6812_32x_sfp32 }, +{} +}; +MODULE_DEVICE_TABLE(i2c, as6812_32x_sfp_id); + + +static struct i2c_driver as6812_32x_sfp_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as6812_32x_sfp", + }, + .probe = as6812_32x_sfp_probe, + .remove = as6812_32x_sfp_remove, + .id_table = as6812_32x_sfp_id, + .address_list = normal_i2c, +}; + +#if 0 +static int as6812_32x_sfp_read_byte(struct i2c_client *client, u8 command, u8 *data) +{ + int result = i2c_smbus_read_byte_data(client, command); + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); + goto abort; + } + + *data = (u8)result; + result = 0; + +abort: + return result; +} +#endif + +static int as6812_32x_sfp_read_word(struct i2c_client *client, u8 command, u16 *data) +{ + int result = i2c_smbus_read_word_data(client, command); + + if (unlikely(result < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); + goto abort; + } + + *data = (u16)result; + result = 0; + +abort: + return result; +} + +#define ALWAYS_UPDATE 1 + +static struct as6812_32x_sfp_data *as6812_32x_sfp_update_device(struct device *dev, int update_eeprom) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as6812_32x_sfp_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (ALWAYS_UPDATE || time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int status = -1; + int i = 0, j = 0; + + data->valid = 0; + + /* Read present status of port 1~32 */ + data->is_present = 0; + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + status = as6812_32x_i2c_cpld_read(0x62+i*2, 0xA+j); + + if (status < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x62+i*2, 0xA+j, status); + goto exit; + } + + data->is_present |= (u64)status << ((i*16) + (j*8)); + } + } + + if (update_eeprom) { + /* Read eeprom data based on port number */ + memset(data->eeprom, 0, sizeof(data->eeprom)); + + /* Check if the port is present */ + if ((data->is_present & BIT_INDEX(data->port)) == 0) { + /* read eeprom */ + u16 eeprom_data; + for (i = 0; i < (sizeof(data->eeprom) / 2); i++) { + status = as6812_32x_sfp_read_word(client, i*2, &eeprom_data); + + if (status < 0) { + dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); + goto exit; + } + + data->eeprom[i*2] = eeprom_data & 0xff; + data->eeprom[i*2 + 1] = eeprom_data >> 8; + } + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +module_i2c_driver(as6812_32x_sfp_driver); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton as6812_32x_sfp driver"); +MODULE_LICENSE("GPL");