From f04f6e605f1f40b52a146c75ea9add14a0d2947e Mon Sep 17 00:00:00 2001 From: Jeffrey Townsend Date: Tue, 3 Jan 2017 11:52:00 -0800 Subject: [PATCH] All platform patches have been moved to kernel modules. --- .../patches/driver-hwmon-cpr-4011-4mxx.patch | 439 --- .../patches/mgmt-port-init-config.patch | 50 - ...orm-accton-as5512_54x-device-drivers.patch | 2615 ---------------- ...orm-accton-as5712_54x-device-drivers.patch | 2639 ---------------- ...orm-accton-as5812_54t-device-drivers.patch | 1861 ------------ ...orm-accton-as5812_54x-device-drivers.patch | 2401 --------------- ...orm-accton-as6712_32x-device-drivers.patch | 2334 -------------- ...orm-accton-as6812_32x-device-drivers.patch | 2300 -------------- ...orm-accton-as7512_32x-device-drivers.patch | 2675 ----------------- ...orm-accton-as7712_32x-device-drivers.patch | 1830 ----------- ...orm-accton-as7716_32x-device-drivers.patch | 1707 ----------- .../kernels/3.2.65-1+deb7u2/patches/series | 11 - 12 files changed, 20862 deletions(-) delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/driver-hwmon-cpr-4011-4mxx.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/mgmt-port-init-config.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5512_54x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5712_54x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54t-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6712_32x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6812_32x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7512_32x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7712_32x-device-drivers.patch delete mode 100644 packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7716_32x-device-drivers.patch diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/driver-hwmon-cpr-4011-4mxx.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/driver-hwmon-cpr-4011-4mxx.patch deleted file mode 100644 index 9cf1aa88..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/driver-hwmon-cpr-4011-4mxx.patch +++ /dev/null @@ -1,439 +0,0 @@ -Patch for COMPUWARE CPR-4011-4M11 and CPR-4011-4M21 power module. - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 5c984a6..b3abc7b 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1412,6 +1412,17 @@ config SENSORS_MC13783_ADC - help - Support for the A/D converter on MC13783 PMIC. - -+config SENSORS_CPR_4011_4MXX -+ tristate "Compuware CPR_4011_4MXX Redundant Power Module" -+ depends on I2C -+ help -+ If you say yes here you get support for Compuware CPR_4011_4MXX -+ Redundant Power Module. -+ -+ This driver can also be built as a module. If so, the module will -+ be called cpr_4011_4mxx. -+ -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index ff3a18e..1e90738 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -42,6 +42,7 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o - obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o - obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o - obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o -+obj-$(CONFIG_SENSORS_CPR_4011_4MXX) += cpr_4011_4mxx.o - obj-$(CONFIG_SENSORS_CY8CXX) += cy8cxx.o - obj-$(CONFIG_SENSORS_CY8C3245R1) += cy8c3245r1.o - obj-$(CONFIG_SENSORS_DME1737) += dme1737.o -diff --git a/drivers/hwmon/cpr_4011_4mxx.c b/drivers/hwmon/cpr_4011_4mxx.c -new file mode 100644 -index 0000000..c14c733 ---- /dev/null -+++ b/drivers/hwmon/cpr_4011_4mxx.c -@@ -0,0 +1,397 @@ -+/* -+ * An hwmon driver for the CPR-4011-4Mxx Redundant Power Module -+ * -+ * Copyright (C) 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 MAX_FAN_DUTY_CYCLE 100 -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x3c, 0x3d, 0x3e, 0x3f, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct cpr_4011_4mxx_data { -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+ char valid; /* !=0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 vout_mode; /* Register value */ -+ u16 v_in; /* Register value */ -+ u16 v_out; /* Register value */ -+ u16 i_in; /* Register value */ -+ u16 i_out; /* Register value */ -+ u16 p_in; /* Register value */ -+ u16 p_out; /* Register value */ -+ u16 temp_input[2]; /* Register value */ -+ u8 fan_fault; /* Register value */ -+ u16 fan_duty_cycle[2]; /* Register value */ -+ u16 fan_speed[2]; /* Register value */ -+}; -+ -+static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, const char *buf, size_t count); -+static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value); -+static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev); -+ -+enum cpr_4011_4mxx_sysfs_attributes { -+ PSU_V_IN, -+ PSU_V_OUT, -+ PSU_I_IN, -+ PSU_I_OUT, -+ PSU_P_IN, -+ PSU_P_OUT, -+ PSU_TEMP1_INPUT, -+ PSU_FAN1_FAULT, -+ PSU_FAN1_DUTY_CYCLE, -+ PSU_FAN1_SPEED, -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, show_linear, NULL, PSU_V_IN); -+static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); -+static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, show_linear, NULL, PSU_I_IN); -+static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); -+static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, show_linear, NULL, PSU_P_IN); -+static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); -+static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); -+static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); -+static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); -+static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); -+ -+static struct attribute *cpr_4011_4mxx_attributes[] = { -+ &sensor_dev_attr_psu_v_in.dev_attr.attr, -+ &sensor_dev_attr_psu_v_out.dev_attr.attr, -+ &sensor_dev_attr_psu_i_in.dev_attr.attr, -+ &sensor_dev_attr_psu_i_out.dev_attr.attr, -+ &sensor_dev_attr_psu_p_in.dev_attr.attr, -+ &sensor_dev_attr_psu_p_out.dev_attr.attr, -+ &sensor_dev_attr_psu_temp1_input.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, -+ NULL -+}; -+ -+static int two_complement_to_int(u16 data, u8 valid_bit, int mask) -+{ -+ u16 valid_data = data & mask; -+ bool is_negative = valid_data >> (valid_bit - 1); -+ -+ return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; -+} -+ -+static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct i2c_client *client = to_i2c_client(dev); -+ struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); -+ int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1; -+ long speed; -+ int error; -+ -+ error = kstrtol(buf, 10, &speed); -+ if (error) -+ return error; -+ -+ if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ data->fan_duty_cycle[nr] = speed; -+ cpr_4011_4mxx_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t show_linear(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); -+ -+ u16 value = 0; -+ int exponent, mantissa; -+ int multiplier = 1000; -+ -+ switch (attr->index) { -+ case PSU_V_IN: -+ value = data->v_in; -+ break; -+ case PSU_I_IN: -+ value = data->i_in; -+ break; -+ case PSU_I_OUT: -+ value = data->i_out; -+ break; -+ case PSU_P_IN: -+ value = data->p_in; -+ break; -+ case PSU_P_OUT: -+ value = data->p_out; -+ break; -+ case PSU_TEMP1_INPUT: -+ value = data->temp_input[0]; -+ break; -+ case PSU_FAN1_DUTY_CYCLE: -+ multiplier = 1; -+ value = data->fan_duty_cycle[0]; -+ break; -+ case PSU_FAN1_SPEED: -+ multiplier = 1; -+ value = data->fan_speed[0]; -+ break; -+ default: -+ break; -+ } -+ -+ exponent = two_complement_to_int(value >> 11, 5, 0x1f); -+ mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); -+ -+ return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : -+ sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); -+} -+ -+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); -+ -+ u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; -+ -+ return sprintf(buf, "%d\n", data->fan_fault >> shift); -+} -+ -+static ssize_t show_vout(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct cpr_4011_4mxx_data *data = cpr_4011_4mxx_update_device(dev); -+ int exponent, mantissa; -+ int multiplier = 1000; -+ -+ exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); -+ mantissa = data->v_out; -+ -+ return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : -+ sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); -+} -+ -+static const struct attribute_group cpr_4011_4mxx_group = { -+ .attrs = cpr_4011_4mxx_attributes, -+}; -+ -+static int cpr_4011_4mxx_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct cpr_4011_4mxx_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 cpr_4011_4mxx_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, &cpr_4011_4mxx_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: psu '%s'\n", -+ dev_name(data->hwmon_dev), client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int cpr_4011_4mxx_remove(struct i2c_client *client) -+{ -+ struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &cpr_4011_4mxx_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id cpr_4011_4mxx_id[] = { -+ { "cpr_4011_4mxx", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, cpr_4011_4mxx_id); -+ -+static struct i2c_driver cpr_4011_4mxx_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "cpr_4011_4mxx", -+ }, -+ .probe = cpr_4011_4mxx_probe, -+ .remove = cpr_4011_4mxx_remove, -+ .id_table = cpr_4011_4mxx_id, -+ .address_list = normal_i2c, -+}; -+ -+static int cpr_4011_4mxx_read_byte(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_byte_data(client, reg); -+} -+ -+static int cpr_4011_4mxx_read_word(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_word_data(client, reg); -+} -+ -+static int cpr_4011_4mxx_write_word(struct i2c_client *client, u8 reg, u16 value) -+{ -+ return i2c_smbus_write_word_data(client, reg, value); -+} -+ -+struct reg_data_byte { -+ u8 reg; -+ u8 *value; -+}; -+ -+struct reg_data_word { -+ u8 reg; -+ u16 *value; -+}; -+ -+static struct cpr_4011_4mxx_data *cpr_4011_4mxx_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct cpr_4011_4mxx_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid) { -+ int i, status; -+ struct reg_data_byte regs_byte[] = { {0x20, &data->vout_mode}, -+ {0x81, &data->fan_fault}}; -+ struct reg_data_word regs_word[] = { {0x88, &data->v_in}, -+ {0x8b, &data->v_out}, -+ {0x89, &data->i_in}, -+ {0x8c, &data->i_out}, -+ {0x96, &data->p_out}, -+ {0x97, &data->p_in}, -+ {0x8d, &(data->temp_input[0])}, -+ {0x8e, &(data->temp_input[1])}, -+ {0x3b, &(data->fan_duty_cycle[0])}, -+ {0x3c, &(data->fan_duty_cycle[1])}, -+ {0x90, &(data->fan_speed[0])}, -+ {0x91, &(data->fan_speed[1])}}; -+ -+ dev_dbg(&client->dev, "Starting cpr_4011_4mxx update\n"); -+ -+ /* Read byte data */ -+ for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { -+ status = cpr_4011_4mxx_read_byte(client, regs_byte[i].reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", -+ regs_byte[i].reg, status); -+ } -+ else { -+ *(regs_byte[i].value) = status; -+ } -+ } -+ -+ /* Read word data */ -+ for (i = 0; i < ARRAY_SIZE(regs_word); i++) { -+ status = cpr_4011_4mxx_read_word(client, regs_word[i].reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", -+ regs_word[i].reg, status); -+ } -+ else { -+ *(regs_word[i].value) = status; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init cpr_4011_4mxx_init(void) -+{ -+ return i2c_add_driver(&cpr_4011_4mxx_driver); -+} -+ -+static void __exit cpr_4011_4mxx_exit(void) -+{ -+ i2c_del_driver(&cpr_4011_4mxx_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("CPR_4011_4MXX driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(cpr_4011_4mxx_init); -+module_exit(cpr_4011_4mxx_exit); diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/mgmt-port-init-config.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/mgmt-port-init-config.patch deleted file mode 100644 index 23546620..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/mgmt-port-init-config.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/drivers/net/ethernet/broadcom/tg3/tg3.c b/drivers/net/ethernet/broadcom/tg3/tg3.c -index 4894a11..9b7b7b4 100644 ---- a/drivers/net/ethernet/broadcom/tg3/tg3.c -+++ b/drivers/net/ethernet/broadcom/tg3/tg3.c -@@ -561,6 +561,7 @@ static const struct { - - #define TG3_NUM_TEST ARRAY_SIZE(ethtool_test_keys) - -+static int as7716 = -1; /* as7716=1: as7716 switch is used, it needs as7716 specific patch */ - - static void tg3_write32(struct tg3 *tp, u32 off, u32 val) - { -@@ -1628,6 +1629,10 @@ static void tg3_mdio_config_5785(struct tg3 *tp) - static void tg3_mdio_start(struct tg3 *tp) - { - tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL; -+ -+ if (as7716 == 1) -+ tp->mi_mode |= MAC_MI_MODE_SHORT_PREAMBLE; /* as7716: for accessing external PHY(0x1F) BCM54616S */ -+ - tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(80); - -@@ -2899,6 +2904,11 @@ static int tg3_phy_reset(struct tg3 *tp) - } - } - -+ if (as7716 == 1 && tp->phy_id == TG3_PHY_ID_BCM5718S) { -+ __tg3_writephy(tp, 0x8, 0x10, 0x1d0); /* as7716: set internal phy 0x8 to make linkup */ -+ __tg3_writephy(tp, 0x1f, 0x4, 0x5e1); /* as7716 enable 10/100 cability of external phy BCM 54616S*/ -+ } -+ - if (tg3_flag(tp, 5717_PLUS) && - (tp->phy_flags & TG3_PHYFLG_MII_SERDES)) - return 0; -@@ -19874,6 +19884,14 @@ static struct pci_driver tg3_driver = { - - static int __init tg3_init(void) - { -+ extern int platform_accton_as7716_32x(void); -+ if (platform_accton_as7716_32x()) { -+ as7716 = 1; -+ printk_once(KERN_INFO "\nAS7716-32X\n"); -+ } -+ else -+ as7716 = 0; -+ - #ifdef TG3_VMWARE_NETQ_ENABLE - int i; - for (i = 0; i < TG3_MAX_NIC; i++) { diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5512_54x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5512_54x-device-drivers.patch deleted file mode 100644 index af063811..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5512_54x-device-drivers.patch +++ /dev/null @@ -1,2615 +0,0 @@ -Device driver patches for accton as5512 (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 89c619d..968bd5f 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1574,6 +1574,24 @@ config SENSORS_ACCTON_AS5812_54t_PSU - This driver can also be built as a module. If so, the module will - be called accton_as5812_54t_psu. - -+config SENSORS_ACCTON_AS5512_54X_PSU -+ tristate "Accton as5512 54x psu" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as5512 54x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5512_54x_psu. -+ -+config SENSORS_ACCTON_AS5512_54X_FAN -+ tristate "Accton as5512 54x fan" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as5512 54x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5512_54x_fan. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index de922bc..b8ee7b0 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -36,6 +36,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_FAN) += accton_as6812_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_PSU) += accton_as6812_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_FAN) += accton_as5812_54t_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_PSU) += accton_as5812_54t_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5512_54X_PSU) += accton_as5512_54x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5512_54X_FAN) += accton_as5512_54x_fan.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as5512_54x_fan.c b/drivers/hwmon/accton_as5512_54x_fan.c -new file mode 100644 -index 0000000..67e3dd6 ---- /dev/null -+++ b/drivers/hwmon/accton_as5512_54x_fan.c -@@ -0,0 +1,454 @@ -+/* -+ * A hwmon driver for the Accton as5512 54x fan control -+ * -+ * 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 0x0C -+#define CPLD_REG_FANR_STATUS_OFFSET 0x1E -+#define CPLD_REG_FAN_DIRECTION_OFFSET 0x1D -+ -+#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 0x0D -+ -+#define CPLD_FAN1_INFO_BIT_MASK 0x01 -+#define CPLD_FAN2_INFO_BIT_MASK 0x02 -+#define CPLD_FAN3_INFO_BIT_MASK 0x04 -+#define CPLD_FAN4_INFO_BIT_MASK 0x08 -+#define CPLD_FAN5_INFO_BIT_MASK 0x10 -+ -+#define PROJECT_NAME -+ -+#define LOCAL_DEBUG 0 -+ -+static struct accton_as5512_54x_fan *fan_data = NULL; -+ -+struct accton_as5512_54x_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_as5512_54x_fan_update_device(struct device *dev); -+static int accton_as5512_54x_fan_read_value(u8 reg); -+static int accton_as5512_54x_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 accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_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##_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) -+ -+#define _MAKE_SENSOR_DEVICE_ATTR_FAN_DUTY(prj,id) \ -+ static SENSOR_DEVICE_ATTR(prj##fan##id##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, \ -+ fan_set_duty_cycle, FAN1_DUTY_CYCLE); -+ -+#define MAKE_SENSOR_DEVICE_ATTR_FAN_DUTY(prj,id) _MAKE_SENSOR_DEVICE_ATTR_FAN_DUTY(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) -+MAKE_SENSOR_DEVICE_ATTR_FAN_DUTY(PROJECT_NAME,) -+/*******************/ -+ -+#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##_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) -+ -+#define _MAKE_FAN_DUTY_ATTR(prj, id) \ -+ &sensor_dev_attr_##prj##fan##id##_duty_cycle_percentage.dev_attr.attr, -+ -+#define MAKE_FAN_DUTY_ATTR(prj, id) _MAKE_FAN_DUTY_ATTR(prj, id) -+ -+static struct attribute *accton_as5512_54x_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) -+ MAKE_FAN_DUTY_ATTR(PROJECT_NAME,) -+ 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_as5512_54x_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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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*/ -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); -+ break; -+ default: -+ if (LOCAL_DEBUG) -+ printk ("[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_as5512_54x_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_as5512_54x_fan_group = { -+ .attrs = accton_as5512_54x_fan_attributes, -+}; -+ -+static int accton_as5512_54x_fan_read_value(u8 reg) -+{ -+ return accton_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5512_54x_fan_write_value(u8 reg, u8 value) -+{ -+ return accton_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5512_54x_fan_update_device(struct device *dev) -+{ -+ int speed, r_speed, fault, r_fault, ctrl_speed, direction; -+ int i; -+ -+ mutex_lock(&fan_data->update_lock); -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5512_54x_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; -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5512_54x_fan update 2 \n"); -+ -+ fault = accton_as5512_54x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); -+ r_fault = accton_as5512_54x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); -+ direction = accton_as5512_54x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); -+ ctrl_speed = accton_as5512_54x_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); -+ -+ if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); -+ -+ for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; -+ if (LOCAL_DEBUG) -+ printk ("[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_as5512_54x_fan_read_value(fan_speed_reg[i]); -+ r_speed = accton_as5512_54x_fan_read_value(fanr_speed_reg[i]); -+ if ( (speed < 0) || (r_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[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_as5512_54x_fan_probe(struct platform_device *pdev) -+{ -+ int status = -1; -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &accton_as5512_54x_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_as5512_54x_fan\n"); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &accton_as5512_54x_fan_group); -+exit: -+ return status; -+} -+ -+static int accton_as5512_54x_fan_remove(struct platform_device *pdev) -+{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5512_54x_fan_group); -+ -+ return 0; -+} -+ -+#define DRVNAME "as5512_54x_fan" -+ -+static struct platform_driver accton_as5512_54x_fan_driver = { -+ .probe = accton_as5512_54x_fan_probe, -+ .remove = accton_as5512_54x_fan_remove, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5512_54x_fan_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5512_54x(void); -+ if(!platform_accton_as5512_54x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5512_54x_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ fan_data = kzalloc(sizeof(struct accton_as5512_54x_fan), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5512_54x_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_as5512_54x_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5512_54x_fan_exit(void) -+{ -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&accton_as5512_54x_fan_driver); -+ kfree(fan_data); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5512_54x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_as5512_54x_fan_init); -+module_exit(accton_as5512_54x_fan_exit); -+ -+ -diff --git a/drivers/hwmon/accton_as5512_54x_psu.c b/drivers/hwmon/accton_as5512_54x_psu.c -new file mode 100644 -index 0000000..66d61f3 ---- /dev/null -+++ b/drivers/hwmon/accton_as5512_54x_psu.c -@@ -0,0 +1,295 @@ -+/* -+ * An hwmon driver for accton as5512_54x 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 -+ -+static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); -+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 as5512_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x38, 0x3b, 0x50, 0x53, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as5512_54x_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 as5512_54x_psu_data *as5512_54x_psu_update_device(struct device *dev); -+ -+enum as5512_54x_psu_sysfs_attributes { -+ PSU_INDEX, -+ PSU_PRESENT, -+ PSU_MODEL_NAME, -+ PSU_POWER_GOOD -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); -+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 *as5512_54x_psu_attributes[] = { -+ &sensor_dev_attr_psu_index.dev_attr.attr, -+ &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_index(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5512_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", data->index); -+} -+ -+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 as5512_54x_psu_data *data = as5512_54x_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 as5512_54x_psu_data *data = as5512_54x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s", data->model_name); -+} -+ -+static const struct attribute_group as5512_54x_psu_group = { -+ .attrs = as5512_54x_psu_attributes, -+}; -+ -+static int as5512_54x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5512_54x_psu_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5512_54x_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, &as5512_54x_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 == 0x38 || client->addr == 0x50) { -+ data->index = 1; -+ } -+ else if (client->addr == 0x3b || client->addr == 0x53) { -+ 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, &as5512_54x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5512_54x_psu_remove(struct i2c_client *client) -+{ -+ struct as5512_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5512_54x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as5512_54x_psu_id[] = { -+ { "as5512_54x_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as5512_54x_psu_id); -+ -+static struct i2c_driver as5512_54x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5512_54x_psu", -+ }, -+ .probe = as5512_54x_psu_probe, -+ .remove = as5512_54x_psu_remove, -+ .id_table = as5512_54x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5512_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as5512_54x_psu_data *as5512_54x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5512_54x_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 = -1; -+ -+ dev_dbg(&client->dev, "Starting as5512_54x update\n"); -+ -+ /* Read model name */ -+ if (client->addr == 0x38 || client->addr == 0x3b) { -+ /* AC power */ -+ status = as5512_54x_psu_read_block(client, 0x26, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ else { -+ /* DC power */ -+ status = as5512_54x_psu_read_block(client, 0x50, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ -+ /* Read psu status */ -+ status = accton_i2c_cpld_read(0x60, 0x2); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); -+ } -+ else { -+ data->status = status; -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as5512_54x_psu_init(void) -+{ -+ extern int platform_accton_as5512_54x(void); -+ if(!platform_accton_as5512_54x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as5512_54x_psu_driver); -+} -+ -+static void __exit as5512_54x_psu_exit(void) -+{ -+ i2c_del_driver(&as5512_54x_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5512_54x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as5512_54x_psu_init); -+module_exit(as5512_54x_psu_exit); -+ -diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c -index acf88c9..e50c599 100644 ---- a/drivers/hwmon/accton_i2c_cpld.c -+++ b/drivers/hwmon/accton_i2c_cpld.c -@@ -255,6 +255,29 @@ int platform_accton_as5812_54t(void) - } - EXPORT_SYMBOL(platform_accton_as5812_54t); - -+static struct dmi_system_id as5512_54x_dmi_table[] = { -+ { -+ .ident = "Accton AS5512", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5512"), -+ }, -+ }, -+ { -+ .ident = "Accton AS5512", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5512"), -+ }, -+ }, -+}; -+ -+int platform_accton_as5512_54x(void) -+{ -+ return dmi_check_system(as5512_54x_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as5512_54x); -+ - MODULE_AUTHOR("Brandon Chuang "); - MODULE_DESCRIPTION("accton_i2c_cpld driver"); - MODULE_LICENSE("GPL"); -diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig -index 599b97b..9ba4a1b 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -88,7 +88,14 @@ config LEDS_ACCTON_AS5812_54t - help - This option enables support for the LEDs on the Accton as5812 54t. - Say Y to enable LEDs on the Accton as5812 54t. -- -+ -+config LEDS_ACCTON_AS5512_54X -+ tristate "LED support for the Accton as5512 54x" -+ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD -+ help -+ This option enables support for the LEDs on the Accton as5512 54x. -+ Say Y to enable LEDs on the Accton as5512 54x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index bd20baa..ff3be6c 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -50,6 +50,7 @@ obj-$(CONFIG_LEDS_ACCTON_AS7712_32x) += leds-accton_as7712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS5812_54x) += leds-accton_as5812_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6812_32x) += leds-accton_as6812_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS5812_54t) += leds-accton_as5812_54t.o -+obj-$(CONFIG_LEDS_ACCTON_AS5512_54X) += leds-accton_as5512_54x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as5512_54x.c b/drivers/leds/leds-accton_as5512_54x.c -new file mode 100644 -index 0000000..761483a ---- /dev/null -+++ b/drivers/leds/leds-accton_as5512_54x.c -@@ -0,0 +1,463 @@ -+/* -+ * A LED driver for the accton_as5512_54x_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 accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_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 "as5512_54x_led" -+ -+struct accton_as5512_54x_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[2]; /* Register value, 0 = LOC/DIAG/FAN LED -+ 1 = PSU1/PSU2 LED */ -+}; -+ -+static struct accton_as5512_54x_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_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_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 */ -+}; -+ -+enum led_type { -+ LED_TYPE_PSU1, -+ LED_TYPE_PSU2, -+ LED_TYPE_DIAG, -+ LED_TYPE_FAN, -+ LED_TYPE_LOC -+}; -+ -+enum led_light_mode { -+ LED_MODE_OFF = 0, -+ LED_MODE_GREEN, -+ LED_MODE_GREEN_BLINK, -+ LED_MODE_AMBER, -+ LED_MODE_AMBER_BLINK, -+ LED_MODE_RED, -+ LED_MODE_RED_BLINK, -+ LED_MODE_BLUE, -+ LED_MODE_BLUE_BLINK, -+ LED_MODE_AUTO, -+ LED_MODE_UNKNOWN -+}; -+ -+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_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_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} -+}; -+ -+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 ((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; -+ -+ reg_val = led_type_mode_data[i].mode_mask | -+ (reg_val & (~led_type_mode_data[i].type_mask)); -+ } -+ -+ return reg_val; -+} -+ -+static int accton_as5512_54x_led_read_value(u8 reg) -+{ -+ return accton_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5512_54x_led_write_value(u8 reg, u8 value) -+{ -+ return accton_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5512_54x_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_as5512_54x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as5512_54x_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_as5512_54x_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_as5512_54x_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_as5512_54x_led_write_value(reg, reg_val); -+ -+ /* to prevent the slow-update issue */ -+ ledctl->valid = 0; -+ -+exit: -+ mutex_unlock(&ledctl->update_lock); -+} -+ -+static void accton_as5512_54x_led_psu_1_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5512_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); -+} -+ -+static enum led_brightness accton_as5512_54x_led_psu_1_get(struct led_classdev *cdev) -+{ -+ accton_as5512_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); -+} -+ -+static void accton_as5512_54x_led_psu_2_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5512_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); -+} -+ -+static enum led_brightness accton_as5512_54x_led_psu_2_get(struct led_classdev *cdev) -+{ -+ accton_as5512_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); -+} -+ -+static void accton_as5512_54x_led_fan_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5512_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); -+} -+ -+static enum led_brightness accton_as5512_54x_led_fan_get(struct led_classdev *cdev) -+{ -+ accton_as5512_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); -+} -+ -+static void accton_as5512_54x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5512_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as5512_54x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as5512_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as5512_54x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5512_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as5512_54x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as5512_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static struct led_classdev accton_as5512_54x_leds[] = { -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as5512_54x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5512_54x_led_psu_1_set, -+ .brightness_get = accton_as5512_54x_led_psu_1_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as5512_54x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5512_54x_led_psu_2_set, -+ .brightness_get = accton_as5512_54x_led_psu_2_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as5512_54x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5512_54x_led_fan_set, -+ .brightness_get = accton_as5512_54x_led_fan_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as5512_54x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5512_54x_led_diag_set, -+ .brightness_get = accton_as5512_54x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as5512_54x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5512_54x_led_loc_set, -+ .brightness_get = accton_as5512_54x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as5512_54x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5512_54x_leds); i++) { -+ led_classdev_suspend(&accton_as5512_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5512_54x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5512_54x_leds); i++) { -+ led_classdev_resume(&accton_as5512_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5512_54x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5512_54x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as5512_54x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as5512_54x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as5512_54x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as5512_54x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5512_54x_leds); i++) { -+ led_classdev_unregister(&accton_as5512_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as5512_54x_led_driver = { -+ .probe = accton_as5512_54x_led_probe, -+ .remove = accton_as5512_54x_led_remove, -+ .suspend = accton_as5512_54x_led_suspend, -+ .resume = accton_as5512_54x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5512_54x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5512_54x(void); -+ if(!platform_accton_as5512_54x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5512_54x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as5512_54x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5512_54x_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_as5512_54x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5512_54x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as5512_54x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as5512_54x_led_init); -+module_exit(accton_as5512_54x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5512_54x_led driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index c75227b..d90ebe2 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -135,7 +135,16 @@ config EEPROM_ACCTON_AS5812_54t_SFP - - This driver can also be built as a module. If so, the module will - be called accton_as5812_54t_sfp. -- -+ -+config EEPROM_ACCTON_AS5512_54X_SFP -+ tristate "Accton as5512_54x sfp" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton 5512_54x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_5512_54x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 152a8bc..907f836 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -13,4 +13,5 @@ obj-$(CONFIG_EEPROM_ACCTON_AS7712_32x_SFP) += accton_as7712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5812_54x_SFP) += accton_as5812_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6812_32x_SFP) += accton_as6812_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5812_54t_SFP) += accton_as5812_54t_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS5512_54X_SFP) += accton_as5512_54x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as5512_54x_sfp.c b/drivers/misc/eeprom/accton_as5512_54x_sfp.c -new file mode 100644 -index 0000000..d89e71d ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as5512_54x_sfp.c -@@ -0,0 +1,1237 @@ -+/* -+ * SFP driver for accton as5512_54x sfp -+ * -+ * Copyright (C) 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 -+ -+#define DRIVER_NAME "as5512_54x_sfp" -+ -+#define DEBUG_MODE 0 -+ -+#if (DEBUG_MODE == 1) -+ #define DEBUG_PRINT(fmt, args...) \ -+ printk (KERN_INFO "%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) -+#else -+ #define DEBUG_PRINT(fmt, args...) -+#endif -+ -+#define NUM_OF_SFP_PORT 54 -+#define EEPROM_NAME "sfp_eeprom" -+#define EEPROM_SIZE 256 /* 256 byte eeprom */ -+#define BIT_INDEX(i) (1ULL << (i)) -+#define USE_I2C_BLOCK_READ 1 -+#define I2C_RW_RETRY_COUNT 3 -+#define I2C_RW_RETRY_INTERVAL 100 /* ms */ -+ -+#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) -+#define SFP_EEPROM_A2_I2C_ADDR (0xA2 >> 1) -+ -+#define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 -+#define SFF8024_DEVICE_ID_SFP 0x3 -+#define SFF8024_DEVICE_ID_QSFP 0xC -+#define SFF8024_DEVICE_ID_QSFP_PLUS 0xD -+#define SFF8024_DEVICE_ID_QSFP28 0x11 -+ -+#define SFF8472_DIAG_MON_TYPE_ADDR 92 -+#define SFF8472_DIAG_MON_TYPE_DDM_MASK 0x40 -+#define SFF8472_10G_ETH_COMPLIANCE_ADDR 0x3 -+#define SFF8472_10G_BASE_MASK 0xF0 -+ -+#define SFF8436_RX_LOS_ADDR 3 -+#define SFF8436_TX_FAULT_ADDR 4 -+#define SFF8436_TX_DISABLE_ADDR 86 -+ -+static ssize_t sfp_eeprom_read(struct i2c_client *, u8, u8 *,int); -+static ssize_t sfp_eeprom_write(struct i2c_client *, u8 , const char *,int); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { SFP_EEPROM_A0_I2C_ADDR, SFP_EEPROM_A2_I2C_ADDR, I2C_CLIENT_END }; -+ -+#define CPLD_PORT_TO_FRONT_PORT(port) (port+1) -+ -+enum port_numbers { -+sfp1, sfp2, sfp3, sfp4, sfp5, sfp6, sfp7, sfp8, -+sfp9, sfp10, sfp11, sfp12, sfp13, sfp14, sfp15, sfp16, -+sfp17, sfp18, sfp19, sfp20, sfp21, sfp22, sfp23, sfp24, -+sfp25, sfp26, sfp27, sfp28, sfp29, sfp30, sfp31, sfp32, -+sfp33, sfp34, sfp35, sfp36, sfp37, sfp38, sfp39, sfp40, -+sfp41, sfp42, sfp43, sfp44, sfp45, sfp46, sfp47, sfp48, -+sfp49, sfp50, sfp51, sfp52, sfp53, sfp54 -+}; -+ -+static const struct i2c_device_id sfp_device_id[] = { -+{ "sfp1", sfp1 }, { "sfp2", sfp2 }, { "sfp3", sfp3 }, { "sfp4", sfp4 }, -+{ "sfp5", sfp5 }, { "sfp6", sfp6 }, { "sfp7", sfp7 }, { "sfp8", sfp8 }, -+{ "sfp9", sfp9 }, { "sfp10", sfp10 }, { "sfp11", sfp11 }, { "sfp12", sfp12 }, -+{ "sfp13", sfp13 }, { "sfp14", sfp14 }, { "sfp15", sfp15 }, { "sfp16", sfp16 }, -+{ "sfp17", sfp17 }, { "sfp18", sfp18 }, { "sfp19", sfp19 }, { "sfp20", sfp20 }, -+{ "sfp21", sfp21 }, { "sfp22", sfp22 }, { "sfp23", sfp23 }, { "sfp24", sfp24 }, -+{ "sfp25", sfp25 }, { "sfp26", sfp26 }, { "sfp27", sfp27 }, { "sfp28", sfp28 }, -+{ "sfp29", sfp29 }, { "sfp30", sfp30 }, { "sfp31", sfp31 }, { "sfp32", sfp32 }, -+{ "sfp33", sfp33 }, { "sfp34", sfp34 }, { "sfp35", sfp35 }, { "sfp36", sfp36 }, -+{ "sfp37", sfp37 }, { "sfp38", sfp38 }, { "sfp39", sfp39 }, { "sfp40", sfp40 }, -+{ "sfp41", sfp41 }, { "sfp42", sfp42 }, { "sfp43", sfp43 }, { "sfp44", sfp44 }, -+{ "sfp45", sfp45 }, { "sfp46", sfp46 }, { "sfp47", sfp47 }, { "sfp48", sfp48 }, -+{ "sfp49", sfp49 }, { "sfp50", sfp50 }, { "sfp51", sfp51 }, { "sfp52", sfp52 }, -+{ "sfp53", sfp53 }, { "sfp54", sfp54 }, -+{ /* LIST END */ } -+}; -+MODULE_DEVICE_TABLE(i2c, sfp_device_id); -+ -+/* -+ * list of valid port types -+ * note OOM_PORT_TYPE_NOT_PRESENT to indicate no -+ * module is present in this port -+ */ -+typedef enum oom_driver_port_type_e { -+ OOM_DRIVER_PORT_TYPE_INVALID, -+ OOM_DRIVER_PORT_TYPE_NOT_PRESENT, -+ OOM_DRIVER_PORT_TYPE_SFP, -+ OOM_DRIVER_PORT_TYPE_SFP_PLUS, -+ OOM_DRIVER_PORT_TYPE_QSFP, -+ OOM_DRIVER_PORT_TYPE_QSFP_PLUS, -+ OOM_DRIVER_PORT_TYPE_QSFP28 -+} oom_driver_port_type_t; -+ -+enum driver_type_e { -+ DRIVER_TYPE_SFP_MSA, -+ DRIVER_TYPE_SFP_DDM, -+ DRIVER_TYPE_QSFP -+}; -+ -+/* Each client has this additional data -+ */ -+struct eeprom_data { -+ char valid; /* !=0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ struct bin_attribute bin; /* eeprom data */ -+}; -+ -+struct sfp_msa_data { -+ char valid; /* !=0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u64 status[6]; /* bit0:port0, bit1:port1 and so on */ -+ /* index 0 => tx_fail -+ 1 => tx_disable -+ 2 => rx_loss -+ 3 => device id -+ 4 => 10G Ethernet Compliance Codes -+ to distinguish SFP or SFP+ -+ 5 => DIAGNOSTIC MONITORING TYPE */ -+ struct eeprom_data eeprom; -+}; -+ -+struct sfp_ddm_data { -+ struct eeprom_data eeprom; -+}; -+ -+struct qsfp_data { -+ char valid; /* !=0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 status[3]; /* bit0:port0, bit1:port1 and so on */ -+ /* index 0 => tx_fail -+ 1 => tx_disable -+ 2 => rx_loss */ -+ -+ u8 device_id; -+ struct eeprom_data eeprom; -+}; -+ -+struct sfp_port_data { -+ struct mutex update_lock; -+ enum driver_type_e driver_type; -+ int port; /* CPLD port index */ -+ oom_driver_port_type_t port_type; -+ u64 present; /* present status, bit0:port0, bit1:port1 and so on */ -+ -+ struct sfp_msa_data *msa; -+ struct sfp_ddm_data *ddm; -+ struct qsfp_data *qsfp; -+ -+ struct i2c_client *client; -+}; -+ -+enum sfp_sysfs_attributes { -+ PRESENT, -+ PRESENT_ALL, -+ PORT_NUMBER, -+ PORT_TYPE, -+ DDM_IMPLEMENTED, -+ TX_FAULT, -+ TX_FAULT1, -+ TX_FAULT2, -+ TX_FAULT3, -+ TX_FAULT4, -+ TX_DISABLE, -+ TX_DISABLE1, -+ TX_DISABLE2, -+ TX_DISABLE3, -+ TX_DISABLE4, -+ RX_LOS, -+ RX_LOS1, -+ RX_LOS2, -+ RX_LOS3, -+ RX_LOS4, -+ RX_LOS_ALL -+}; -+ -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); -+} -+ -+static struct sfp_port_data* sfp_update_present(struct i2c_client *client) -+{ -+ int i = 0, j = 0, status = -1; -+ u8 reg; -+ unsigned short cpld_addr; -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ -+ DEBUG_PRINT("Starting sfp present status update"); -+ mutex_lock(&data->update_lock); -+ data->present = 0; -+ -+ /* Read present status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 3; j++) { -+ cpld_addr = 0x61+i; -+ reg = 0x6+j; -+ status = accton_i2c_cpld_read(cpld_addr, reg); -+ -+ if (unlikely(status < 0)) { -+ data = ERR_PTR(status); -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); -+ goto exit; -+ } -+ -+ DEBUG_PRINT("Present status = 0x%lx\r\n", data->present); -+ data->present |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ cpld_addr = 0x62; -+ reg = 0x14; -+ status = accton_i2c_cpld_read(cpld_addr, reg); -+ -+ if (unlikely(status < 0)) { -+ data = ERR_PTR(status); -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); -+ goto exit; -+ } -+ else { -+ data->present |= (u64)status << 48; -+ } -+ -+ DEBUG_PRINT("Present status = 0x%lx", data->present); -+exit: -+ mutex_unlock(&data->update_lock); -+ return data; -+} -+ -+static struct sfp_port_data* sfp_update_tx_rx_status(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ int i = 0, j = 0; -+ int status = -1; -+ -+ if (time_before(jiffies, data->msa->last_updated + HZ + HZ / 2) && data->msa->valid) { -+ return data; -+ } -+ -+ DEBUG_PRINT("Starting as5512_54x sfp tx rx status update"); -+ mutex_lock(&data->update_lock); -+ data->msa->valid = 0; -+ memset(data->msa->status, 0, sizeof(data->msa->status)); -+ -+ /* Read status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 9; j++) { -+ u8 reg; -+ unsigned short cpld_addr; -+ reg = 0x9+j; -+ cpld_addr = 0x61+i; -+ -+ status = accton_i2c_cpld_read(cpld_addr, reg); -+ if (unlikely(status < 0)) { -+ data = ERR_PTR(status); -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", cpld_addr, reg, status); -+ goto exit; -+ } -+ -+ data->msa->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ data->msa->valid = 1; -+ data->msa->last_updated = jiffies; -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return data; -+} -+ -+static ssize_t sfp_set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ unsigned short cpld_addr = 0; -+ u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; -+ long disable; -+ int error; -+ -+ error = kstrtol(buf, 10, &disable); -+ if (error) { -+ return error; -+ } -+ -+ mutex_lock(&data->update_lock); -+ -+ if(data->port < 24) { -+ cpld_addr = 0x61; -+ cpld_reg = 0xC + data->port / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ else { /* port 24 ~ 48 */ -+ cpld_addr = 0x62; -+ cpld_reg = 0xC + (data->port - 24) / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ -+ /* Read current status */ -+ cpld_val = accton_i2c_cpld_read(cpld_addr, cpld_reg); -+ -+ /* Update tx_disable status */ -+ if (disable) { -+ data->msa->status[1] |= BIT_INDEX(data->port); -+ cpld_val |= cpld_bit; -+ } -+ else { -+ data->msa->status[1] &= ~BIT_INDEX(data->port); -+ cpld_val &= ~cpld_bit; -+ } -+ -+ accton_i2c_cpld_write(cpld_addr, cpld_reg, cpld_val); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static int sfp_is_port_present(struct i2c_client *client, int port) -+{ -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ -+ data = sfp_update_present(client); -+ if (IS_ERR(data)) { -+ return PTR_ERR(data); -+ } -+ -+ return (data->present & BIT_INDEX(data->port)) ? 0 : 1; -+} -+ -+static ssize_t show_present(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct i2c_client *client = to_i2c_client(dev); -+ -+ if (PRESENT_ALL == attr->index) { -+ int i; -+ u8 values[7] = {0}; -+ struct sfp_port_data *data = sfp_update_present(client); -+ -+ if (IS_ERR(data)) { -+ return PTR_ERR(data); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(values); i++) { -+ values[i] = ~(u8)(data->present >> (i * 8)); -+ } -+ -+ /* Return values 1 -> 54 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5], -+ values[6] & 0x3F); -+ } -+ else { -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ int present = sfp_is_port_present(client, data->port); -+ -+ if (IS_ERR_VALUE(present)) { -+ return present; -+ } -+ -+ /* PRESENT */ -+ return sprintf(buf, "%d\n", present); -+ } -+} -+ -+static struct sfp_port_data *sfp_update_port_type(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ u8 buf = 0; -+ int status; -+ -+ mutex_lock(&data->update_lock); -+ -+ switch (data->driver_type) { -+ case DRIVER_TYPE_SFP_MSA: -+ { -+ status = sfp_eeprom_read(client, SFF8024_PHYSICAL_DEVICE_ID_ADDR, &buf, sizeof(buf)); -+ if (unlikely(status < 0)) { -+ data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; -+ break; -+ } -+ -+ if (buf != SFF8024_DEVICE_ID_SFP) { -+ data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; -+ break; -+ } -+ -+ status = sfp_eeprom_read(client, SFF8472_10G_ETH_COMPLIANCE_ADDR, &buf, sizeof(buf)); -+ if (unlikely(status < 0)) { -+ data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; -+ break; -+ } -+ -+ DEBUG_PRINT("sfp port type (0x3) data = (0x%x)", buf); -+ data->port_type = buf & SFF8472_10G_BASE_MASK ? OOM_DRIVER_PORT_TYPE_SFP_PLUS : OOM_DRIVER_PORT_TYPE_SFP; -+ break; -+ } -+ case DRIVER_TYPE_QSFP: -+ { -+ status = sfp_eeprom_read(client, SFF8024_PHYSICAL_DEVICE_ID_ADDR, &buf, sizeof(buf)); -+ if (unlikely(status < 0)) { -+ data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; -+ break; -+ } -+ -+ DEBUG_PRINT("qsfp port type (0x0) buf = (0x%x)", buf); -+ switch (buf) { -+ case SFF8024_DEVICE_ID_QSFP: -+ data->port_type = OOM_DRIVER_PORT_TYPE_QSFP; -+ break; -+ case SFF8024_DEVICE_ID_QSFP_PLUS: -+ data->port_type = OOM_DRIVER_PORT_TYPE_QSFP_PLUS; -+ break; -+ case SFF8024_DEVICE_ID_QSFP28: -+ data->port_type = OOM_DRIVER_PORT_TYPE_QSFP_PLUS; -+ break; -+ default: -+ data->port_type = OOM_DRIVER_PORT_TYPE_INVALID; -+ break; -+ } -+ -+ break; -+ } -+ default: -+ break; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ return data; -+} -+ -+static ssize_t show_port_type(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ int present = sfp_is_port_present(client, data->port); -+ -+ if (IS_ERR_VALUE(present)) { -+ return present; -+ } -+ -+ if (!present) { -+ return sprintf(buf, "%d\n", OOM_DRIVER_PORT_TYPE_NOT_PRESENT); -+ } -+ -+ sfp_update_port_type(dev); -+ return sprintf(buf, "%d\n", data->port_type); -+} -+ -+static struct sfp_port_data* qsfp_update_tx_rx_status(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ int i, status = -1; -+ u8 buf = 0; -+ u8 reg[] = {SFF8436_TX_FAULT_ADDR, SFF8436_TX_DISABLE_ADDR, SFF8436_RX_LOS_ADDR}; -+ -+ if (time_before(jiffies, data->qsfp->last_updated + HZ + HZ / 2) && data->qsfp->valid) { -+ return data; -+ } -+ -+ DEBUG_PRINT("Starting sfp tx rx status update"); -+ mutex_lock(&data->update_lock); -+ data->qsfp->valid = 0; -+ memset(data->qsfp->status, 0, sizeof(data->qsfp->status)); -+ -+ /* Notify device to update tx fault/ tx disable/ rx los status */ -+ for (i = 0; i < ARRAY_SIZE(reg); i++) { -+ status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); -+ if (unlikely(status < 0)) { -+ data = ERR_PTR(status); -+ goto exit; -+ } -+ } -+ msleep(200); -+ -+ /* Read actual tx fault/ tx disable/ rx los status */ -+ for (i = 0; i < ARRAY_SIZE(reg); i++) { -+ status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); -+ if (unlikely(status < 0)) { -+ data = ERR_PTR(status); -+ goto exit; -+ } -+ -+ DEBUG_PRINT("qsfp reg(0x%x) status = (0x%x)", reg[i], data->qsfp->status[i]); -+ data->qsfp->status[i] = (buf & 0xF); -+ } -+ -+ data->qsfp->valid = 1; -+ data->qsfp->last_updated = jiffies; -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return data; -+} -+ -+static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ int status; -+ u8 val = 0; -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ -+ status = sfp_is_port_present(client, data->port); -+ if (IS_ERR_VALUE(status)) { -+ return status; -+ } -+ -+ data = qsfp_update_tx_rx_status(dev); -+ if (IS_ERR(data)) { -+ return PTR_ERR(data); -+ } -+ -+ switch (attr->index) { -+ case TX_FAULT1: -+ case TX_FAULT2: -+ case TX_FAULT3: -+ case TX_FAULT4: -+ val = (data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)) ? 1 : 0; -+ break; -+ case TX_DISABLE1: -+ case TX_DISABLE2: -+ case TX_DISABLE3: -+ case TX_DISABLE4: -+ val = (data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)) ? 1 : 0; -+ break; -+ case RX_LOS1: -+ case RX_LOS2: -+ case RX_LOS3: -+ case RX_LOS4: -+ val = (data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)) ? 1 : 0; -+ break; -+ default: -+ break; -+ } -+ -+ return sprintf(buf, "%d\n", val); -+} -+ -+static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ long disable; -+ int status; -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct sfp_port_data *data = NULL; -+ -+ status = kstrtol(buf, 10, &disable); -+ if (status) { -+ return status; -+ } -+ -+ data = qsfp_update_tx_rx_status(dev); -+ if (IS_ERR(data)) { -+ return PTR_ERR(data); -+ } -+ -+ mutex_lock(&data->update_lock); -+ -+ if (disable) { -+ data->qsfp->status[1] |= (1 << (attr->index - TX_DISABLE1)); -+ } -+ else { -+ data->qsfp->status[1] &= ~(1 << (attr->index - TX_DISABLE1)); -+ } -+ -+ DEBUG_PRINT("index = (%d), status = (0x%x)", attr->index, data->qsfp->status[1]); -+ status = sfp_eeprom_write(data->client, SFF8436_TX_DISABLE_ADDR, &data->qsfp->status[1], sizeof(data->qsfp->status[1])); -+ if (unlikely(status < 0)) { -+ count = status; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t sfp_show_ddm_implemented(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ int status; -+ char ddm; -+ struct i2c_client *client = to_i2c_client(dev); -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ -+ status = sfp_is_port_present(client, data->port); -+ if (IS_ERR_VALUE(status)) { -+ return status; -+ } -+ -+ status = sfp_eeprom_read(client, SFF8472_DIAG_MON_TYPE_ADDR, &ddm, sizeof(ddm)); -+ if (unlikely(status < 0)) { -+ return status; -+ } -+ -+ return sprintf(buf, "%d\n", (ddm & SFF8472_DIAG_MON_TYPE_DDM_MASK) ? 1 : 0); -+} -+ -+static ssize_t sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ u8 val = 0, index = 0; -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct sfp_port_data *data = sfp_update_tx_rx_status(dev); -+ -+ if (IS_ERR(data)) { -+ return PTR_ERR(data); -+ } -+ -+ if(attr->index == RX_LOS_ALL) { -+ int i = 0; -+ u8 values[6] = {0}; -+ -+ for (i = 0; i < ARRAY_SIZE(values); i++) { -+ values[i] = (u8)(data->msa->status[2] >> (i * 8)); -+ } -+ -+ /** Return values 1 -> 48 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5]); -+ } -+ -+ switch (attr->index) { -+ case TX_FAULT: -+ index = 0; -+ break; -+ case TX_DISABLE: -+ index = 1; -+ break; -+ case RX_LOS: -+ index = 2; -+ break; -+ default: -+ break; -+ } -+ -+ val = (data->msa->status[index] & BIT_INDEX(data->port)) ? 1 : 0; -+ return sprintf(buf, "%d\n", val); -+} -+ -+/* SFP/QSFP common attributes for sysfs */ -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, PORT_NUMBER); -+static SENSOR_DEVICE_ATTR(sfp_port_type, S_IRUGO, show_port_type, NULL, PORT_TYPE); -+static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, PRESENT); -+static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, PRESENT_ALL); -+ -+/* QSFP attributes for sysfs */ -+static SENSOR_DEVICE_ATTR(sfp_rx_los1, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS1); -+static SENSOR_DEVICE_ATTR(sfp_rx_los2, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS2); -+static SENSOR_DEVICE_ATTR(sfp_rx_los3, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS3); -+static SENSOR_DEVICE_ATTR(sfp_rx_los4, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS4); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable1, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE1); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable2, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE2); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable3, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE3); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable4, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE4); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT1); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault2, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT2); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault3, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT3); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault4, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT4); -+static struct attribute *qsfp_attributes[] = { -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_port_type.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los1.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los2.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los3.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los4.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable1.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable2.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable3.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable4.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault2.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault3.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault4.dev_attr.attr, -+ NULL -+}; -+ -+/* SFP msa attributes for sysfs */ -+static SENSOR_DEVICE_ATTR(sfp_ddm_implemented, S_IRUGO, sfp_show_ddm_implemented, NULL, DDM_IMPLEMENTED); -+static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS); -+static SENSOR_DEVICE_ATTR(sfp_rx_los_all, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS_ALL); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, sfp_show_tx_rx_status, sfp_set_tx_disable, TX_DISABLE); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, sfp_show_tx_rx_status, NULL, TX_FAULT); -+static struct attribute *sfp_msa_attributes[] = { -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_port_type.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_ddm_implemented.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, -+ NULL -+}; -+ -+/* SFP ddm attributes for sysfs */ -+static struct attribute *sfp_ddm_attributes[] = { -+ NULL -+}; -+ -+static ssize_t sfp_eeprom_write(struct i2c_client *client, u8 command, const char *data, -+ int data_len) -+{ -+#if USE_I2C_BLOCK_READ -+ int status, retry = I2C_RW_RETRY_COUNT; -+ -+ if (data_len > I2C_SMBUS_BLOCK_MAX) { -+ data_len = I2C_SMBUS_BLOCK_MAX; -+ } -+ -+ while (retry) { -+ status = i2c_smbus_write_i2c_block_data(client, command, data_len, data); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ if (unlikely(status < 0)) { -+ return status; -+ } -+ -+ return data_len; -+#else -+ int status, retry = I2C_RW_RETRY_COUNT; -+ -+ while (retry) { -+ status = i2c_smbus_write_byte_data(client, command, *data); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ if (unlikely(status < 0)) { -+ return status; -+ } -+ -+ return 1; -+#endif -+ -+ -+} -+ -+static ssize_t sfp_port_write(struct sfp_port_data *data, -+ const char *buf, loff_t off, size_t count) -+{ -+ ssize_t retval = 0; -+ -+ if (unlikely(!count)) { -+ return count; -+ } -+ -+ /* -+ * Write data to chip, protecting against concurrent updates -+ * from this host, but not from other I2C masters. -+ */ -+ mutex_lock(&data->update_lock); -+ -+ while (count) { -+ ssize_t status; -+ -+ status = sfp_eeprom_write(data->client, off, buf, count); -+ if (status <= 0) { -+ if (retval == 0) { -+ retval = status; -+ } -+ break; -+ } -+ buf += status; -+ off += status; -+ count -= status; -+ retval += status; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ return retval; -+} -+ -+ -+static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, -+ struct bin_attribute *attr, -+ char *buf, loff_t off, size_t count) -+{ -+ struct sfp_port_data *data; -+ DEBUG_PRINT("%s(%d) offset = (%d), count = (%d)", off, count); -+ data = dev_get_drvdata(container_of(kobj, struct device, kobj)); -+ return sfp_port_write(data, buf, off, count); -+} -+ -+static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+#if USE_I2C_BLOCK_READ -+ int status, retry = I2C_RW_RETRY_COUNT; -+ -+ if (data_len > I2C_SMBUS_BLOCK_MAX) { -+ data_len = I2C_SMBUS_BLOCK_MAX; -+ } -+ -+ while (retry) { -+ status = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ if (unlikely(status < 0)) { -+ goto abort; -+ } -+ if (unlikely(status != data_len)) { -+ status = -EIO; -+ goto abort; -+ } -+ -+ //result = data_len; -+ -+abort: -+ return status; -+#else -+ int status, retry = I2C_RW_RETRY_COUNT; -+ -+ while (retry) { -+ status = i2c_smbus_read_byte_data(client, command); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ if (unlikely(status < 0)) { -+ dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, result); -+ goto abort; -+ } -+ -+ *data = (u8)status; -+ status = 1; -+ -+abort: -+ return status; -+#endif -+} -+ -+static ssize_t sfp_port_read(struct sfp_port_data *data, -+ char *buf, loff_t off, size_t count) -+{ -+ ssize_t retval = 0; -+ -+ if (unlikely(!count)) { -+ DEBUG_PRINT("Count = 0, return"); -+ return count; -+ } -+ -+ /* -+ * Read data from chip, protecting against concurrent updates -+ * from this host, but not from other I2C masters. -+ */ -+ mutex_lock(&data->update_lock); -+ -+ while (count) { -+ ssize_t status; -+ -+ status = sfp_eeprom_read(data->client, off, buf, count); -+ if (status <= 0) { -+ if (retval == 0) { -+ retval = status; -+ } -+ break; -+ } -+ -+ buf += status; -+ off += status; -+ count -= status; -+ retval += status; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ return retval; -+ -+} -+ -+static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, -+ struct bin_attribute *attr, -+ char *buf, loff_t off, size_t count) -+{ -+ struct sfp_port_data *data; -+ DEBUG_PRINT("offset = (%d), count = (%d)", off, count); -+ data = dev_get_drvdata(container_of(kobj, struct device, kobj)); -+ return sfp_port_read(data, buf, off, count); -+} -+ -+static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) -+{ -+ int err; -+ -+ sysfs_bin_attr_init(eeprom); -+ eeprom->attr.name = EEPROM_NAME; -+ eeprom->attr.mode = S_IWUSR | S_IRUGO; -+ eeprom->read = sfp_bin_read; -+ eeprom->write = sfp_bin_write; -+ eeprom->size = EEPROM_SIZE; -+ -+ /* Create eeprom file */ -+ err = sysfs_create_bin_file(kobj, eeprom); -+ if (err) { -+ return err; -+ } -+ -+ return 0; -+} -+ -+static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) -+{ -+ sysfs_remove_bin_file(kobj, eeprom); -+ return 0; -+} -+ -+static const struct attribute_group sfp_msa_group = { -+ .attrs = sfp_msa_attributes, -+}; -+ -+static int sfp_i2c_check_functionality(struct i2c_client *client) -+{ -+#if USE_I2C_BLOCK_READ -+ return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); -+#else -+ return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); -+#endif -+} -+ -+static int sfp_msa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, -+ struct sfp_msa_data **data) -+{ -+ int status; -+ struct sfp_msa_data *msa; -+ -+ if (!sfp_i2c_check_functionality(client)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ msa = kzalloc(sizeof(struct sfp_msa_data), GFP_KERNEL); -+ if (!msa) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &sfp_msa_group); -+ if (status) { -+ goto exit_free; -+ } -+ -+ /* init eeprom */ -+ status = sfp_sysfs_eeprom_init(&client->dev.kobj, &msa->eeprom.bin); -+ if (status) { -+ goto exit_remove; -+ } -+ -+ *data = msa; -+ dev_info(&client->dev, "sfp msa '%s'\n", client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); -+exit_free: -+ kfree(msa); -+exit: -+ -+ return status; -+} -+ -+static const struct attribute_group sfp_ddm_group = { -+ .attrs = sfp_ddm_attributes, -+}; -+ -+static int sfp_ddm_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, -+ struct sfp_ddm_data **data) -+{ -+ int status; -+ struct sfp_ddm_data *ddm; -+ -+ if (!sfp_i2c_check_functionality(client)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ ddm = kzalloc(sizeof(struct sfp_ddm_data), GFP_KERNEL); -+ if (!ddm) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &sfp_ddm_group); -+ if (status) { -+ goto exit_free; -+ } -+ -+ /* init eeprom */ -+ status = sfp_sysfs_eeprom_init(&client->dev.kobj, &ddm->eeprom.bin); -+ if (status) { -+ goto exit_remove; -+ } -+ -+ *data = ddm; -+ dev_info(&client->dev, "sfp ddm '%s'\n", client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &sfp_ddm_group); -+exit_free: -+ kfree(ddm); -+exit: -+ -+ return status; -+} -+ -+static const struct attribute_group qsfp_group = { -+ .attrs = qsfp_attributes, -+}; -+ -+static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, -+ struct qsfp_data **data) -+{ -+ int status; -+ struct qsfp_data *qsfp; -+ -+ if (!sfp_i2c_check_functionality(client)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); -+ if (!qsfp) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &qsfp_group); -+ if (status) { -+ goto exit_free; -+ } -+ -+ /* init eeprom */ -+ status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); -+ if (status) { -+ goto exit_remove; -+ } -+ -+ /* Bring QSFPs out of reset */ -+ accton_i2c_cpld_write(0x62, 0x15, 0x3F); -+ -+ *data = qsfp; -+ dev_info(&client->dev, "qsfp '%s'\n", client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &qsfp_group); -+exit_free: -+ kfree(qsfp); -+exit: -+ -+ return status; -+} -+ -+static int sfp_device_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct sfp_port_data *data = NULL; -+ -+ data = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); -+ if (!data) { -+ return -ENOMEM; -+ } -+ -+ i2c_set_clientdata(client, data); -+ mutex_init(&data->update_lock); -+ data->port = dev_id->driver_data; -+ data->client = client; -+ -+ if (dev_id->driver_data >= sfp1 && dev_id->driver_data <= sfp48) { -+ if (client->addr == SFP_EEPROM_A0_I2C_ADDR) { -+ data->driver_type = DRIVER_TYPE_SFP_MSA; -+ return sfp_msa_probe(client, dev_id, &data->msa); -+ } -+ else if (client->addr == SFP_EEPROM_A2_I2C_ADDR) { -+ data->driver_type = DRIVER_TYPE_SFP_DDM; -+ return sfp_ddm_probe(client, dev_id, &data->ddm); -+ } -+ } -+ else { /* sfp49 ~ sfp54 */ -+ if (client->addr == SFP_EEPROM_A0_I2C_ADDR) { -+ data->driver_type = DRIVER_TYPE_QSFP; -+ return qsfp_probe(client, dev_id, &data->qsfp); -+ } -+ } -+ -+ return -ENODEV; -+} -+ -+static int sfp_msa_remove(struct i2c_client *client, struct sfp_msa_data *data) -+{ -+ sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); -+ sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); -+ kfree(data); -+ return 0; -+} -+ -+static int sfp_ddm_remove(struct i2c_client *client, struct sfp_ddm_data *data) -+{ -+ sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); -+ sysfs_remove_group(&client->dev.kobj, &sfp_ddm_group); -+ kfree(data); -+ return 0; -+} -+ -+static int qfp_remove(struct i2c_client *client, struct qsfp_data *data) -+{ -+ sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); -+ sysfs_remove_group(&client->dev.kobj, &qsfp_group); -+ kfree(data); -+ return 0; -+} -+ -+static int sfp_device_remove(struct i2c_client *client) -+{ -+ struct sfp_port_data *data = i2c_get_clientdata(client); -+ -+ switch (data->driver_type) { -+ case DRIVER_TYPE_SFP_MSA: -+ return sfp_msa_remove(client, data->msa); -+ case DRIVER_TYPE_SFP_DDM: -+ return sfp_ddm_remove(client, data->ddm); -+ case DRIVER_TYPE_QSFP: -+ return qfp_remove(client, data->qsfp); -+ } -+ -+ return 0; -+} -+ -+static struct i2c_driver sfp_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ }, -+ .probe = sfp_device_probe, -+ .remove = sfp_device_remove, -+ .id_table = sfp_device_id, -+ .address_list = normal_i2c, -+}; -+ -+static int __init sfp_init(void) -+{ -+ extern int platform_accton_as5512_54x(void); -+ if(!platform_accton_as5512_54x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&sfp_driver); -+} -+ -+static void __exit sfp_exit(void) -+{ -+ i2c_del_driver(&sfp_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5512_54x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(sfp_init); -+module_exit(sfp_exit); -+ -+ diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5712_54x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5712_54x-device-drivers.patch deleted file mode 100644 index f7a74202..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5712_54x-device-drivers.patch +++ /dev/null @@ -1,2639 +0,0 @@ -Device driver patches for accton as5712 (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index b3abc7b..52a68eb 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1423,6 +1423,25 @@ config SENSORS_CPR_4011_4MXX - be called cpr_4011_4mxx. - - -+config SENSORS_ACCTON_AS5712_54x_FAN -+ tristate "Accton as5712 54x fan" -+ depends on I2C && I2C_MUX_ACCTON_AS5712_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5712 54x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5712_54x_fan. -+ -+config SENSORS_ACCTON_AS5712_54x_PSU -+ tristate "Accton as5712 54x psu" -+ depends on I2C && I2C_MUX_ACCTON_AS5712_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5712 54x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5712_54x_psu. -+ -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 1e90738..e2f3bce 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -21,6 +21,8 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o - - obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o - obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_FAN) += accton_as5712_54x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_PSU) += accton_as5712_54x_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as5712_54x_fan.c b/drivers/hwmon/accton_as5712_54x_fan.c -new file mode 100644 -index 0000000..df6c222 ---- /dev/null -+++ b/drivers/hwmon/accton_as5712_54x_fan.c -@@ -0,0 +1,442 @@ -+/* -+ * A hwmon driver for the Accton as5710 54x fan contrl -+ * -+ * Copyright (C) 2013 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 /* 10% ??*/ -+#define FAN_DUTY_CYCLE_MAX 100 /* 100% */ -+ -+#define CPLD_REG_FAN_STATUS_OFFSET 0xC -+#define CPLD_REG_FANR_STATUS_OFFSET 0x1F -+#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 LOCAL_DEBUG 0 -+ -+static struct accton_as5712_54x_fan *fan_data = NULL; -+ -+struct accton_as5712_54x_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_as5712_54x_fan_update_device(struct device *dev); -+static int accton_as5712_54x_fan_read_value(u8 reg); -+static int accton_as5712_54x_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 as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as5712_54x_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_as5712_54x_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_as5712_54x_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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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*/ -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); -+ break; -+ default: -+ if (LOCAL_DEBUG) -+ printk ("[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_as5712_54x_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_as5712_54x_fan_group = { -+ .attrs = accton_as5712_54x_fan_attributes, -+}; -+ -+static int accton_as5712_54x_fan_read_value(u8 reg) -+{ -+ return as5712_54x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5712_54x_fan_write_value(u8 reg, u8 value) -+{ -+ return as5712_54x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5712_54x_fan_update_device(struct device *dev) -+{ -+ int speed, r_speed, fault, r_fault, ctrl_speed, direction; -+ int i; -+ -+ mutex_lock(&fan_data->update_lock); -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5712_54x_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; -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5712_54x_fan update 2 \n"); -+ -+ fault = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); -+ r_fault = accton_as5712_54x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); -+ direction = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); -+ ctrl_speed = accton_as5712_54x_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); -+ -+ if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); -+ -+ for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; -+ if (LOCAL_DEBUG) -+ printk ("[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_as5712_54x_fan_read_value(fan_speed_reg[i]); -+ r_speed = accton_as5712_54x_fan_read_value(fanr_speed_reg[i]); -+ if ( (speed < 0) || (r_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[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_as5712_54x_fan_probe(struct platform_device *pdev) -+{ -+ int status = -1; -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &accton_as5712_54x_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_as5712_54x_fan\n"); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &accton_as5712_54x_fan_group); -+exit: -+ return status; -+} -+ -+static int accton_as5712_54x_fan_remove(struct platform_device *pdev) -+{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5712_54x_fan_group); -+ -+ return 0; -+} -+ -+#define DRVNAME "as5712_54x_fan" -+ -+static struct platform_driver accton_as5712_54x_fan_driver = { -+ .probe = accton_as5712_54x_fan_probe, -+ .remove = accton_as5712_54x_fan_remove, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5712_54x_fan_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5712_54x(void); -+ if(!platform_accton_as5712_54x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5712_54x_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ fan_data = kzalloc(sizeof(struct accton_as5712_54x_fan), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5712_54x_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_as5712_54x_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5712_54x_fan_exit(void) -+{ -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&accton_as5712_54x_fan_driver); -+ kfree(fan_data); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5712_54x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_as5712_54x_fan_init); -+module_exit(accton_as5712_54x_fan_exit); -+ -diff --git a/drivers/hwmon/accton_as5712_54x_psu.c b/drivers/hwmon/accton_as5712_54x_psu.c -new file mode 100644 -index 0000000..0890e92 ---- /dev/null -+++ b/drivers/hwmon/accton_as5712_54x_psu.c -@@ -0,0 +1,293 @@ -+/* -+ * An hwmon driver for accton as5712_54x Power Module -+ * -+ * Copyright (C) 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 -+ -+static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); -+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 as5712_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x38, 0x3b, 0x50, 0x53, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as5712_54x_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 as5712_54x_psu_data *as5712_54x_psu_update_device(struct device *dev); -+ -+enum as5712_54x_psu_sysfs_attributes { -+ PSU_INDEX, -+ PSU_PRESENT, -+ PSU_MODEL_NAME, -+ PSU_POWER_GOOD -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); -+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 *as5712_54x_psu_attributes[] = { -+ &sensor_dev_attr_psu_index.dev_attr.attr, -+ &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_index(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5712_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", data->index); -+} -+ -+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 as5712_54x_psu_data *data = as5712_54x_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 as5712_54x_psu_data *data = as5712_54x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s", data->model_name); -+} -+ -+static const struct attribute_group as5712_54x_psu_group = { -+ .attrs = as5712_54x_psu_attributes, -+}; -+ -+static int as5712_54x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5712_54x_psu_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5712_54x_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, &as5712_54x_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 == 0x38 || client->addr == 0x50) { -+ data->index = 1; -+ } -+ else if (client->addr == 0x3b || client->addr == 0x53) { -+ 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, &as5712_54x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5712_54x_psu_remove(struct i2c_client *client) -+{ -+ struct as5712_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5712_54x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as5712_54x_psu_id[] = { -+ { "as5712_54x_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as5712_54x_psu_id); -+ -+static struct i2c_driver as5712_54x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5712_54x_psu", -+ }, -+ .probe = as5712_54x_psu_probe, -+ .remove = as5712_54x_psu_remove, -+ .id_table = as5712_54x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5712_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as5712_54x_psu_data *as5712_54x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5712_54x_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 = -1; -+ -+ dev_dbg(&client->dev, "Starting as5712_54x update\n"); -+ -+ /* Read model name */ -+ if (client->addr == 0x38 || client->addr == 0x3b) { -+ /* AC power */ -+ status = as5712_54x_psu_read_block(client, 0x26, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ else { -+ /* DC power */ -+ status = as5712_54x_psu_read_block(client, 0x50, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ -+ /* Read psu status */ -+ status = as5712_54x_i2c_cpld_read(0x60, 0x2); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); -+ } -+ else { -+ data->status = status; -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as5712_54x_psu_init(void) -+{ -+ extern int platform_accton_as5712_54x(void); -+ if(!platform_accton_as5712_54x()) { -+ return -ENODEV; -+ } -+ return i2c_add_driver(&as5712_54x_psu_driver); -+} -+ -+static void __exit as5712_54x_psu_exit(void) -+{ -+ i2c_del_driver(&as5712_54x_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5712_54x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as5712_54x_psu_init); -+module_exit(as5712_54x_psu_exit); -+ -diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig -index 73ed997..339e2b2 100644 ---- a/drivers/i2c/muxes/Kconfig -+++ b/drivers/i2c/muxes/Kconfig -@@ -5,6 +5,16 @@ - menu "Multiplexer I2C Chip support" - depends on I2C_MUX - -+ -+config I2C_MUX_ACCTON_AS5712_54x_CPLD -+ tristate "Accton as5712_54x CPLD I2C multiplexer" -+ help -+ If you say yes here you get support for the Accton CPLD -+ I2C mux devices. -+ -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mux-accton_as5712_54x_cpld. -+ - config I2C_MUX_GPIO - tristate "GPIO-based I2C multiplexer" - depends on GENERIC_GPIO -diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile -index 7fbdd2f..997522c 100644 ---- a/drivers/i2c/muxes/Makefile -+++ b/drivers/i2c/muxes/Makefile -@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o - obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o - obj-$(CONFIG_I2C_MUX_DNI_6448) += dni_6448_i2c_mux.o - obj-$(CONFIG_I2C_MUX_QUANTA) += quanta-i2cmux.o - obj-$(CONFIG_I2C_MUX_QUANTA_LY2) += quanta-ly2-i2c-mux.o -+obj-$(CONFIG_I2C_MUX_ACCTON_AS5712_54x_CPLD) += i2c-mux-accton_as5712_54x_cpld.o - - ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG -diff --git a/drivers/i2c/muxes/i2c-mux-accton_as5712_54x_cpld.c b/drivers/i2c/muxes/i2c-mux-accton_as5712_54x_cpld.c -new file mode 100644 -index 0000000..6381db5 ---- /dev/null -+++ b/drivers/i2c/muxes/i2c-mux-accton_as5712_54x_cpld.c -@@ -0,0 +1,466 @@ -+/* -+ * I2C multiplexer -+ * -+ * Copyright (C) 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 as5712_54x 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 as5712_dmi_table[] = { -+ { -+ .ident = "Accton AS5712", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5712"), -+ }, -+ }, -+ { -+ .ident = "Accton AS5712", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5712"), -+ }, -+ }, -+}; -+ -+int platform_accton_as5712_54x(void) -+{ -+ return dmi_check_system(as5712_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as5712_54x); -+ -+#define NUM_OF_CPLD1_CHANS 0x0 -+#define NUM_OF_CPLD2_CHANS 0x18 -+#define NUM_OF_CPLD3_CHANS 0x1E -+#define CPLD_CHANNEL_SELECT_REG 0x2 -+#define CPLD_DESELECT_CHANNEL 0xFF -+ -+#if 0 -+#define NUM_OF_ALL_CPLD_CHANS (NUM_OF_CPLD2_CHANS + NUM_OF_CPLD3_CHANS) -+#endif -+ -+#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 { -+ as5712_54x_cpld2, -+ as5712_54x_cpld3, -+ as5712_54x_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 */ -+}; -+ -+#if 0 -+/* The mapping table between mux index and adapter index -+ array index : the mux index -+ the content : adapter index -+ */ -+static int mux_adap_map[NUM_OF_ALL_CPLD_CHANS]; -+#endif -+ -+struct chip_desc { -+ u8 nchans; -+ u8 deselectChan; -+}; -+ -+/* Provide specs for the PCA954x types we know about */ -+static const struct chip_desc chips[] = { -+ [as5712_54x_cpld1] = { -+ .nchans = NUM_OF_CPLD1_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ }, -+ [as5712_54x_cpld2] = { -+ .nchans = NUM_OF_CPLD2_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ }, -+ [as5712_54x_cpld3] = { -+ .nchans = NUM_OF_CPLD3_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ } -+}; -+ -+static const struct i2c_device_id accton_i2c_cpld_mux_id[] = { -+ { "as5712_54x_cpld1", as5712_54x_cpld1 }, -+ { "as5712_54x_cpld2", as5712_54x_cpld2 }, -+ { "as5712_54x_cpld3", as5712_54x_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) -+{ -+#if 0 -+ int ret = -ENODEV; -+ -+ //if (adap->algo->master_xfer) { -+ if (0) -+ struct i2c_msg msg; -+ char buf[2]; -+ -+ msg.addr = client->addr; -+ msg.flags = 0; -+ msg.len = 2; -+ buf[0] = 0x2; -+ buf[1] = val; -+ msg.buf = buf; -+ ret = adap->algo->master_xfer(adap, &msg, 1); -+ } -+ else { -+ union i2c_smbus_data data; -+ ret = adap->algo->smbus_xfer(adap, client->addr, -+ client->flags, -+ I2C_SMBUS_WRITE, -+ 0x2, I2C_SMBUS_BYTE, &data); -+ } -+ -+ return ret; -+#else -+ 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, CPLD_CHANNEL_SELECT_REG, -+ I2C_SMBUS_BYTE_DATA, &data); -+ if (res != -EAGAIN) -+ break; -+ if (time_after(jiffies, -+ orig_jiffies + adap->timeout)) -+ break; -+ } -+ } -+ -+ return res; -+#endif -+} -+ -+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); -+ -+#if 0 -+ /* Write the mux register at addr to verify -+ * that the mux is in fact present. -+ */ -+ if (i2c_smbus_write_byte(client, 0) < 0) { -+ dev_warn(&client->dev, "probe failed\n"); -+ goto exit_free; -+ } -+#endif -+ -+ data->type = id->driver_data; -+ -+ if (data->type == as5712_54x_cpld2 || data->type == as5712_54x_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++) { -+#if 0 -+ int idx; -+#endif -+ data->virt_adaps[chan] = i2c_add_mux_adapter(adap, &client->dev, client, 0, chan, -+ 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; -+ } -+ -+#if 0 -+ idx = (data->type - as5712_54x_cpld2) * NUM_OF_CPLD2_CHANS + chan; -+ mux_adap_map[idx] = data->virt_adaps[chan]->nr; -+#endif -+ } -+ -+ 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 as5712_54x_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(as5712_54x_i2c_cpld_read); -+ -+int as5712_54x_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(as5712_54x_i2c_cpld_write); -+ -+#if 0 -+int accton_i2c_cpld_mux_get_index(int adap_index) -+{ -+ int i; -+ -+ for (i = 0; i < NUM_OF_ALL_CPLD_CHANS; i++) { -+ if (mux_adap_map[i] == adap_index) { -+ return i; -+ } -+ } -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL(accton_i2c_cpld_mux_get_index); -+#endif -+ -+static struct i2c_driver accton_i2c_cpld_mux_driver = { -+ .driver = { -+ .name = "as5712_54x_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/drivers/leds/Kconfig b/drivers/leds/Kconfig -index ff203a4..361ef45 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -40,6 +40,13 @@ config LEDS_ATMEL_PWM - This option enables support for LEDs driven using outputs - of the dedicated PWM controller found on newer Atmel SOCs. - -+config LEDS_ACCTON_AS5712_54x -+ tristate "LED support for the Accton as5712 54x" -+ depends on LEDS_CLASS && I2C_MUX_ACCTON_AS5712_54x_CPLD -+ help -+ This option enables support for the LEDs on the Accton as5712 54x. -+ Say Y to enable LEDs on the Accton as5712 54x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index e4f6bf5..db2d096 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -43,6 +43,7 @@ obj-$(CONFIG_LEDS_NS2) += leds-ns2.o - obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o - obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o - obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o -+obj-$(CONFIG_LEDS_ACCTON_AS5712_54x) += leds-accton_as5712_54x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as5712_54x.c b/drivers/leds/leds-accton_as5712_54x.c -new file mode 100644 -index 0000000..5e346b2 ---- /dev/null -+++ b/drivers/leds/leds-accton_as5712_54x.c -@@ -0,0 +1,597 @@ -+/* -+ * A LED driver for the accton_as5712_54x_led -+ * -+ * Copyright (C) 2013 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 as5712_54x_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int as5712_54x_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 "as5712_54x_led" -+ -+struct accton_as5712_54x_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_as5712_54x_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_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 0x0C -+#define LED_TYPE_FAN3_REG_MASK 0x30 -+#define LED_TYPE_FAN4_REG_MASK 0xC0 -+#define LED_TYPE_FAN5_REG_MASK 0x03 -+#define LED_TYPE_FAN6_REG_MASK 0x0C -+ -+#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 */ -+ 0x16, /* FAN1-4 LED */ -+ 0x17, /* FAN4-6 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 << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, -+{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 << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, -+{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_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, 2}, -+ {'2', LED_TYPE_FAN2, 2}, -+ {'3', LED_TYPE_FAN3, 2}, -+ {'4', LED_TYPE_FAN4, 2}, -+ {'5', LED_TYPE_FAN5, 3} -+}; -+ -+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 ((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; -+ -+ reg_val = led_type_mode_data[i].mode_mask | -+ (reg_val & (~led_type_mode_data[i].type_mask)); -+ } -+ -+ return reg_val; -+} -+ -+static int accton_as5712_54x_led_read_value(u8 reg) -+{ -+ return as5712_54x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5712_54x_led_write_value(u8 reg, u8 value) -+{ -+ return as5712_54x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5712_54x_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_as5712_54x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as5712_54x_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_as5712_54x_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_as5712_54x_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_as5712_54x_led_write_value(reg, reg_val); -+ -+ /* to prevent the slow-update issue */ -+ ledctl->valid = 0; -+ -+exit: -+ mutex_unlock(&ledctl->update_lock); -+} -+ -+static void accton_as5712_54x_led_psu_1_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); -+} -+ -+static enum led_brightness accton_as5712_54x_led_psu_1_get(struct led_classdev *cdev) -+{ -+ accton_as5712_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); -+} -+ -+static void accton_as5712_54x_led_psu_2_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); -+} -+ -+static enum led_brightness accton_as5712_54x_led_psu_2_get(struct led_classdev *cdev) -+{ -+ accton_as5712_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); -+} -+ -+static void accton_as5712_54x_led_fan_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); -+} -+ -+static enum led_brightness accton_as5712_54x_led_fan_get(struct led_classdev *cdev) -+{ -+ accton_as5712_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); -+} -+ -+ -+static void accton_as5712_54x_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_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); -+ return; -+ } -+ } -+} -+ -+ -+static enum led_brightness accton_as5712_54x_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_as5712_54x_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_as5712_54x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as5712_54x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as5712_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as5712_54x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5712_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as5712_54x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as5712_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static struct led_classdev accton_as5712_54x_leds[] = { -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as5712_54x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_psu_1_set, -+ .brightness_get = accton_as5712_54x_led_psu_1_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as5712_54x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_psu_2_set, -+ .brightness_get = accton_as5712_54x_led_psu_2_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as5712_54x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fan_set, -+ .brightness_get = accton_as5712_54x_led_fan_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN1] = { -+ .name = "accton_as5712_54x_led::fan1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fanx_set, -+ .brightness_get = accton_as5712_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN2] = { -+ .name = "accton_as5712_54x_led::fan2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fanx_set, -+ .brightness_get = accton_as5712_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN3] = { -+ .name = "accton_as5712_54x_led::fan3", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fanx_set, -+ .brightness_get = accton_as5712_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN4] = { -+ .name = "accton_as5712_54x_led::fan4", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fanx_set, -+ .brightness_get = accton_as5712_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN5] = { -+ .name = "accton_as5712_54x_led::fan5", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_fanx_set, -+ .brightness_get = accton_as5712_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as5712_54x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_diag_set, -+ .brightness_get = accton_as5712_54x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as5712_54x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5712_54x_led_loc_set, -+ .brightness_get = accton_as5712_54x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as5712_54x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { -+ led_classdev_suspend(&accton_as5712_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5712_54x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { -+ led_classdev_resume(&accton_as5712_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5712_54x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as5712_54x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as5712_54x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as5712_54x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as5712_54x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5712_54x_leds); i++) { -+ led_classdev_unregister(&accton_as5712_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as5712_54x_led_driver = { -+ .probe = accton_as5712_54x_led_probe, -+ .remove = accton_as5712_54x_led_remove, -+ .suspend = accton_as5712_54x_led_suspend, -+ .resume = accton_as5712_54x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5712_54x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5712_54x(void); -+ if(!platform_accton_as5712_54x()) { -+ return -ENODEV; -+ } -+ ret = platform_driver_register(&accton_as5712_54x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as5712_54x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5712_54x_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_as5712_54x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5712_54x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as5712_54x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as5712_54x_led_init); -+module_exit(accton_as5712_54x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5712_54x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 7c7b208..4e5d6dc 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -73,6 +73,16 @@ config EEPROM_MAX6875 - will be called max6875. - - -+config EEPROM_ACCTON_AS5712_54x_SFP -+ tristate "Accton as5712 54x sfp" -+ depends on I2C && I2C_MUX_ACCTON_AS5712_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5712 54x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5712_54x_sfp. -+ -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 9edd559..807158a 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -6,4 +6,5 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o - obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o - obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o - obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o -+obj-$(CONFIG_EEPROM_ACCTON_AS5712_54x_SFP) += accton_as5712_54x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as5712_54x_sfp.c b/drivers/misc/eeprom/accton_as5712_54x_sfp.c -new file mode 100644 -index 0000000..54d885e ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as5712_54x_sfp.c -@@ -0,0 +1,672 @@ -+/* -+ * An hwmon driver for accton as5712_54x sfp -+ * -+ * Copyright (C) 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 NUM_OF_SFP_PORT 54 -+#define BIT_INDEX(i) (1ULL << (i)) -+ -+#if 0 -+static ssize_t show_status(struct device *dev, struct device_attribute *da,char *buf); -+static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); -+static int as5712_54x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as5712_54x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+#endif -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as5712_54x_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 status[4]; /* bit0:port0, bit1:port1 and so on */ -+ /* index 0 => is_present -+ 1 => tx_fail -+ 2 => tx_disable -+ 3 => rx_loss */ -+}; -+ -+/* The table maps active port to cpld port. -+ * Array index 0 is for active port 1, -+ * index 1 for active port 2, and so on. -+ * The array content implies cpld port index. -+ */ -+static const u8 cpld_to_front_port_table[] = -+{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -+ 49, 52, 50, 53, 51, 54}; -+ -+#define CPLD_PORT_TO_FRONT_PORT(port) (cpld_to_front_port_table[port]) -+ -+static struct as5712_54x_sfp_data *as5712_54x_sfp_update_device(struct device *dev, int update_eeprom); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_status(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 set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+extern int as5712_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as5712_54x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as5712_54x_sfp_sysfs_attributes { -+ SFP_IS_PRESENT, -+ SFP_TX_FAULT, -+ SFP_TX_DISABLE, -+ SFP_RX_LOSS, -+ SFP_PORT_NUMBER, -+ SFP_EEPROM, -+ SFP_RX_LOS_ALL, -+ SFP_IS_PRESENT_ALL, -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_status, NULL, SFP_IS_PRESENT); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, show_status, NULL, SFP_TX_FAULT); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, show_status, set_tx_disable, SFP_TX_DISABLE); -+static SENSOR_DEVICE_ATTR(sfp_rx_loss, S_IRUGO, show_status,NULL, SFP_RX_LOSS); -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); -+static SENSOR_DEVICE_ATTR(sfp_rx_los_all, S_IRUGO, show_status,NULL, SFP_RX_LOS_ALL); -+static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_status,NULL, SFP_IS_PRESENT_ALL); -+ -+static struct attribute *as5712_54x_sfp_attributes[] = { -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_loss.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, -+ &sensor_dev_attr_sfp_eeprom.dev_attr.attr, -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los_all.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 as5712_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); -+} -+ -+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 as5712_54x_sfp_data *data; -+ u8 val; -+ int values[7]; -+ -+ /* 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) -+ -+ if(attr->index == SFP_RX_LOS_ALL) { -+ /* -+ * Report the RX_LOS status for all ports. -+ * This does not depend on the currently active SFP selector. -+ */ -+ -+ /* RX_LOS Ports 1-8 */ -+ VALIDATED_READ(buf, values[0], as5712_54x_i2c_cpld_read(0x61, 0x0F), 0); -+ /* RX_LOS Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], as5712_54x_i2c_cpld_read(0x61, 0x10), 0); -+ /* RX_LOS Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], as5712_54x_i2c_cpld_read(0x61, 0x11), 0); -+ /* RX_LOS Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], as5712_54x_i2c_cpld_read(0x62, 0x0F), 0); -+ /* RX_LOS Ports 33-40 */ -+ VALIDATED_READ(buf, values[4], as5712_54x_i2c_cpld_read(0x62, 0x10), 0); -+ /* RX_LOS Ports 41-48 */ -+ VALIDATED_READ(buf, values[5], as5712_54x_i2c_cpld_read(0x62, 0x11), 0); -+ -+ /** Return values 1 -> 48 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5]); -+ } -+ -+ if(attr->index == SFP_IS_PRESENT_ALL) { -+ /* -+ * Report the SFP_PRESENCE status for all ports. -+ * This does not depend on the currently active SFP selector. -+ */ -+ -+ /* SFP_PRESENT Ports 1-8 */ -+ VALIDATED_READ(buf, values[0], as5712_54x_i2c_cpld_read(0x61, 0x6), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], as5712_54x_i2c_cpld_read(0x61, 0x7), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], as5712_54x_i2c_cpld_read(0x61, 0x8), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], as5712_54x_i2c_cpld_read(0x62, 0x6), 1); -+ /* SFP_PRESENT Ports 33-40 */ -+ VALIDATED_READ(buf, values[4], as5712_54x_i2c_cpld_read(0x62, 0x7), 1); -+ /* SFP_PRESENT Ports 41-48 */ -+ VALIDATED_READ(buf, values[5], as5712_54x_i2c_cpld_read(0x62, 0x8), 1); -+ /* QSFP_PRESENT Ports 49-54 */ -+ VALIDATED_READ(buf, values[6], as5712_54x_i2c_cpld_read(0x62, 0x14), 1); -+ -+ /* Return values 1 -> 54 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5], -+ values[6] & 0x3F); -+ } -+ /* -+ * The remaining attributes are gathered on a per-selected-sfp basis. -+ */ -+ data = as5712_54x_sfp_update_device(dev, 0); -+ if (attr->index == SFP_IS_PRESENT) { -+ val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 0 : 1; -+ } -+ else { -+ val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 1 : 0; -+ } -+ -+ return sprintf(buf, "%d", val); -+} -+ -+static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); -+ unsigned short cpld_addr = 0; -+ u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; -+ long disable; -+ int error; -+ -+ /* Tx disable is not supported for QSFP ports(49-54) */ -+ if (data->port >= 48) { -+ return -EINVAL; -+ } -+ -+ error = kstrtol(buf, 10, &disable); -+ if (error) { -+ return error; -+ } -+ -+ mutex_lock(&data->update_lock); -+ -+ if(data->port < 24) { -+ cpld_addr = 0x61; -+ cpld_reg = 0xC + data->port / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ else { -+ cpld_addr = 0x62; -+ cpld_reg = 0xC + (data->port - 24) / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ -+ cpld_val = as5712_54x_i2c_cpld_read(cpld_addr, cpld_reg); -+ -+ /* Update tx_disable status */ -+ if (disable) { -+ data->status[SFP_TX_DISABLE] |= BIT_INDEX(data->port); -+ cpld_val |= cpld_bit; -+ } -+ else { -+ data->status[SFP_TX_DISABLE] &= ~BIT_INDEX(data->port); -+ cpld_val &= ~cpld_bit; -+ } -+ -+ as5712_54x_i2c_cpld_write(cpld_addr, cpld_reg, cpld_val); -+ -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as5712_54x_sfp_data *data = as5712_54x_sfp_update_device(dev, 1); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if ((data->status[SFP_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 as5712_54x_sfp_group = { -+ .attrs = as5712_54x_sfp_attributes, -+}; -+ -+static int as5712_54x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5712_54x_sfp_data *data; -+ int status; -+ -+ extern int platform_accton_as5712_54x(void); -+ if(!platform_accton_as5712_54x()) { -+ return -ENODEV; -+ } -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5712_54x_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, &as5712_54x_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, &as5712_54x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5712_54x_sfp_remove(struct i2c_client *client) -+{ -+ struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5712_54x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as5712_54x_sfp1, as5712_54x_sfp2, as5712_54x_sfp3, as5712_54x_sfp4, -+as5712_54x_sfp5, as5712_54x_sfp6, as5712_54x_sfp7, as5712_54x_sfp8, -+as5712_54x_sfp9, as5712_54x_sfp10, as5712_54x_sfp11,as5712_54x_sfp12, -+as5712_54x_sfp13, as5712_54x_sfp14, as5712_54x_sfp15,as5712_54x_sfp16, -+as5712_54x_sfp17, as5712_54x_sfp18, as5712_54x_sfp19,as5712_54x_sfp20, -+as5712_54x_sfp21, as5712_54x_sfp22, as5712_54x_sfp23,as5712_54x_sfp24, -+as5712_54x_sfp25, as5712_54x_sfp26, as5712_54x_sfp27,as5712_54x_sfp28, -+as5712_54x_sfp29, as5712_54x_sfp30, as5712_54x_sfp31,as5712_54x_sfp32, -+as5712_54x_sfp33, as5712_54x_sfp34, as5712_54x_sfp35,as5712_54x_sfp36, -+as5712_54x_sfp37, as5712_54x_sfp38, as5712_54x_sfp39,as5712_54x_sfp40, -+as5712_54x_sfp41, as5712_54x_sfp42, as5712_54x_sfp43,as5712_54x_sfp44, -+as5712_54x_sfp45, as5712_54x_sfp46, as5712_54x_sfp47,as5712_54x_sfp48, -+as5712_54x_sfp49, as5712_54x_sfp52, as5712_54x_sfp50,as5712_54x_sfp53, -+as5712_54x_sfp51, as5712_54x_sfp54 -+}; -+ -+static const struct i2c_device_id as5712_54x_sfp_id[] = { -+{ "as5712_54x_sfp1", as5712_54x_sfp1 }, { "as5712_54x_sfp2", as5712_54x_sfp2 }, -+{ "as5712_54x_sfp3", as5712_54x_sfp3 }, { "as5712_54x_sfp4", as5712_54x_sfp4 }, -+{ "as5712_54x_sfp5", as5712_54x_sfp5 }, { "as5712_54x_sfp6", as5712_54x_sfp6 }, -+{ "as5712_54x_sfp7", as5712_54x_sfp7 }, { "as5712_54x_sfp8", as5712_54x_sfp8 }, -+{ "as5712_54x_sfp9", as5712_54x_sfp9 }, { "as5712_54x_sfp10", as5712_54x_sfp10 }, -+{ "as5712_54x_sfp11", as5712_54x_sfp11 }, { "as5712_54x_sfp12", as5712_54x_sfp12 }, -+{ "as5712_54x_sfp13", as5712_54x_sfp13 }, { "as5712_54x_sfp14", as5712_54x_sfp14 }, -+{ "as5712_54x_sfp15", as5712_54x_sfp15 }, { "as5712_54x_sfp16", as5712_54x_sfp16 }, -+{ "as5712_54x_sfp17", as5712_54x_sfp17 }, { "as5712_54x_sfp18", as5712_54x_sfp18 }, -+{ "as5712_54x_sfp19", as5712_54x_sfp19 }, { "as5712_54x_sfp20", as5712_54x_sfp20 }, -+{ "as5712_54x_sfp21", as5712_54x_sfp21 }, { "as5712_54x_sfp22", as5712_54x_sfp22 }, -+{ "as5712_54x_sfp23", as5712_54x_sfp23 }, { "as5712_54x_sfp24", as5712_54x_sfp24 }, -+{ "as5712_54x_sfp25", as5712_54x_sfp25 }, { "as5712_54x_sfp26", as5712_54x_sfp26 }, -+{ "as5712_54x_sfp27", as5712_54x_sfp27 }, { "as5712_54x_sfp28", as5712_54x_sfp28 }, -+{ "as5712_54x_sfp29", as5712_54x_sfp29 }, { "as5712_54x_sfp30", as5712_54x_sfp30 }, -+{ "as5712_54x_sfp31", as5712_54x_sfp31 }, { "as5712_54x_sfp32", as5712_54x_sfp32 }, -+{ "as5712_54x_sfp33", as5712_54x_sfp33 }, { "as5712_54x_sfp34", as5712_54x_sfp34 }, -+{ "as5712_54x_sfp35", as5712_54x_sfp35 }, { "as5712_54x_sfp36", as5712_54x_sfp36 }, -+{ "as5712_54x_sfp37", as5712_54x_sfp37 }, { "as5712_54x_sfp38", as5712_54x_sfp38 }, -+{ "as5712_54x_sfp39", as5712_54x_sfp39 }, { "as5712_54x_sfp40", as5712_54x_sfp40 }, -+{ "as5712_54x_sfp41", as5712_54x_sfp41 }, { "as5712_54x_sfp42", as5712_54x_sfp42 }, -+{ "as5712_54x_sfp43", as5712_54x_sfp43 }, { "as5712_54x_sfp44", as5712_54x_sfp44 }, -+{ "as5712_54x_sfp45", as5712_54x_sfp45 }, { "as5712_54x_sfp46", as5712_54x_sfp46 }, -+{ "as5712_54x_sfp47", as5712_54x_sfp47 }, { "as5712_54x_sfp48", as5712_54x_sfp48 }, -+{ "as5712_54x_sfp49", as5712_54x_sfp49 }, { "as5712_54x_sfp50", as5712_54x_sfp50 }, -+{ "as5712_54x_sfp51", as5712_54x_sfp51 }, { "as5712_54x_sfp52", as5712_54x_sfp52 }, -+{ "as5712_54x_sfp53", as5712_54x_sfp53 }, { "as5712_54x_sfp54", as5712_54x_sfp54 }, -+ -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as5712_54x_sfp_id); -+ -+static struct i2c_driver as5712_54x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5712_54x_sfp", -+ }, -+ .probe = as5712_54x_sfp_probe, -+ .remove = as5712_54x_sfp_remove, -+ .id_table = as5712_54x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5712_54x_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; -+} -+ -+#define ALWAYS_UPDATE_DEVICE 1 -+ -+static struct as5712_54x_sfp_data *as5712_54x_sfp_update_device(struct device *dev, int update_eeprom) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (ALWAYS_UPDATE_DEVICE || time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid) { -+ int status = -1; -+ int i = 0, j = 0; -+ -+ data->valid = 0; -+ //dev_dbg(&client->dev, "Starting as5712_54x sfp status update\n"); -+ memset(data->status, 0, sizeof(data->status)); -+ -+ /* Read status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 12; j++) { -+ status = as5712_54x_i2c_cpld_read(0x61+i, 0x6+j); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ goto exit; -+ } -+ -+ data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ /* -+ * Bring QSFPs out of reset, -+ * This is a temporary fix until the QSFP+_MOD_RST register -+ * can be exposed through the driver. -+ */ -+ as5712_54x_i2c_cpld_write(0x62, 0x15, 0x3F); -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ status = as5712_54x_i2c_cpld_read(0x62, 0x14); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ } -+ else { -+ data->status[SFP_IS_PRESENT] |= (u64)status << 48; -+ } -+ -+ 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->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) == 0) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom); i++) { -+ status = as5712_54x_sfp_read_byte(client, i, data->eeprom + i); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", -+ CPLD_PORT_TO_FRONT_PORT(data->port)); -+ goto exit; -+ } -+ } -+ } -+ } -+ -+ data->valid = 1; -+ data->last_updated = jiffies; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+module_i2c_driver(as5712_54x_sfp_driver); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5712_54x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+#if 0 -+ int i = 0, j = 0; -+ -+ data->valid = 0; -+ //dev_dbg(&client->dev, "Starting as5712_54x sfp update\n"); -+ memset(data->status, 0, sizeof(data->status)); -+ -+ /* Read status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 12; j++) { -+ status = as5712_54x_i2c_cpld_read(0x61+i, 0x6+j); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ continue; -+ } -+ -+ data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ status = as5712_54x_i2c_cpld_read(0x62, 0x14); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ } -+ else { -+ data->status[SFP_IS_PRESENT] |= (u64)status << 48; -+ } -+#endif -+ -+/* Reserver to prevent from CPLD port mapping is changed -+ */ -+#if 0 -+BIT_INDEX(port_present_index[data->port]) -+/* The bit index of is_present field read from CPLD -+ * Array index 0 is for as5712_54x_sfp1, -+ * index 1 is for as5712_54x_sfp2, and so on. -+ */ -+static const int port_present_index[] = { -+ 4, 5, 6, 7, 9, 8, 11, 10, -+ 0, 1, 2, 3, 12, 13, 14, 15, -+16, 17, 18, 19, 28, 29, 30, 31, -+20, 21, 22, 23, 24, 25, 26, 27 -+}; -+#endif -+ -+#if 0 -+static struct as5712_54x_sfp_data *as5712_54x_sfp_update_status(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5712_54x_sfp_data *data = i2c_get_clientdata(client); -+ int status = -1; -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->status_last_updated + HZ + HZ / 2) -+ || !data->status_valid) { -+ int status = -1; -+ int i = 0, j = 0; -+ -+ data->status_valid = 0; -+ //dev_dbg(&client->dev, "Starting as5712_54x sfp status update\n"); -+ memset(data->status, 0, sizeof(data->status)); -+ -+ /* Read status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 12; j++) { -+ status = as5712_54x_i2c_cpld_read(0x61+i, 0x6+j); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ goto exit; -+ } -+ -+ data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ /* -+ * Bring QSFPs out of reset, -+ * This is a temporary fix until the QSFP+_MOD_RST register -+ * can be exposed through the driver. -+ */ -+ as5712_54x_i2c_cpld_write(0x62, 0x15, 0x3F); -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ status = as5712_54x_i2c_cpld_read(0x62, 0x14); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ } -+ else { -+ data->status[SFP_IS_PRESENT] |= (u64)status << 48; -+ } -+ -+ data->status_valid = 1; -+ data->status_last_updated = jiffies; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static struct as5712_54x_sfp_data *as5712_54x_sfp_update_eeprom(struct device *dev) -+{ -+ struct as5712_54x_sfp_data *data = NULL; -+ -+ data = as5712_54x_sfp_update_status(dev); -+ -+ if (data == NULL || data->status_valid == 0) { -+ data->eeprom_valid = 0; -+ return data; -+ } -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->eeprom_last_updated + HZ + HZ / 2) -+ || !data->eeprom_valid) { -+ int status = -1; -+ int i = 0; -+ -+ /* Read eeprom data based on port number */ -+ memset(data->eeprom, 0, sizeof(data->eeprom)); -+ -+ /* Check if the port is present */ -+ if ((data->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) == 0) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { -+ status = as5712_54x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, -+ data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), -+ I2C_SMBUS_BLOCK_MAX); -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", -+ CPLD_PORT_TO_FRONT_PORT(data->port)); -+ goto exit; -+ } -+ } -+ } -+ -+ data->eeprom_last_updated = jiffies; -+ data->eeprom_valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+#endif diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54t-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54t-device-drivers.patch deleted file mode 100644 index 17db04c3..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54t-device-drivers.patch +++ /dev/null @@ -1,1861 +0,0 @@ -Device driver patches for accton as5812_54t (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 73ee085..89c619d 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1555,6 +1555,24 @@ config SENSORS_ACCTON_AS6812_32x_PSU - - This driver can also be built as a module. If so, the module will - be called accton_as6812_32x_psu. -+ -+config SENSORS_ACCTON_AS5812_54t_FAN -+ tristate "Accton as5812 54t fan" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54t fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54t_fan. -+ -+config SENSORS_ACCTON_AS5812_54t_PSU -+ tristate "Accton as5812 54t psu" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54t psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54t_psu. - - if ACPI - -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 7700250..b8cf2ef 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_FAN) += accton_as5812_54x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_PSU) += accton_as5812_54x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_FAN) += accton_as6812_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_PSU) += accton_as6812_32x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_FAN) += accton_as5812_54t_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_PSU) += accton_as5812_54t_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as5812_54t_fan.c b/drivers/hwmon/accton_as5812_54t_fan.c -new file mode 100644 -index 0000000..bad9245 ---- /dev/null -+++ b/drivers/hwmon/accton_as5812_54t_fan.c -@@ -0,0 +1,442 @@ -+/* -+ * A hwmon driver for the Accton as5812 54t fan -+ * -+ * 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 0x1F -+#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 LOCAL_DEBUG 0 -+ -+static struct accton_as5812_54t_fan *fan_data = NULL; -+ -+struct accton_as5812_54t_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_as5812_54t_fan_update_device(struct device *dev); -+static int accton_as5812_54t_fan_read_value(u8 reg); -+static int accton_as5812_54t_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 accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_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_as5812_54t_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_as5812_54t_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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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*/ -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); -+ break; -+ default: -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54t_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_as5812_54t_fan_group = { -+ .attrs = accton_as5812_54t_fan_attributes, -+}; -+ -+static int accton_as5812_54t_fan_read_value(u8 reg) -+{ -+ return accton_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5812_54t_fan_write_value(u8 reg, u8 value) -+{ -+ return accton_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5812_54t_fan_update_device(struct device *dev) -+{ -+ int speed, r_speed, fault, r_fault, ctrl_speed, direction; -+ int i; -+ -+ mutex_lock(&fan_data->update_lock); -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5812_54t_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; -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5812_54t_fan update 2 \n"); -+ -+ fault = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); -+ r_fault = accton_as5812_54t_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); -+ direction = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); -+ ctrl_speed = accton_as5812_54t_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); -+ -+ if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); -+ -+ for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54t_fan_read_value(fan_speed_reg[i]); -+ r_speed = accton_as5812_54t_fan_read_value(fanr_speed_reg[i]); -+ if ( (speed < 0) || (r_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54t_fan_probe(struct platform_device *pdev) -+{ -+ int status = -1; -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &accton_as5812_54t_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_as5812_54t_fan\n"); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &accton_as5812_54t_fan_group); -+exit: -+ return status; -+} -+ -+static int accton_as5812_54t_fan_remove(struct platform_device *pdev) -+{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5812_54t_fan_group); -+ -+ return 0; -+} -+ -+#define DRVNAME "as5812_54t_fan" -+ -+static struct platform_driver accton_as5812_54t_fan_driver = { -+ .probe = accton_as5812_54t_fan_probe, -+ .remove = accton_as5812_54t_fan_remove, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5812_54t_fan_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5812_54t(void); -+ if (!platform_accton_as5812_54t()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5812_54t_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ fan_data = kzalloc(sizeof(struct accton_as5812_54t_fan), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5812_54t_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_as5812_54t_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5812_54t_fan_exit(void) -+{ -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&accton_as5812_54t_fan_driver); -+ kfree(fan_data); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5812_54t_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_as5812_54t_fan_init); -+module_exit(accton_as5812_54t_fan_exit); -+ -diff --git a/drivers/hwmon/accton_as5812_54t_psu.c b/drivers/hwmon/accton_as5812_54t_psu.c -new file mode 100644 -index 0000000..bf1b79e ---- /dev/null -+++ b/drivers/hwmon/accton_as5812_54t_psu.c -@@ -0,0 +1,295 @@ -+/* -+ * An hwmon driver for accton as5812_54t 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 -+ -+static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); -+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 as5812_54t_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x38, 0x3b, 0x50, 0x53, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as5812_54t_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 as5812_54t_psu_data *as5812_54t_psu_update_device(struct device *dev); -+ -+enum as5812_54t_psu_sysfs_attributes { -+ PSU_INDEX, -+ PSU_PRESENT, -+ PSU_MODEL_NAME, -+ PSU_POWER_GOOD -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); -+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 *as5812_54t_psu_attributes[] = { -+ &sensor_dev_attr_psu_index.dev_attr.attr, -+ &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_index(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54t_psu_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", data->index); -+} -+ -+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 as5812_54t_psu_data *data = as5812_54t_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 as5812_54t_psu_data *data = as5812_54t_psu_update_device(dev); -+ -+ return sprintf(buf, "%s", data->model_name); -+} -+ -+static const struct attribute_group as5812_54t_psu_group = { -+ .attrs = as5812_54t_psu_attributes, -+}; -+ -+static int as5812_54t_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5812_54t_psu_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5812_54t_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, &as5812_54t_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 == 0x38 || client->addr == 0x50) { -+ data->index = 1; -+ } -+ else if (client->addr == 0x3b || client->addr == 0x53) { -+ 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, &as5812_54t_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5812_54t_psu_remove(struct i2c_client *client) -+{ -+ struct as5812_54t_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5812_54t_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as5812_54t_psu_id[] = { -+ { "as5812_54t_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as5812_54t_psu_id); -+ -+static struct i2c_driver as5812_54t_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5812_54t_psu", -+ }, -+ .probe = as5812_54t_psu_probe, -+ .remove = as5812_54t_psu_remove, -+ .id_table = as5812_54t_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5812_54t_psu_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as5812_54t_psu_data *as5812_54t_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54t_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 = -1; -+ -+ dev_dbg(&client->dev, "Starting as5812_54t update\n"); -+ -+ /* Read model name */ -+ if (client->addr == 0x38 || client->addr == 0x3b) { -+ /* AC power */ -+ status = as5812_54t_psu_read_block(client, 0x26, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ else { -+ /* DC power */ -+ status = as5812_54t_psu_read_block(client, 0x50, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ -+ /* Read psu status */ -+ status = accton_i2c_cpld_read(0x60, 0x2); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); -+ } -+ else { -+ data->status = status; -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as5812_54t_psu_init(void) -+{ -+ extern int platform_accton_as5812_54t(void); -+ if (!platform_accton_as5812_54t()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as5812_54t_psu_driver); -+} -+ -+static void __exit as5812_54t_psu_exit(void) -+{ -+ i2c_del_driver(&as5812_54t_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5812_54t_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as5812_54t_psu_init); -+module_exit(as5812_54t_psu_exit); -+ -diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c -index 3aeb08d..acf88c9 100644 ---- a/drivers/hwmon/accton_i2c_cpld.c -+++ b/drivers/hwmon/accton_i2c_cpld.c -@@ -40,6 +40,22 @@ struct cpld_client_node { - */ - static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END }; - -+static ssize_t show_cpld_version(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ int val = 0; -+ struct i2c_client *client = to_i2c_client(dev); -+ -+ val = i2c_smbus_read_byte_data(client, 0x1); -+ -+ if (val < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val); -+ } -+ -+ return sprintf(buf, "%d", val); -+} -+ -+static struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL); -+ - static void accton_i2c_cpld_add_client(struct i2c_client *client) - { - struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); -@@ -93,6 +109,11 @@ static int accton_i2c_cpld_probe(struct i2c_client *client, - goto exit; - } - -+ status = sysfs_create_file(&client->dev.kobj, &ver.attr); -+ if (status) { -+ goto exit; -+ } -+ - dev_info(&client->dev, "chip found\n"); - accton_i2c_cpld_add_client(client); - -@@ -104,6 +125,7 @@ exit: - - static int accton_i2c_cpld_remove(struct i2c_client *client) - { -+ sysfs_remove_file(&client->dev.kobj, &ver.attr); - accton_i2c_cpld_remove_client(client); - - return 0; -@@ -217,6 +239,29 @@ int platform_accton_as7712_32x(void) - } - EXPORT_SYMBOL(platform_accton_as7712_32x); - -+static struct dmi_system_id as5812_54t_dmi_table[] = { -+ { -+ .ident = "Accton AS5812 54t", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54T"), -+ }, -+ }, -+ { -+ .ident = "Accton AS5812 54t", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54T"), -+ }, -+ }, -+}; -+ -+int platform_accton_as5812_54t(void) -+{ -+ return dmi_check_system(as5812_54t_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as5812_54t); -+ - MODULE_AUTHOR("Brandon Chuang "); - MODULE_DESCRIPTION("accton_i2c_cpld driver"); - MODULE_LICENSE("GPL"); -diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig -index cb0c17f..599b97b 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -81,6 +81,13 @@ config LEDS_ACCTON_AS6812_32x - help - This option enables support for the LEDs on the Accton as6812 32x. - Say Y to enable LEDs on the Accton as6812 32x. -+ -+config LEDS_ACCTON_AS5812_54t -+ tristate "LED support for the Accton as5812 54t" -+ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD -+ help -+ This option enables support for the LEDs on the Accton as5812 54t. -+ Say Y to enable LEDs on the Accton as5812 54t. - - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index 8db7a43..bd20baa 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -49,6 +49,7 @@ obj-$(CONFIG_LEDS_ACCTON_AS7512_32x) += leds-accton_as7512_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7712_32x) += leds-accton_as7712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS5812_54x) += leds-accton_as5812_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6812_32x) += leds-accton_as6812_32x.o -+obj-$(CONFIG_LEDS_ACCTON_AS5812_54t) += leds-accton_as5812_54t.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as5812_54t.c b/drivers/leds/leds-accton_as5812_54t.c -new file mode 100644 -index 0000000..011f62e ---- /dev/null -+++ b/drivers/leds/leds-accton_as5812_54t.c -@@ -0,0 +1,601 @@ -+/* -+ * A LED driver for the accton_as5812_54t_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 accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_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 "as5812_54t_led" -+ -+struct accton_as5812_54t_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_as5812_54t_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_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 0x0C -+#define LED_TYPE_FAN3_REG_MASK 0x30 -+#define LED_TYPE_FAN4_REG_MASK 0xC0 -+#define LED_TYPE_FAN5_REG_MASK 0x03 -+#define LED_TYPE_FAN6_REG_MASK 0x0C -+ -+#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 */ -+ 0x16, /* FAN1-4 LED */ -+ 0x17, /* FAN4-6 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_GREEN_BLINK, -+ LED_MODE_AMBER, -+ LED_MODE_AMBER_BLINK, -+ LED_MODE_RED, -+ LED_MODE_RED_BLINK, -+ LED_MODE_BLUE, -+ LED_MODE_BLUE_BLINK, -+ LED_MODE_AUTO, -+ LED_MODE_UNKNOWN -+}; -+ -+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 << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, -+{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 << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, -+{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_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, 2}, -+ {'2', LED_TYPE_FAN2, 2}, -+ {'3', LED_TYPE_FAN3, 2}, -+ {'4', LED_TYPE_FAN4, 2}, -+ {'5', LED_TYPE_FAN5, 3} -+}; -+ -+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 ((led_type_mode_data[i].type_mask & reg_val) == -+ led_type_mode_data[i].mode_mask) -+ { -+ return led_type_mode_data[i].mode; -+ } -+ } -+ -+ return LED_MODE_UNKNOWN; -+} -+ -+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; -+ -+ reg_val = led_type_mode_data[i].mode_mask | -+ (reg_val & (~led_type_mode_data[i].type_mask)); -+ } -+ -+ return reg_val; -+} -+ -+static int accton_as5812_54t_led_read_value(u8 reg) -+{ -+ return accton_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5812_54t_led_write_value(u8 reg, u8 value) -+{ -+ return accton_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5812_54t_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_as5812_54t_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as5812_54t_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_as5812_54t_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_as5812_54t_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_as5812_54t_led_write_value(reg, reg_val); -+ -+ /* to prevent the slow-update issue */ -+ ledctl->valid = 0; -+ -+exit: -+ mutex_unlock(&ledctl->update_lock); -+} -+ -+static void accton_as5812_54t_led_psu_1_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); -+} -+ -+static enum led_brightness accton_as5812_54t_led_psu_1_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54t_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); -+} -+ -+static void accton_as5812_54t_led_psu_2_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); -+} -+ -+static enum led_brightness accton_as5812_54t_led_psu_2_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54t_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); -+} -+ -+static void accton_as5812_54t_led_fan_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); -+} -+ -+static enum led_brightness accton_as5812_54t_led_fan_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54t_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); -+} -+ -+ -+static void accton_as5812_54t_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_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); -+ return; -+ } -+ } -+} -+ -+ -+static enum led_brightness accton_as5812_54t_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_as5812_54t_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_as5812_54t_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as5812_54t_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54t_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as5812_54t_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54t_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as5812_54t_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54t_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static struct led_classdev accton_as5812_54t_leds[] = { -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as5812_54t_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_psu_1_set, -+ .brightness_get = accton_as5812_54t_led_psu_1_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as5812_54t_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_psu_2_set, -+ .brightness_get = accton_as5812_54t_led_psu_2_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as5812_54t_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fan_set, -+ .brightness_get = accton_as5812_54t_led_fan_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN1] = { -+ .name = "accton_as5812_54t_led::fan1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fanx_set, -+ .brightness_get = accton_as5812_54t_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN2] = { -+ .name = "accton_as5812_54t_led::fan2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fanx_set, -+ .brightness_get = accton_as5812_54t_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN3] = { -+ .name = "accton_as5812_54t_led::fan3", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fanx_set, -+ .brightness_get = accton_as5812_54t_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN4] = { -+ .name = "accton_as5812_54t_led::fan4", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fanx_set, -+ .brightness_get = accton_as5812_54t_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN5] = { -+ .name = "accton_as5812_54t_led::fan5", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_fanx_set, -+ .brightness_get = accton_as5812_54t_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as5812_54t_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_diag_set, -+ .brightness_get = accton_as5812_54t_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as5812_54t_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54t_led_loc_set, -+ .brightness_get = accton_as5812_54t_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as5812_54t_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { -+ led_classdev_suspend(&accton_as5812_54t_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5812_54t_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { -+ led_classdev_resume(&accton_as5812_54t_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5812_54t_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as5812_54t_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as5812_54t_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as5812_54t_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as5812_54t_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54t_leds); i++) { -+ led_classdev_unregister(&accton_as5812_54t_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as5812_54t_led_driver = { -+ .probe = accton_as5812_54t_led_probe, -+ .remove = accton_as5812_54t_led_remove, -+ .suspend = accton_as5812_54t_led_suspend, -+ .resume = accton_as5812_54t_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5812_54t_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5812_54t(void); -+ if (!platform_accton_as5812_54t()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5812_54t_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as5812_54t_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5812_54t_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_as5812_54t_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5812_54t_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as5812_54t_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as5812_54t_led_init); -+module_exit(accton_as5812_54t_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5812_54t_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index ff68df7..c75227b 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -126,6 +126,15 @@ config EEPROM_ACCTON_AS6812_32x_SFP - - This driver can also be built as a module. If so, the module will - be called accton_as6812_32x_sfp. -+ -+config EEPROM_ACCTON_AS5812_54t_SFP -+ tristate "Accton as5812 54t sfp" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54t sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54t_sfp. - - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 4b682a1..152a8bc 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -12,4 +12,5 @@ obj-$(CONFIG_EEPROM_ACCTON_AS7512_32x_SFP) += accton_as7512_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7712_32x_SFP) += accton_as7712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5812_54x_SFP) += accton_as5812_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6812_32x_SFP) += accton_as6812_32x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS5812_54t_SFP) += accton_as5812_54t_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as5812_54t_sfp.c b/drivers/misc/eeprom/accton_as5812_54t_sfp.c -new file mode 100644 -index 0000000..0985c80 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as5812_54t_sfp.c -@@ -0,0 +1,318 @@ -+/* -+ * An hwmon driver for accton as5812_54t 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 QSFP_PORT_START_INDEX 49 -+#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 as5812_54t_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 */ -+ u8 status; /* bit0:port49, bit1:port50 and so on */ -+}; -+ -+static struct as5812_54t_sfp_data *as5812_54t_sfp_update_device(struct device *dev, int update_eeprom); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as5812_54t_sfp_sysfs_attributes { -+ SFP_IS_PRESENT, -+ SFP_PORT_NUMBER, -+ SFP_EEPROM, -+ SFP_IS_PRESENT_ALL, -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_status, NULL, SFP_IS_PRESENT); -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); -+static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_status,NULL, SFP_IS_PRESENT_ALL); -+ -+static struct attribute *as5812_54t_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 as5812_54t_sfp_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n",data->port); -+} -+ -+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 as5812_54t_sfp_data *data = as5812_54t_sfp_update_device(dev, 0); -+ -+ if (attr->index == SFP_IS_PRESENT) { -+ u8 val; -+ -+ val = (data->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) ? 0 : 1; -+ return sprintf(buf, "%d", val); -+ } -+ else { /* SFP_IS_PRESENT_ALL */ -+ return sprintf(buf, "%.2x\n", ~data->status); -+ } -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as5812_54t_sfp_data *data = as5812_54t_sfp_update_device(dev, 1); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if ((data->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) != 0) { -+ return 0; -+ } -+ -+ memcpy(buf, data->eeprom, sizeof(data->eeprom)); -+ -+ return sizeof(data->eeprom); -+} -+ -+static const struct attribute_group as5812_54t_sfp_group = { -+ .attrs = as5812_54t_sfp_attributes, -+}; -+ -+static int as5812_54t_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5812_54t_sfp_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5812_54t_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, &as5812_54t_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, &as5812_54t_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5812_54t_sfp_remove(struct i2c_client *client) -+{ -+ struct as5812_54t_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5812_54t_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as5812_54t_qsfp49 = 49, -+as5812_54t_qsfp50, -+as5812_54t_qsfp51, -+as5812_54t_qsfp52, -+as5812_54t_qsfp53, -+as5812_54t_qsfp54 -+}; -+ -+static const struct i2c_device_id as5812_54t_sfp_id[] = { -+{ "as5812_54t_qsfp49", as5812_54t_qsfp49 }, { "as5812_54t_qsfp50", as5812_54t_qsfp50 }, -+{ "as5812_54t_qsfp51", as5812_54t_qsfp51 }, { "as5812_54t_qsfp52", as5812_54t_qsfp52 }, -+{ "as5812_54t_qsfp53", as5812_54t_qsfp53 }, { "as5812_54t_qsfp54", as5812_54t_qsfp54 }, -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as5812_54t_sfp_id); -+ -+static struct i2c_driver as5812_54t_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5812_54t_sfp", -+ }, -+ .probe = as5812_54t_sfp_probe, -+ .remove = as5812_54t_sfp_remove, -+ .id_table = as5812_54t_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5812_54t_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; -+} -+ -+static struct as5812_54t_sfp_data *as5812_54t_sfp_update_device(struct device *dev, int update_eeprom) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54t_sfp_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid || update_eeprom) { -+ int status = -1; -+ int i = 0; -+ -+ data->valid = 0; -+ //dev_dbg(&client->dev, "Starting as5812_54t sfp status update\n"); -+ data->status = 0xFF; -+ -+ /* -+ * Bring QSFPs out of reset, -+ * This is a temporary fix until the QSFP+_MOD_RST register -+ * can be exposed through the driver. -+ */ -+ accton_i2c_cpld_write(0x60, 0x23, 0x3F); -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ status = accton_i2c_cpld_read(0x60, 0x22); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x60) reg(0x22) err %d\n", status); -+ } -+ else { -+ data->status = status & 0x3F; /* (u32)status */ -+ } -+ -+ 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->status & BIT_INDEX(data->port - QSFP_PORT_START_INDEX)) == 0) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom); i++) { -+ status = as5812_54t_sfp_read_byte(client, i, data->eeprom + i); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", -+ data->port); -+ goto exit; -+ } -+ } -+ } -+ } -+ -+ data->valid = 1; -+ data->last_updated = jiffies; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as5812_54t_sfp_init(void) -+{ -+ extern int platform_accton_as5812_54t(void); -+ if (!platform_accton_as5812_54t()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as5812_54t_sfp_driver); -+} -+ -+static void __exit as5812_54t_sfp_exit(void) -+{ -+ i2c_del_driver(&as5812_54t_sfp_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5812_54t_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as5812_54t_sfp_init); -+module_exit(as5812_54t_sfp_exit); -+ diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54x-device-drivers.patch deleted file mode 100644 index 0b25bf3c..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as5812_54x-device-drivers.patch +++ /dev/null @@ -1,2401 +0,0 @@ -Device driver patches for accton as5812-54x (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 2d4a7fb..4d9fb22 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1520,6 +1520,24 @@ config SENSORS_ACCTON_AS7712_32x_PSU - This driver can also be built as a module. If so, the module will - be called accton_as7712_32x_psu. - -+config SENSORS_ACCTON_AS5812_54x_FAN -+ tristate "Accton as5812 54x fan" -+ depends on I2C && I2C_MUX_ACCTON_AS5812_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54x_fan. -+ -+config SENSORS_ACCTON_AS5812_54x_PSU -+ tristate "Accton as5812 54x psu" -+ depends on I2C && I2C_MUX_ACCTON_AS5812_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54x_psu. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index ea97f4a..818dd01 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -30,6 +30,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS7512_32x_PSU) += accton_as7512_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS7712_32x_FAN) += accton_as7712_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS7712_32x_PSU) += accton_as7712_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_I2C_CPLD) += accton_i2c_cpld.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_FAN) += accton_as5812_54x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_PSU) += accton_as5812_54x_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as5812_54x_fan.c b/drivers/hwmon/accton_as5812_54x_fan.c -new file mode 100644 -index 0000000..3e25db1 ---- /dev/null -+++ b/drivers/hwmon/accton_as5812_54x_fan.c -@@ -0,0 +1,442 @@ -+/* -+ * A hwmon driver for the Accton as5812 54x fan -+ * -+ * 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 0x1F -+#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 LOCAL_DEBUG 0 -+ -+static struct accton_as5812_54x_fan *fan_data = NULL; -+ -+struct accton_as5812_54x_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_as5812_54x_fan_update_device(struct device *dev); -+static int accton_as5812_54x_fan_read_value(u8 reg); -+static int accton_as5812_54x_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 as5812_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as5812_54x_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_as5812_54x_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_as5812_54x_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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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*/ -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[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]); -+ if (LOCAL_DEBUG) -+ printk ("[Check !!][%s][%d][type->index=%d][data->index=%d]\n", __FUNCTION__, __LINE__, type_index, data_index); -+ break; -+ default: -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54x_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_as5812_54x_fan_group = { -+ .attrs = accton_as5812_54x_fan_attributes, -+}; -+ -+static int accton_as5812_54x_fan_read_value(u8 reg) -+{ -+ return as5812_54x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5812_54x_fan_write_value(u8 reg, u8 value) -+{ -+ return as5812_54x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5812_54x_fan_update_device(struct device *dev) -+{ -+ int speed, r_speed, fault, r_fault, ctrl_speed, direction; -+ int i; -+ -+ mutex_lock(&fan_data->update_lock); -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5812_54x_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; -+ -+ if (LOCAL_DEBUG) -+ printk ("Starting accton_as5812_54x_fan update 2 \n"); -+ -+ fault = accton_as5812_54x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); -+ r_fault = accton_as5812_54x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); -+ direction = accton_as5812_54x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); -+ ctrl_speed = accton_as5812_54x_fan_read_value(CPLD_REG_FAN_PWM_CYCLE_OFFSET); -+ -+ if ( (fault < 0) || (r_fault < 0) || (direction < 0) || (ctrl_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[fan:] fault:%d, r_fault=%d, direction=%d, ctrl_speed=%d \n",fault, r_fault, direction, ctrl_speed); -+ -+ for (i=0; istatus[i] = (fault & fan_info_mask[i]) >> i; -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54x_fan_read_value(fan_speed_reg[i]); -+ r_speed = accton_as5812_54x_fan_read_value(fanr_speed_reg[i]); -+ if ( (speed < 0) || (r_speed < 0) ) -+ { -+ if (LOCAL_DEBUG) -+ printk ("[Error!!][%s][%d] \n", __FUNCTION__, __LINE__); -+ goto _exit; /* error */ -+ } -+ -+ if (LOCAL_DEBUG) -+ printk ("[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_as5812_54x_fan_probe(struct platform_device *pdev) -+{ -+ int status = -1; -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &accton_as5812_54x_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_as5812_54x_fan\n"); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &accton_as5812_54x_fan_group); -+exit: -+ return status; -+} -+ -+static int accton_as5812_54x_fan_remove(struct platform_device *pdev) -+{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as5812_54x_fan_group); -+ -+ return 0; -+} -+ -+#define DRVNAME "as5812_54x_fan" -+ -+static struct platform_driver accton_as5812_54x_fan_driver = { -+ .probe = accton_as5812_54x_fan_probe, -+ .remove = accton_as5812_54x_fan_remove, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5812_54x_fan_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5812_54x(void); -+ if(!platform_accton_as5812_54x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as5812_54x_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ fan_data = kzalloc(sizeof(struct accton_as5812_54x_fan), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5812_54x_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_as5812_54x_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5812_54x_fan_exit(void) -+{ -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&accton_as5812_54x_fan_driver); -+ kfree(fan_data); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5812_54x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_as5812_54x_fan_init); -+module_exit(accton_as5812_54x_fan_exit); -+ -diff --git a/drivers/hwmon/accton_as5812_54x_psu.c b/drivers/hwmon/accton_as5812_54x_psu.c -new file mode 100644 -index 0000000..0d29980 ---- /dev/null -+++ b/drivers/hwmon/accton_as5812_54x_psu.c -@@ -0,0 +1,294 @@ -+/* -+ * An hwmon driver for accton as5812_54x 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 -+ -+static ssize_t show_index(struct device *dev, struct device_attribute *da, char *buf); -+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 as5812_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int as5812_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x38, 0x3b, 0x50, 0x53, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as5812_54x_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 as5812_54x_psu_data *as5812_54x_psu_update_device(struct device *dev); -+ -+enum as5812_54x_psu_sysfs_attributes { -+ PSU_INDEX, -+ PSU_PRESENT, -+ PSU_MODEL_NAME, -+ PSU_POWER_GOOD -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_index, S_IRUGO, show_index, NULL, PSU_INDEX); -+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 *as5812_54x_psu_attributes[] = { -+ &sensor_dev_attr_psu_index.dev_attr.attr, -+ &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_index(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", data->index); -+} -+ -+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 as5812_54x_psu_data *data = as5812_54x_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 as5812_54x_psu_data *data = as5812_54x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s", data->model_name); -+} -+ -+static const struct attribute_group as5812_54x_psu_group = { -+ .attrs = as5812_54x_psu_attributes, -+}; -+ -+static int as5812_54x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5812_54x_psu_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5812_54x_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, &as5812_54x_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 == 0x38 || client->addr == 0x50) { -+ data->index = 1; -+ } -+ else if (client->addr == 0x3b || client->addr == 0x53) { -+ 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, &as5812_54x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5812_54x_psu_remove(struct i2c_client *client) -+{ -+ struct as5812_54x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5812_54x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as5812_54x_psu_id[] = { -+ { "as5812_54x_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as5812_54x_psu_id); -+ -+static struct i2c_driver as5812_54x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5812_54x_psu", -+ }, -+ .probe = as5812_54x_psu_probe, -+ .remove = as5812_54x_psu_remove, -+ .id_table = as5812_54x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5812_54x_psu_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as5812_54x_psu_data *as5812_54x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54x_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 = -1; -+ -+ dev_dbg(&client->dev, "Starting as5812_54x update\n"); -+ -+ /* Read model name */ -+ if (client->addr == 0x38 || client->addr == 0x3b) { -+ /* AC power */ -+ status = as5812_54x_psu_read_block(client, 0x26, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ else { -+ /* DC power */ -+ status = as5812_54x_psu_read_block(client, 0x50, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ } -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ -+ /* Read psu status */ -+ status = as5812_54x_i2c_cpld_read(0x60, 0x2); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); -+ } -+ else { -+ data->status = status; -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as5812_54x_psu_init(void) -+{ -+ extern int platform_accton_as5812_54x(void); -+ if(!platform_accton_as5812_54x()) { -+ return -ENODEV; -+ } -+ return i2c_add_driver(&as5812_54x_psu_driver); -+} -+ -+static void __exit as5812_54x_psu_exit(void) -+{ -+ i2c_del_driver(&as5812_54x_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5812_54x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as5812_54x_psu_init); -+module_exit(as5812_54x_psu_exit); -+ -diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig -index 4429dd9..8ac67ef 100644 ---- a/drivers/i2c/muxes/Kconfig -+++ b/drivers/i2c/muxes/Kconfig -@@ -24,6 +24,15 @@ config I2C_MUX_ACCTON_AS6712_32x_CPLD - This driver can also be built as a module. If so, the module - will be called i2c-mux-accton_as6712_32x_cpld. - -+config I2C_MUX_ACCTON_AS5812_54x_CPLD -+ tristate "Accton as5812_54x CPLD I2C multiplexer" -+ help -+ If you say yes here you get support for the Accton CPLD -+ I2C mux devices. -+ -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mux-accton_as5812_54x_cpld. -+ - config I2C_MUX_GPIO - tristate "GPIO-based I2C multiplexer" - depends on GENERIC_GPIO -diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile -index cab3174..7769d29 100644 ---- a/drivers/i2c/muxes/Makefile -+++ b/drivers/i2c/muxes/Makefile -@@ -9,5 +9,6 @@ obj-$(CONFIG_I2C_MUX_QUANTA) += quanta-i2cmux.o - obj-$(CONFIG_I2C_MUX_QUANTA_LY2) += quanta-ly2-i2c-mux.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS5712_54x_CPLD) += i2c-mux-accton_as5712_54x_cpld.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS6712_32x_CPLD) += i2c-mux-accton_as6712_32x_cpld.o -+obj-$(CONFIG_I2C_MUX_ACCTON_AS5812_54x_CPLD) += i2c-mux-accton_as5812_54x_cpld.o - - ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG -diff --git a/drivers/i2c/muxes/i2c-mux-accton_as5812_54x_cpld.c b/drivers/i2c/muxes/i2c-mux-accton_as5812_54x_cpld.c -new file mode 100644 -index 0000000..e01e557 ---- /dev/null -+++ b/drivers/i2c/muxes/i2c-mux-accton_as5812_54x_cpld.c -@@ -0,0 +1,394 @@ -+/* -+ * An I2C multiplexer dirver for accton as5812 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 as5812_54x 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 as5812_54x_dmi_table[] = { -+ { -+ .ident = "Accton AS5812-54X", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54X"), -+ }, -+ }, -+ { -+ .ident = "Accton AS5812-54X", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS5812-54X"), -+ }, -+ }, -+}; -+ -+int platform_accton_as5812_54x(void) -+{ -+ return dmi_check_system(as5812_54x_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as5812_54x); -+ -+#define NUM_OF_CPLD1_CHANS 0x0 -+#define NUM_OF_CPLD2_CHANS 0x18 -+#define NUM_OF_CPLD3_CHANS 0x1E -+#define CPLD_CHANNEL_SELECT_REG 0x2 -+#define CPLD_DESELECT_CHANNEL 0xFF -+ -+#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 { -+ as5812_54x_cpld2, -+ as5812_54x_cpld3, -+ as5812_54x_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[] = { -+ [as5812_54x_cpld1] = { -+ .nchans = NUM_OF_CPLD1_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ }, -+ [as5812_54x_cpld2] = { -+ .nchans = NUM_OF_CPLD2_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ }, -+ [as5812_54x_cpld3] = { -+ .nchans = NUM_OF_CPLD3_CHANS, -+ .deselectChan = CPLD_DESELECT_CHANNEL, -+ } -+}; -+ -+static const struct i2c_device_id accton_i2c_cpld_mux_id[] = { -+ { "as5812_54x_cpld1", as5812_54x_cpld1 }, -+ { "as5812_54x_cpld2", as5812_54x_cpld2 }, -+ { "as5812_54x_cpld3", as5812_54x_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, CPLD_CHANNEL_SELECT_REG, -+ 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 == as5812_54x_cpld2 || data->type == as5812_54x_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, -+ 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 as5812_54x_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(as5812_54x_i2c_cpld_read); -+ -+int as5812_54x_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(as5812_54x_i2c_cpld_write); -+ -+static struct i2c_driver accton_i2c_cpld_mux_driver = { -+ .driver = { -+ .name = "as5812_54x_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/drivers/leds/Kconfig b/drivers/leds/Kconfig -index 48106f2..514f978 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -68,6 +68,13 @@ config LEDS_ACCTON_AS7712_32x - This option enables support for the LEDs on the Accton as7712 32x. - Say Y to enable LEDs on the Accton as7712 32x. - -+config LEDS_ACCTON_AS5812_54x -+ tristate "LED support for the Accton as5812 54x" -+ depends on LEDS_CLASS && I2C_MUX_ACCTON_AS5812_54x_CPLD -+ help -+ This option enables support for the LEDs on the Accton as5812 54x. -+ Say Y to enable LEDs on the Accton as5812 54x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index c4ea931..379c448 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -47,6 +47,8 @@ obj-$(CONFIG_LEDS_ACCTON_AS5712_54x) += leds-accton_as5712_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6712_32x) += leds-accton_as6712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7512_32x) += leds-accton_as7512_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7712_32x) += leds-accton_as7712_32x.o -+obj-$(CONFIG_LEDS_ACCTON_AS5812_54x) += leds-accton_as5812_54x.o -+ - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o - -diff --git a/drivers/leds/leds-accton_as5812_54x.c b/drivers/leds/leds-accton_as5812_54x.c -new file mode 100644 -index 0000000..b701868 ---- /dev/null -+++ b/drivers/leds/leds-accton_as5812_54x.c -@@ -0,0 +1,597 @@ -+/* -+ * A LED driver for the accton_as5812_54x_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 as5812_54x_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int as5812_54x_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 "as5812_54x_led" -+ -+struct accton_as5812_54x_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_as5812_54x_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_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 0x0C -+#define LED_TYPE_FAN3_REG_MASK 0x30 -+#define LED_TYPE_FAN4_REG_MASK 0xC0 -+#define LED_TYPE_FAN5_REG_MASK 0x03 -+#define LED_TYPE_FAN6_REG_MASK 0x0C -+ -+#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 */ -+ 0x16, /* FAN1-4 LED */ -+ 0x17, /* FAN4-6 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 << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 2}, -+{LED_TYPE_FAN2, LED_TYPE_FAN2_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 2}, -+{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 << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_RED, LED_MODE_FANX_RED_MASK << 6}, -+{LED_TYPE_FAN4, LED_TYPE_FAN4_REG_MASK, LED_MODE_OFF, LED_MODE_FANX_OFF_MASK << 6}, -+{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_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, 2}, -+ {'2', LED_TYPE_FAN2, 2}, -+ {'3', LED_TYPE_FAN3, 2}, -+ {'4', LED_TYPE_FAN4, 2}, -+ {'5', LED_TYPE_FAN5, 3} -+}; -+ -+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 ((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; -+ -+ reg_val = led_type_mode_data[i].mode_mask | -+ (reg_val & (~led_type_mode_data[i].type_mask)); -+ } -+ -+ return reg_val; -+} -+ -+static int accton_as5812_54x_led_read_value(u8 reg) -+{ -+ return as5812_54x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as5812_54x_led_write_value(u8 reg, u8 value) -+{ -+ return as5812_54x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as5812_54x_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_as5812_54x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as5812_54x_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_as5812_54x_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_as5812_54x_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_as5812_54x_led_write_value(reg, reg_val); -+ -+ /* to prevent the slow-update issue */ -+ ledctl->valid = 0; -+ -+exit: -+ mutex_unlock(&ledctl->update_lock); -+} -+ -+static void accton_as5812_54x_led_psu_1_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); -+} -+ -+static enum led_brightness accton_as5812_54x_led_psu_1_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); -+} -+ -+static void accton_as5812_54x_led_psu_2_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); -+} -+ -+static enum led_brightness accton_as5812_54x_led_psu_2_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); -+} -+ -+static void accton_as5812_54x_led_fan_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); -+} -+ -+static enum led_brightness accton_as5812_54x_led_fan_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); -+} -+ -+ -+static void accton_as5812_54x_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_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); -+ return; -+ } -+ } -+} -+ -+ -+static enum led_brightness accton_as5812_54x_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_as5812_54x_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_as5812_54x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as5812_54x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as5812_54x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as5812_54x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as5812_54x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as5812_54x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static struct led_classdev accton_as5812_54x_leds[] = { -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as5812_54x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_psu_1_set, -+ .brightness_get = accton_as5812_54x_led_psu_1_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as5812_54x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_psu_2_set, -+ .brightness_get = accton_as5812_54x_led_psu_2_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as5812_54x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fan_set, -+ .brightness_get = accton_as5812_54x_led_fan_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN1] = { -+ .name = "accton_as5812_54x_led::fan1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fanx_set, -+ .brightness_get = accton_as5812_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN2] = { -+ .name = "accton_as5812_54x_led::fan2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fanx_set, -+ .brightness_get = accton_as5812_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN3] = { -+ .name = "accton_as5812_54x_led::fan3", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fanx_set, -+ .brightness_get = accton_as5812_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN4] = { -+ .name = "accton_as5812_54x_led::fan4", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fanx_set, -+ .brightness_get = accton_as5812_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN5] = { -+ .name = "accton_as5812_54x_led::fan5", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_fanx_set, -+ .brightness_get = accton_as5812_54x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as5812_54x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_diag_set, -+ .brightness_get = accton_as5812_54x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as5812_54x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as5812_54x_led_loc_set, -+ .brightness_get = accton_as5812_54x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as5812_54x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54x_leds); i++) { -+ led_classdev_suspend(&accton_as5812_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5812_54x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54x_leds); i++) { -+ led_classdev_resume(&accton_as5812_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as5812_54x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as5812_54x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as5812_54x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as5812_54x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as5812_54x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as5812_54x_leds); i++) { -+ led_classdev_unregister(&accton_as5812_54x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as5812_54x_led_driver = { -+ .probe = accton_as5812_54x_led_probe, -+ .remove = accton_as5812_54x_led_remove, -+ .suspend = accton_as5812_54x_led_suspend, -+ .resume = accton_as5812_54x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as5812_54x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as5812_54x(void); -+ if(!platform_accton_as5812_54x()) { -+ return -ENODEV; -+ } -+ ret = platform_driver_register(&accton_as5812_54x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as5812_54x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as5812_54x_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_as5812_54x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as5812_54x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as5812_54x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as5812_54x_led_init); -+module_exit(accton_as5812_54x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as5812_54x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index bd435a0..7c8d3b8 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -109,6 +109,15 @@ config EEPROM_ACCTON_AS7712_32x_SFP - This driver can also be built as a module. If so, the module will - be called accton_as7712_32x_sfp. - -+config EEPROM_ACCTON_AS5812_54x_SFP -+ tristate "Accton as5812 54x sfp" -+ depends on I2C && I2C_MUX_ACCTON_AS5812_54x_CPLD -+ help -+ If you say yes here you get support for Accton as5812 54x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as5812_54x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 4ad6540..e11d273 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -10,4 +10,5 @@ obj-$(CONFIG_EEPROM_ACCTON_AS5712_54x_SFP) += accton_as5712_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6712_32x_SFP) += accton_as6712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7512_32x_SFP) += accton_as7512_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7712_32x_SFP) += accton_as7712_32x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS5812_54x_SFP) += accton_as5812_54x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as5812_54x_sfp.c b/drivers/misc/eeprom/accton_as5812_54x_sfp.c -new file mode 100644 -index 0000000..44727e2 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as5812_54x_sfp.c -@@ -0,0 +1,508 @@ -+/* -+ * An hwmon driver for accton as5812_54x 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 NUM_OF_SFP_PORT 54 -+#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 as5812_54x_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 status[4]; /* bit0:port0, bit1:port1 and so on */ -+ /* index 0 => is_present -+ 1 => tx_fail -+ 2 => tx_disable -+ 3 => rx_loss */ -+}; -+ -+/* The table maps active port to cpld port. -+ * Array index 0 is for active port 1, -+ * index 1 for active port 2, and so on. -+ * The array content implies cpld port index. -+ */ -+static const u8 cpld_to_front_port_table[] = -+{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -+ 49, 52, 50, 53, 51, 54}; -+ -+#define CPLD_PORT_TO_FRONT_PORT(port) (cpld_to_front_port_table[port]) -+ -+static struct as5812_54x_sfp_data *as5812_54x_sfp_update_device(struct device *dev, int update_eeprom); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t show_status(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 set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+extern int as5812_54x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as5812_54x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as5812_54x_sfp_sysfs_attributes { -+ SFP_IS_PRESENT, -+ SFP_TX_FAULT, -+ SFP_TX_DISABLE, -+ SFP_RX_LOSS, -+ SFP_PORT_NUMBER, -+ SFP_EEPROM, -+ SFP_RX_LOS_ALL, -+ SFP_IS_PRESENT_ALL, -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_status, NULL, SFP_IS_PRESENT); -+static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, show_status, NULL, SFP_TX_FAULT); -+static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, show_status, set_tx_disable, SFP_TX_DISABLE); -+static SENSOR_DEVICE_ATTR(sfp_rx_loss, S_IRUGO, show_status,NULL, SFP_RX_LOSS); -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM); -+static SENSOR_DEVICE_ATTR(sfp_rx_los_all, S_IRUGO, show_status,NULL, SFP_RX_LOS_ALL); -+static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_status,NULL, SFP_IS_PRESENT_ALL); -+ -+static struct attribute *as5812_54x_sfp_attributes[] = { -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_loss.dev_attr.attr, -+ &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, -+ &sensor_dev_attr_sfp_eeprom.dev_attr.attr, -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_rx_los_all.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 as5812_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); -+} -+ -+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 as5812_54x_sfp_data *data; -+ u8 val; -+ int values[7]; -+ -+ /* 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) -+ -+ if(attr->index == SFP_RX_LOS_ALL) { -+ /* -+ * Report the RX_LOS status for all ports. -+ * This does not depend on the currently active SFP selector. -+ */ -+ -+ /* RX_LOS Ports 1-8 */ -+ VALIDATED_READ(buf, values[0], as5812_54x_i2c_cpld_read(0x61, 0x0F), 0); -+ /* RX_LOS Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], as5812_54x_i2c_cpld_read(0x61, 0x10), 0); -+ /* RX_LOS Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], as5812_54x_i2c_cpld_read(0x61, 0x11), 0); -+ /* RX_LOS Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], as5812_54x_i2c_cpld_read(0x62, 0x0F), 0); -+ /* RX_LOS Ports 33-40 */ -+ VALIDATED_READ(buf, values[4], as5812_54x_i2c_cpld_read(0x62, 0x10), 0); -+ /* RX_LOS Ports 41-48 */ -+ VALIDATED_READ(buf, values[5], as5812_54x_i2c_cpld_read(0x62, 0x11), 0); -+ -+ /** Return values 1 -> 48 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5]); -+ } -+ -+ if(attr->index == SFP_IS_PRESENT_ALL) { -+ /* -+ * Report the SFP_PRESENCE status for all ports. -+ * This does not depend on the currently active SFP selector. -+ */ -+ -+ /* SFP_PRESENT Ports 1-8 */ -+ VALIDATED_READ(buf, values[0], as5812_54x_i2c_cpld_read(0x61, 0x6), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], as5812_54x_i2c_cpld_read(0x61, 0x7), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], as5812_54x_i2c_cpld_read(0x61, 0x8), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], as5812_54x_i2c_cpld_read(0x62, 0x6), 1); -+ /* SFP_PRESENT Ports 33-40 */ -+ VALIDATED_READ(buf, values[4], as5812_54x_i2c_cpld_read(0x62, 0x7), 1); -+ /* SFP_PRESENT Ports 41-48 */ -+ VALIDATED_READ(buf, values[5], as5812_54x_i2c_cpld_read(0x62, 0x8), 1); -+ /* QSFP_PRESENT Ports 49-54 */ -+ VALIDATED_READ(buf, values[6], as5812_54x_i2c_cpld_read(0x62, 0x14), 1); -+ -+ /* Return values 1 -> 54 in order */ -+ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", -+ values[0], values[1], values[2], -+ values[3], values[4], values[5], -+ values[6] & 0x3F); -+ } -+ /* -+ * The remaining attributes are gathered on a per-selected-sfp basis. -+ */ -+ data = as5812_54x_sfp_update_device(dev, 0); -+ if (attr->index == SFP_IS_PRESENT) { -+ val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 0 : 1; -+ } -+ else { -+ val = (data->status[attr->index] & BIT_INDEX(data->port)) ? 1 : 0; -+ } -+ -+ return sprintf(buf, "%d", val); -+} -+ -+static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54x_sfp_data *data = i2c_get_clientdata(client); -+ unsigned short cpld_addr = 0; -+ u8 cpld_reg = 0, cpld_val = 0, cpld_bit = 0; -+ long disable; -+ int error; -+ -+ /* Tx disable is not supported for QSFP ports(49-54) */ -+ if (data->port >= 48) { -+ return -EINVAL; -+ } -+ -+ error = kstrtol(buf, 10, &disable); -+ if (error) { -+ return error; -+ } -+ -+ mutex_lock(&data->update_lock); -+ -+ if(data->port < 24) { -+ cpld_addr = 0x61; -+ cpld_reg = 0xC + data->port / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ else { -+ cpld_addr = 0x62; -+ cpld_reg = 0xC + (data->port - 24) / 8; -+ cpld_bit = 1 << (data->port % 8); -+ } -+ -+ cpld_val = as5812_54x_i2c_cpld_read(cpld_addr, cpld_reg); -+ -+ /* Update tx_disable status */ -+ if (disable) { -+ data->status[SFP_TX_DISABLE] |= BIT_INDEX(data->port); -+ cpld_val |= cpld_bit; -+ } -+ else { -+ data->status[SFP_TX_DISABLE] &= ~BIT_INDEX(data->port); -+ cpld_val &= ~cpld_bit; -+ } -+ -+ as5812_54x_i2c_cpld_write(cpld_addr, cpld_reg, cpld_val); -+ -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as5812_54x_sfp_data *data = as5812_54x_sfp_update_device(dev, 1); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if ((data->status[SFP_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 as5812_54x_sfp_group = { -+ .attrs = as5812_54x_sfp_attributes, -+}; -+ -+static int as5812_54x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as5812_54x_sfp_data *data; -+ int status; -+ -+ extern int platform_accton_as5812_54x(void); -+ if(!platform_accton_as5812_54x()) { -+ return -ENODEV; -+ } -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as5812_54x_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, &as5812_54x_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, &as5812_54x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as5812_54x_sfp_remove(struct i2c_client *client) -+{ -+ struct as5812_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as5812_54x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as5812_54x_sfp1, as5812_54x_sfp2, as5812_54x_sfp3, as5812_54x_sfp4, -+as5812_54x_sfp5, as5812_54x_sfp6, as5812_54x_sfp7, as5812_54x_sfp8, -+as5812_54x_sfp9, as5812_54x_sfp10, as5812_54x_sfp11,as5812_54x_sfp12, -+as5812_54x_sfp13, as5812_54x_sfp14, as5812_54x_sfp15,as5812_54x_sfp16, -+as5812_54x_sfp17, as5812_54x_sfp18, as5812_54x_sfp19,as5812_54x_sfp20, -+as5812_54x_sfp21, as5812_54x_sfp22, as5812_54x_sfp23,as5812_54x_sfp24, -+as5812_54x_sfp25, as5812_54x_sfp26, as5812_54x_sfp27,as5812_54x_sfp28, -+as5812_54x_sfp29, as5812_54x_sfp30, as5812_54x_sfp31,as5812_54x_sfp32, -+as5812_54x_sfp33, as5812_54x_sfp34, as5812_54x_sfp35,as5812_54x_sfp36, -+as5812_54x_sfp37, as5812_54x_sfp38, as5812_54x_sfp39,as5812_54x_sfp40, -+as5812_54x_sfp41, as5812_54x_sfp42, as5812_54x_sfp43,as5812_54x_sfp44, -+as5812_54x_sfp45, as5812_54x_sfp46, as5812_54x_sfp47,as5812_54x_sfp48, -+as5812_54x_sfp49, as5812_54x_sfp52, as5812_54x_sfp50,as5812_54x_sfp53, -+as5812_54x_sfp51, as5812_54x_sfp54 -+}; -+ -+static const struct i2c_device_id as5812_54x_sfp_id[] = { -+{ "as5812_54x_sfp1", as5812_54x_sfp1 }, { "as5812_54x_sfp2", as5812_54x_sfp2 }, -+{ "as5812_54x_sfp3", as5812_54x_sfp3 }, { "as5812_54x_sfp4", as5812_54x_sfp4 }, -+{ "as5812_54x_sfp5", as5812_54x_sfp5 }, { "as5812_54x_sfp6", as5812_54x_sfp6 }, -+{ "as5812_54x_sfp7", as5812_54x_sfp7 }, { "as5812_54x_sfp8", as5812_54x_sfp8 }, -+{ "as5812_54x_sfp9", as5812_54x_sfp9 }, { "as5812_54x_sfp10", as5812_54x_sfp10 }, -+{ "as5812_54x_sfp11", as5812_54x_sfp11 }, { "as5812_54x_sfp12", as5812_54x_sfp12 }, -+{ "as5812_54x_sfp13", as5812_54x_sfp13 }, { "as5812_54x_sfp14", as5812_54x_sfp14 }, -+{ "as5812_54x_sfp15", as5812_54x_sfp15 }, { "as5812_54x_sfp16", as5812_54x_sfp16 }, -+{ "as5812_54x_sfp17", as5812_54x_sfp17 }, { "as5812_54x_sfp18", as5812_54x_sfp18 }, -+{ "as5812_54x_sfp19", as5812_54x_sfp19 }, { "as5812_54x_sfp20", as5812_54x_sfp20 }, -+{ "as5812_54x_sfp21", as5812_54x_sfp21 }, { "as5812_54x_sfp22", as5812_54x_sfp22 }, -+{ "as5812_54x_sfp23", as5812_54x_sfp23 }, { "as5812_54x_sfp24", as5812_54x_sfp24 }, -+{ "as5812_54x_sfp25", as5812_54x_sfp25 }, { "as5812_54x_sfp26", as5812_54x_sfp26 }, -+{ "as5812_54x_sfp27", as5812_54x_sfp27 }, { "as5812_54x_sfp28", as5812_54x_sfp28 }, -+{ "as5812_54x_sfp29", as5812_54x_sfp29 }, { "as5812_54x_sfp30", as5812_54x_sfp30 }, -+{ "as5812_54x_sfp31", as5812_54x_sfp31 }, { "as5812_54x_sfp32", as5812_54x_sfp32 }, -+{ "as5812_54x_sfp33", as5812_54x_sfp33 }, { "as5812_54x_sfp34", as5812_54x_sfp34 }, -+{ "as5812_54x_sfp35", as5812_54x_sfp35 }, { "as5812_54x_sfp36", as5812_54x_sfp36 }, -+{ "as5812_54x_sfp37", as5812_54x_sfp37 }, { "as5812_54x_sfp38", as5812_54x_sfp38 }, -+{ "as5812_54x_sfp39", as5812_54x_sfp39 }, { "as5812_54x_sfp40", as5812_54x_sfp40 }, -+{ "as5812_54x_sfp41", as5812_54x_sfp41 }, { "as5812_54x_sfp42", as5812_54x_sfp42 }, -+{ "as5812_54x_sfp43", as5812_54x_sfp43 }, { "as5812_54x_sfp44", as5812_54x_sfp44 }, -+{ "as5812_54x_sfp45", as5812_54x_sfp45 }, { "as5812_54x_sfp46", as5812_54x_sfp46 }, -+{ "as5812_54x_sfp47", as5812_54x_sfp47 }, { "as5812_54x_sfp48", as5812_54x_sfp48 }, -+{ "as5812_54x_sfp49", as5812_54x_sfp49 }, { "as5812_54x_sfp50", as5812_54x_sfp50 }, -+{ "as5812_54x_sfp51", as5812_54x_sfp51 }, { "as5812_54x_sfp52", as5812_54x_sfp52 }, -+{ "as5812_54x_sfp53", as5812_54x_sfp53 }, { "as5812_54x_sfp54", as5812_54x_sfp54 }, -+ -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as5812_54x_sfp_id); -+ -+static struct i2c_driver as5812_54x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as5812_54x_sfp", -+ }, -+ .probe = as5812_54x_sfp_probe, -+ .remove = as5812_54x_sfp_remove, -+ .id_table = as5812_54x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as5812_54x_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; -+} -+ -+#define ALWAYS_UPDATE_DEVICE 1 -+ -+static struct as5812_54x_sfp_data *as5812_54x_sfp_update_device(struct device *dev, int update_eeprom) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as5812_54x_sfp_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (ALWAYS_UPDATE_DEVICE || time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid) { -+ int status = -1; -+ int i = 0, j = 0; -+ -+ data->valid = 0; -+ //dev_dbg(&client->dev, "Starting as5812_54x sfp status update\n"); -+ memset(data->status, 0, sizeof(data->status)); -+ -+ /* Read status of port 1~48(SFP port) */ -+ for (i = 0; i < 2; i++) { -+ for (j = 0; j < 12; j++) { -+ status = as5812_54x_i2c_cpld_read(0x61+i, 0x6+j); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ goto exit; -+ } -+ -+ data->status[j/3] |= (u64)status << ((i*24) + (j%3)*8); -+ } -+ } -+ -+ /* -+ * Bring QSFPs out of reset, -+ * This is a temporary fix until the QSFP+_MOD_RST register -+ * can be exposed through the driver. -+ */ -+ as5812_54x_i2c_cpld_write(0x62, 0x15, 0x3F); -+ -+ /* Read present status of port 49-54(QSFP port) */ -+ status = as5812_54x_i2c_cpld_read(0x62, 0x14); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", 0x61+i, 0x6+j, status); -+ } -+ else { -+ data->status[SFP_IS_PRESENT] |= (u64)status << 48; -+ } -+ -+ 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->status[SFP_IS_PRESENT] & BIT_INDEX(data->port)) == 0) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom); i++) { -+ status = as5812_54x_sfp_read_byte(client, i, data->eeprom + i); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", -+ CPLD_PORT_TO_FRONT_PORT(data->port)); -+ goto exit; -+ } -+ } -+ } -+ } -+ -+ data->valid = 1; -+ data->last_updated = jiffies; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+module_i2c_driver(as5812_54x_sfp_driver); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as5812_54x_sfp driver"); -+MODULE_LICENSE("GPL"); diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6712_32x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6712_32x-device-drivers.patch deleted file mode 100644 index 95ab532b..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6712_32x-device-drivers.patch +++ /dev/null @@ -1,2334 +0,0 @@ -Device driver patches for accton as6712 (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 52a68eb..2787ddd 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1422,7 +1422,6 @@ config SENSORS_CPR_4011_4MXX - This driver can also be built as a module. If so, the module will - be called cpr_4011_4mxx. - -- - config SENSORS_ACCTON_AS5712_54x_FAN - tristate "Accton as5712 54x fan" - depends on I2C && I2C_MUX_ACCTON_AS5712_54x_CPLD -@@ -1440,7 +1439,24 @@ config SENSORS_ACCTON_AS5712_54x_PSU - - This driver can also be built as a module. If so, the module will - be called accton_as5712_54x_psu. -+ -+config SENSORS_ACCTON_AS6712_32x_FAN -+ tristate "Accton as6712 32x fan" -+ depends on I2C && I2C_MUX_ACCTON_AS6712_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6712 32x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6712_32x_fan. -+ -+config SENSORS_ACCTON_AS6712_32x_PSU -+ tristate "Accton as6712 32x psu" -+ depends on I2C && I2C_MUX_ACCTON_AS6712_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6712 32x psu. - -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6712_32x_psu. - - if ACPI - -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index e2f3bce..50d7007 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -23,6 +23,8 @@ obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o - obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o - obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_FAN) += accton_as5712_54x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_PSU) += accton_as5712_54x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_FAN) += accton_as6712_32x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_PSU) += accton_as6712_32x_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as6712_32x_fan.c b/drivers/hwmon/accton_as6712_32x_fan.c -new file mode 100644 -index 0000000..9c7cac7 ---- /dev/null -+++ b/drivers/hwmon/accton_as6712_32x_fan.c -@@ -0,0 +1,434 @@ -+/* -+ * A hwmon driver for the Accton as6712 32x fan contrl -+ * -+ * Copyright (C) 2014 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 /* 10% ??*/ -+#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_as6712_32x_fan *fan_data = NULL; -+ -+struct accton_as6712_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_as6712_32x_fan_update_device(struct device *dev); -+static int accton_as6712_32x_fan_read_value(u8 reg); -+static int accton_as6712_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 as6712_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as6712_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_as6712_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_as6712_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_as6712_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_as6712_32x_fan_group = { -+ .attrs = accton_as6712_32x_fan_attributes, -+}; -+ -+static int accton_as6712_32x_fan_read_value(u8 reg) -+{ -+ return as6712_32x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as6712_32x_fan_write_value(u8 reg, u8 value) -+{ -+ return as6712_32x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as6712_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_as6712_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_as6712_32x_fan update 2 \n"); -+ -+ fault = accton_as6712_32x_fan_read_value(CPLD_REG_FAN_STATUS_OFFSET); -+ r_fault = accton_as6712_32x_fan_read_value(CPLD_REG_FANR_STATUS_OFFSET); -+ direction = accton_as6712_32x_fan_read_value(CPLD_REG_FAN_DIRECTION_OFFSET); -+ ctrl_speed = accton_as6712_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_as6712_32x_fan_read_value(fan_speed_reg[i]); -+ r_speed = accton_as6712_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_as6712_32x_fan_probe(struct platform_device *pdev) -+{ -+ int status = -1; -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &accton_as6712_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_as6712_32x_fan\n"); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &accton_as6712_32x_fan_group); -+exit: -+ return status; -+} -+ -+static int accton_as6712_32x_fan_remove(struct platform_device *pdev) -+{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&fan_data->pdev->dev.kobj, &accton_as6712_32x_fan_group); -+ -+ return 0; -+} -+ -+#define DRVNAME "as6712_32x_fan" -+ -+static struct platform_driver accton_as6712_32x_fan_driver = { -+ .probe = accton_as6712_32x_fan_probe, -+ .remove = accton_as6712_32x_fan_remove, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as6712_32x_fan_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as6712_32x(void); -+ if(!platform_accton_as6712_32x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as6712_32x_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ fan_data = kzalloc(sizeof(struct accton_as6712_32x_fan), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as6712_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_as6712_32x_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as6712_32x_fan_exit(void) -+{ -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&accton_as6712_32x_fan_driver); -+ kfree(fan_data); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as6712_32x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_as6712_32x_fan_init); -+module_exit(accton_as6712_32x_fan_exit); -+ -diff --git a/drivers/hwmon/accton_as6712_32x_psu.c b/drivers/hwmon/accton_as6712_32x_psu.c -new file mode 100644 -index 0000000..ef9fadf ---- /dev/null -+++ b/drivers/hwmon/accton_as6712_32x_psu.c -@@ -0,0 +1,304 @@ -+/* -+ * An hwmon driver for accton as6712_32x Power Module -+ * -+ * Copyright (C) 2014 Accton Technology Corporation. -+ * -+ * 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 as6712_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int as6712_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 as6712_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 as6712_32x_psu_data *as6712_32x_psu_update_device(struct device *dev); -+ -+enum as6712_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 *as6712_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 as6712_32x_psu_data *data = as6712_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 as6712_32x_psu_data *data = as6712_32x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s\n", data->model_name); -+} -+ -+static const struct attribute_group as6712_32x_psu_group = { -+ .attrs = as6712_32x_psu_attributes, -+}; -+ -+static int as6712_32x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as6712_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 as6712_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, &as6712_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, &as6712_32x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as6712_32x_psu_remove(struct i2c_client *client) -+{ -+ struct as6712_32x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as6712_32x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as6712_32x_psu_id[] = { -+ { "as6712_32x_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as6712_32x_psu_id); -+ -+static struct i2c_driver as6712_32x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as6712_32x_psu", -+ }, -+ .probe = as6712_32x_psu_probe, -+ .remove = as6712_32x_psu_remove, -+ .id_table = as6712_32x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as6712_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 as6712_32x_psu_data *as6712_32x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as6712_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 as6712_32x update\n"); -+ -+ /* Read psu status */ -+ status = as6712_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 */ -+ /* um400d01x DC power */ -+ command = 0x50; -+ model_name_len = 13; -+ } -+ -+ status = as6712_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 as6712_32x_psu_init(void) -+{ -+ extern int platform_accton_as6712_32x(void); -+ if(!platform_accton_as6712_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as6712_32x_psu_driver); -+} -+ -+static void __exit as6712_32x_psu_exit(void) -+{ -+ i2c_del_driver(&as6712_32x_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as6712_32x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as6712_32x_psu_init); -+module_exit(as6712_32x_psu_exit); -diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig -index 339e2b2..4bed9f6 100644 ---- a/drivers/i2c/muxes/Kconfig -+++ b/drivers/i2c/muxes/Kconfig -@@ -14,7 +14,16 @@ config I2C_MUX_ACCTON_AS5712_54x_CPLD - - This driver can also be built as a module. If so, the module - will be called i2c-mux-accton_as5712_54x_cpld. -+ -+config I2C_MUX_ACCTON_AS6712_32x_CPLD -+ tristate "Accton as6712_32x CPLD I2C multiplexer" -+ help -+ If you say yes here you get support for the Accton CPLD -+ I2C mux devices. - -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mux-accton_as6712_32x_cpld. -+ - config I2C_MUX_GPIO - tristate "GPIO-based I2C multiplexer" - depends on GENERIC_GPIO -diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile -index 997522c..2bcfa0c 100644 ---- a/drivers/i2c/muxes/Makefile -+++ b/drivers/i2c/muxes/Makefile -@@ -7,5 +7,6 @@ obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o - obj-$(CONFIG_I2C_MUX_DNI_6448) += dni_6448_i2c_mux.o - obj-$(CONFIG_I2C_MUX_QUANTA) += quanta-i2cmux.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS5712_54x_CPLD) += i2c-mux-accton_as5712_54x_cpld.o -+obj-$(CONFIG_I2C_MUX_ACCTON_AS6712_32x_CPLD) += i2c-mux-accton_as6712_32x_cpld.o - - ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG -diff --git a/drivers/i2c/muxes/i2c-mux-accton_as6712_32x_cpld.c b/drivers/i2c/muxes/i2c-mux-accton_as6712_32x_cpld.c -new file mode 100644 -index 0000000..2ec0a59 ---- /dev/null -+++ b/drivers/i2c/muxes/i2c-mux-accton_as6712_32x_cpld.c -@@ -0,0 +1,427 @@ -+/* -+ * I2C multiplexer -+ * -+ * Copyright (C) 2014 Accton Technology Corporation. -+ * -+ * This module supports the accton cpld that hold the channel select -+ * mechanism for other i2c slave devices, such as SFP. -+ * This includes the: -+ * Accton as6712_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 as6712_dmi_table[] = { -+ { -+ .ident = "Accton AS6712", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS6712"), -+ }, -+ }, -+ { -+ .ident = "Accton AS6712", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS6712"), -+ }, -+ }, -+}; -+ -+int platform_accton_as6712_32x(void) -+{ -+ return dmi_check_system(as6712_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as6712_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 { -+ as6712_32x_cpld2, -+ as6712_32x_cpld3, -+ as6712_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[] = { -+ [as6712_32x_cpld1] = { -+ .nchans = NUM_OF_CPLD1_CHANS, -+ .deselectChan = NUM_OF_CPLD1_CHANS, -+ }, -+ [as6712_32x_cpld2] = { -+ .nchans = NUM_OF_CPLD2_CHANS, -+ .deselectChan = NUM_OF_CPLD2_CHANS, -+ }, -+ [as6712_32x_cpld3] = { -+ .nchans = NUM_OF_CPLD3_CHANS, -+ .deselectChan = NUM_OF_CPLD3_CHANS, -+ } -+}; -+ -+static const struct i2c_device_id accton_i2c_cpld_mux_id[] = { -+ { "as6712_32x_cpld1", as6712_32x_cpld1 }, -+ { "as6712_32x_cpld2", as6712_32x_cpld2 }, -+ { "as6712_32x_cpld3", as6712_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) -+{ -+#if 0 -+ int ret = -ENODEV; -+ -+ //if (adap->algo->master_xfer) { -+ if (0) -+ struct i2c_msg msg; -+ char buf[2]; -+ -+ msg.addr = client->addr; -+ msg.flags = 0; -+ msg.len = 2; -+ buf[0] = 0x2; -+ buf[1] = val; -+ msg.buf = buf; -+ ret = adap->algo->master_xfer(adap, &msg, 1); -+ } -+ else { -+ union i2c_smbus_data data; -+ ret = adap->algo->smbus_xfer(adap, client->addr, -+ client->flags, -+ I2C_SMBUS_WRITE, -+ 0x2, I2C_SMBUS_BYTE, &data); -+ } -+ -+ return ret; -+#else -+ 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; -+#endif -+} -+ -+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); -+ -+#if 0 -+ /* Write the mux register at addr to verify -+ * that the mux is in fact present. -+ */ -+ if (i2c_smbus_write_byte(client, 0) < 0) { -+ dev_warn(&client->dev, "probe failed\n"); -+ goto exit_free; -+ } -+#endif -+ -+ data->type = id->driver_data; -+ -+ if (data->type == as6712_32x_cpld2 || data->type == as6712_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, -+ 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 as6712_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(as6712_32x_i2c_cpld_read); -+ -+int as6712_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(as6712_32x_i2c_cpld_write); -+ -+static struct i2c_driver accton_i2c_cpld_mux_driver = { -+ .driver = { -+ .name = "as6712_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/drivers/leds/Kconfig b/drivers/leds/Kconfig -index 361ef45..a5ccde1 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -47,6 +47,13 @@ config LEDS_ACCTON_AS5712_54x - This option enables support for the LEDs on the Accton as5712 54x. - Say Y to enable LEDs on the Accton as5712 54x. - -+config LEDS_ACCTON_AS6712_32x -+ tristate "LED support for the Accton as6712 32x" -+ depends on LEDS_CLASS && I2C_MUX_ACCTON_AS6712_32x_CPLD -+ help -+ This option enables support for the LEDs on the Accton as6712 32x. -+ Say Y to enable LEDs on the Accton as6712 32x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index db2d096..d952f0f 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -44,6 +44,7 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o - obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o - obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o - obj-$(CONFIG_LEDS_ACCTON_AS5712_54x) += leds-accton_as5712_54x.o -+obj-$(CONFIG_LEDS_ACCTON_AS6712_32x) += leds-accton_as6712_32x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as6712_32x.c b/drivers/leds/leds-accton_as6712_32x.c -new file mode 100644 -index 0000000..c1da3bc ---- /dev/null -+++ b/drivers/leds/leds-accton_as6712_32x.c -@@ -0,0 +1,617 @@ -+/* -+ * A LED driver for the accton_as6712_32x_led -+ * -+ * Copyright (C) 2014 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 as6712_32x_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int as6712_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 "as6712_32x_led" -+ -+struct accton_as6712_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_as6712_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; -+}; -+ -+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_as6712_32x_led_read_value(u8 reg) -+{ -+ return as6712_32x_i2c_cpld_read(0x60, reg); -+} -+ -+static int accton_as6712_32x_led_write_value(u8 reg, u8 value) -+{ -+ return as6712_32x_i2c_cpld_write(0x60, reg, value); -+} -+ -+static void accton_as6712_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_as6712_32x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as6712_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_as6712_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_as6712_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_as6712_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_as6712_32x_led_psu_1_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU1); -+} -+ -+static enum led_brightness accton_as6712_32x_led_psu_1_get(struct led_classdev *cdev) -+{ -+ accton_as6712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU1, ledctl->reg_val[1]); -+} -+ -+static void accton_as6712_32x_led_psu_2_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[1], LED_TYPE_PSU2); -+} -+ -+static enum led_brightness accton_as6712_32x_led_psu_2_get(struct led_classdev *cdev) -+{ -+ accton_as6712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_PSU2, ledctl->reg_val[1]); -+} -+ -+static void accton_as6712_32x_led_fan_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_FAN); -+} -+ -+static enum led_brightness accton_as6712_32x_led_fan_get(struct led_classdev *cdev) -+{ -+ accton_as6712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_FAN, ledctl->reg_val[0]); -+} -+ -+ -+static void accton_as6712_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_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[reg_id], led_type1); -+ return; -+ } -+ } -+} -+ -+ -+static enum led_brightness accton_as6712_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_as6712_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_as6712_32x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as6712_32x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as6712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as6712_32x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as6712_32x_led_set(led_cdev, led_light_mode, led_reg[0], LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as6712_32x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as6712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static struct led_classdev accton_as6712_32x_leds[] = { -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as6712_32x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_psu_1_set, -+ .brightness_get = accton_as6712_32x_led_psu_1_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as6712_32x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_psu_2_set, -+ .brightness_get = accton_as6712_32x_led_psu_2_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as6712_32x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fan_set, -+ .brightness_get = accton_as6712_32x_led_fan_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN1] = { -+ .name = "accton_as6712_32x_led::fan1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fanx_set, -+ .brightness_get = accton_as6712_32x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN2] = { -+ .name = "accton_as6712_32x_led::fan2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fanx_set, -+ .brightness_get = accton_as6712_32x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN3] = { -+ .name = "accton_as6712_32x_led::fan3", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fanx_set, -+ .brightness_get = accton_as6712_32x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN4] = { -+ .name = "accton_as6712_32x_led::fan4", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fanx_set, -+ .brightness_get = accton_as6712_32x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_FAN5] = { -+ .name = "accton_as6712_32x_led::fan5", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_fanx_set, -+ .brightness_get = accton_as6712_32x_led_fanx_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as6712_32x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_diag_set, -+ .brightness_get = accton_as6712_32x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as6712_32x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as6712_32x_led_loc_set, -+ .brightness_get = accton_as6712_32x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as6712_32x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as6712_32x_leds); i++) { -+ led_classdev_suspend(&accton_as6712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as6712_32x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as6712_32x_leds); i++) { -+ led_classdev_resume(&accton_as6712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as6712_32x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as6712_32x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as6712_32x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as6712_32x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as6712_32x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as6712_32x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as6712_32x_leds); i++) { -+ led_classdev_unregister(&accton_as6712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as6712_32x_led_driver = { -+ .probe = accton_as6712_32x_led_probe, -+ .remove = accton_as6712_32x_led_remove, -+ .suspend = accton_as6712_32x_led_suspend, -+ .resume = accton_as6712_32x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as6712_32x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as6712_32x(void); -+ if(!platform_accton_as6712_32x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as6712_32x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as6712_32x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as6712_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_as6712_32x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as6712_32x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as6712_32x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as6712_32x_led_init); -+module_exit(accton_as6712_32x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as6712_32x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 4e5d6dc..6e025e9 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -81,7 +81,15 @@ config EEPROM_ACCTON_AS5712_54x_SFP - - This driver can also be built as a module. If so, the module will - be called accton_as5712_54x_sfp. -- -+ -+config EEPROM_ACCTON_AS6712_32x_SFP -+ tristate "Accton as6712 32x sfp" -+ depends on I2C && I2C_MUX_ACCTON_AS6712_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6712 32x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6712_32x_sfp. - - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 807158a..9001de9 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -7,4 +7,5 @@ obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o - obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o - obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o - obj-$(CONFIG_EEPROM_ACCTON_AS5712_54x_SFP) += accton_as5712_54x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS6712_32x_SFP) += accton_as6712_32x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as6712_32x_sfp.c b/drivers/misc/eeprom/accton_as6712_32x_sfp.c -new file mode 100644 -index 0000000..8253134 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as6712_32x_sfp.c -@@ -0,0 +1,377 @@ -+/* -+ * An hwmon driver for accton as6712_32x sfp -+ * -+ * Copyright (C) 2014 Accton Technology Corporation. -+ * -+ * 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 as6712_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 as6712_32x_sfp_data *as6712_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 as6712_32x_sfp_read_byte(struct i2c_client *client, u8 command, u8 *data); -+extern int as6712_32x_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as6712_32x_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+//extern int accton_i2c_cpld_mux_get_index(int adap_index); -+ -+enum as6712_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 *as6712_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 as6712_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], as6712_32x_i2c_cpld_read(0x62, 0xA), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], as6712_32x_i2c_cpld_read(0x62, 0xB), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], as6712_32x_i2c_cpld_read(0x64, 0xA), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], as6712_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 as6712_32x_sfp_data *data = as6712_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 as6712_32x_sfp_data *data = as6712_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 as6712_32x_sfp_group = { -+ .attrs = as6712_32x_sfp_attributes, -+}; -+ -+static int as6712_32x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as6712_32x_sfp_data *data; -+ int status; -+ -+ extern int platform_accton_as6712_32x(void); -+ if(!platform_accton_as6712_32x()) { -+ return -ENODEV; -+ } -+ -+ if (!i2c_check_functionality(client->adapter, /*I2C_FUNC_SMBUS_BYTE_DATA | */I2C_FUNC_SMBUS_WORD_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as6712_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, &as6712_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, &as6712_32x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as6712_32x_sfp_remove(struct i2c_client *client) -+{ -+ struct as6712_32x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as6712_32x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as6712_32x_sfp1, as6712_32x_sfp2, as6712_32x_sfp3, as6712_32x_sfp4, -+as6712_32x_sfp5, as6712_32x_sfp6, as6712_32x_sfp7, as6712_32x_sfp8, -+as6712_32x_sfp9, as6712_32x_sfp10, as6712_32x_sfp11,as6712_32x_sfp12, -+as6712_32x_sfp13, as6712_32x_sfp14, as6712_32x_sfp15,as6712_32x_sfp16, -+as6712_32x_sfp17, as6712_32x_sfp18, as6712_32x_sfp19,as6712_32x_sfp20, -+as6712_32x_sfp21, as6712_32x_sfp22, as6712_32x_sfp23,as6712_32x_sfp24, -+as6712_32x_sfp25, as6712_32x_sfp26, as6712_32x_sfp27,as6712_32x_sfp28, -+as6712_32x_sfp29, as6712_32x_sfp30, as6712_32x_sfp31,as6712_32x_sfp32 -+}; -+ -+static const struct i2c_device_id as6712_32x_sfp_id[] = { -+{ "as6712_32x_sfp1", as6712_32x_sfp1 }, { "as6712_32x_sfp2", as6712_32x_sfp2 }, -+{ "as6712_32x_sfp3", as6712_32x_sfp3 }, { "as6712_32x_sfp4", as6712_32x_sfp4 }, -+{ "as6712_32x_sfp5", as6712_32x_sfp5 }, { "as6712_32x_sfp6", as6712_32x_sfp6 }, -+{ "as6712_32x_sfp7", as6712_32x_sfp7 }, { "as6712_32x_sfp8", as6712_32x_sfp8 }, -+{ "as6712_32x_sfp9", as6712_32x_sfp9 }, { "as6712_32x_sfp10", as6712_32x_sfp10 }, -+{ "as6712_32x_sfp11", as6712_32x_sfp11 }, { "as6712_32x_sfp12", as6712_32x_sfp12 }, -+{ "as6712_32x_sfp13", as6712_32x_sfp13 }, { "as6712_32x_sfp14", as6712_32x_sfp14 }, -+{ "as6712_32x_sfp15", as6712_32x_sfp15 }, { "as6712_32x_sfp16", as6712_32x_sfp16 }, -+{ "as6712_32x_sfp17", as6712_32x_sfp17 }, { "as6712_32x_sfp18", as6712_32x_sfp18 }, -+{ "as6712_32x_sfp19", as6712_32x_sfp19 }, { "as6712_32x_sfp20", as6712_32x_sfp20 }, -+{ "as6712_32x_sfp21", as6712_32x_sfp21 }, { "as6712_32x_sfp22", as6712_32x_sfp22 }, -+{ "as6712_32x_sfp23", as6712_32x_sfp23 }, { "as6712_32x_sfp24", as6712_32x_sfp24 }, -+{ "as6712_32x_sfp25", as6712_32x_sfp25 }, { "as6712_32x_sfp26", as6712_32x_sfp26 }, -+{ "as6712_32x_sfp27", as6712_32x_sfp27 }, { "as6712_32x_sfp28", as6712_32x_sfp28 }, -+{ "as6712_32x_sfp29", as6712_32x_sfp29 }, { "as6712_32x_sfp30", as6712_32x_sfp30 }, -+{ "as6712_32x_sfp31", as6712_32x_sfp31 }, { "as6712_32x_sfp32", as6712_32x_sfp32 }, -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as6712_32x_sfp_id); -+ -+ -+static struct i2c_driver as6712_32x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as6712_32x_sfp", -+ }, -+ .probe = as6712_32x_sfp_probe, -+ .remove = as6712_32x_sfp_remove, -+ .id_table = as6712_32x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+#if 0 -+static int as6712_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 as6712_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 as6712_32x_sfp_data *as6712_32x_sfp_update_device(struct device *dev, int update_eeprom) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as6712_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 = as6712_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 = as6712_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(as6712_32x_sfp_driver); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as6712_32x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6812_32x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6812_32x-device-drivers.patch deleted file mode 100644 index 5fe16e5e..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as6812_32x-device-drivers.patch +++ /dev/null @@ -1,2300 +0,0 @@ -Device driver patches for accton as6812-32x (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 4d9fb22..73ee085 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1538,6 +1538,24 @@ config SENSORS_ACCTON_AS5812_54x_PSU - This driver can also be built as a module. If so, the module will - be called accton_as5812_54x_psu. - -+config SENSORS_ACCTON_AS6812_32x_FAN -+ tristate "Accton as6812 32x fan" -+ depends on I2C && I2C_MUX_ACCTON_AS6812_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6812 32x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6812_32x_fan. -+ -+config SENSORS_ACCTON_AS6812_32x_PSU -+ tristate "Accton as6812 32x psu" -+ depends on I2C && I2C_MUX_ACCTON_AS6812_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6812 32x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6812_32x_psu. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 818dd01..7700250 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -32,6 +32,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS7712_32x_PSU) += accton_as7712_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_I2C_CPLD) += accton_i2c_cpld.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_FAN) += accton_as5812_54x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54x_PSU) += accton_as5812_54x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_FAN) += accton_as6812_32x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS6812_32x_PSU) += accton_as6812_32x_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as6812_32x_fan.c b/drivers/hwmon/accton_as6812_32x_fan.c -new file mode 100644 -index 0000000..f055567 ---- /dev/null -+++ b/drivers/hwmon/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/drivers/hwmon/accton_as6812_32x_psu.c b/drivers/hwmon/accton_as6812_32x_psu.c -new file mode 100644 -index 0000000..dfee68b ---- /dev/null -+++ b/drivers/hwmon/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/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig -index 8ac67ef..b378ade 100644 ---- a/drivers/i2c/muxes/Kconfig -+++ b/drivers/i2c/muxes/Kconfig -@@ -33,6 +33,15 @@ config I2C_MUX_ACCTON_AS5812_54x_CPLD - This driver can also be built as a module. If so, the module - will be called i2c-mux-accton_as5812_54x_cpld. - -+config I2C_MUX_ACCTON_AS6812_32x_CPLD -+ tristate "Accton as6812_32x CPLD I2C multiplexer" -+ help -+ If you say yes here you get support for the Accton CPLD -+ I2C mux devices. -+ -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mux-accton_as6812_32x_cpld. -+ - config I2C_MUX_GPIO - tristate "GPIO-based I2C multiplexer" - depends on GENERIC_GPIO -diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile -index 7769d29..840a66c 100644 ---- a/drivers/i2c/muxes/Makefile -+++ b/drivers/i2c/muxes/Makefile -@@ -10,5 +10,6 @@ obj-$(CONFIG_I2C_MUX_QUANTA_LY2) += quanta-ly2-i2c-mux.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS5712_54x_CPLD) += i2c-mux-accton_as5712_54x_cpld.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS6712_32x_CPLD) += i2c-mux-accton_as6712_32x_cpld.o - obj-$(CONFIG_I2C_MUX_ACCTON_AS5812_54x_CPLD) += i2c-mux-accton_as5812_54x_cpld.o -+obj-$(CONFIG_I2C_MUX_ACCTON_AS6812_32x_CPLD) += i2c-mux-accton_as6812_32x_cpld.o - - ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG -diff --git a/drivers/i2c/muxes/i2c-mux-accton_as6812_32x_cpld.c b/drivers/i2c/muxes/i2c-mux-accton_as6812_32x_cpld.c -new file mode 100644 -index 0000000..d668ca4 ---- /dev/null -+++ b/drivers/i2c/muxes/i2c-mux-accton_as6812_32x_cpld.c -@@ -0,0 +1,389 @@ -+/* -+ * 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, -+ 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/drivers/leds/Kconfig b/drivers/leds/Kconfig -index 514f978..cb0c17f 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -75,6 +75,13 @@ config LEDS_ACCTON_AS5812_54x - This option enables support for the LEDs on the Accton as5812 54x. - Say Y to enable LEDs on the Accton as5812 54x. - -+config LEDS_ACCTON_AS6812_32x -+ tristate "LED support for the Accton as6812 32x" -+ depends on LEDS_CLASS && I2C_MUX_ACCTON_AS6812_32x_CPLD -+ help -+ This option enables support for the LEDs on the Accton as6812 32x. -+ Say Y to enable LEDs on the Accton as6812 32x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index 379c448..8db7a43 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -48,6 +48,7 @@ obj-$(CONFIG_LEDS_ACCTON_AS6712_32x) += leds-accton_as6712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7512_32x) += leds-accton_as7512_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7712_32x) += leds-accton_as7712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS5812_54x) += leds-accton_as5812_54x.o -+obj-$(CONFIG_LEDS_ACCTON_AS6812_32x) += leds-accton_as6812_32x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as6812_32x.c b/drivers/leds/leds-accton_as6812_32x.c -new file mode 100644 -index 0000000..59c59cb ---- /dev/null -+++ b/drivers/leds/leds-accton_as6812_32x.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/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 7c8d3b8..ff68df7 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -118,6 +118,15 @@ config EEPROM_ACCTON_AS5812_54x_SFP - This driver can also be built as a module. If so, the module will - be called accton_as5812_54x_sfp. - -+config EEPROM_ACCTON_AS6812_32x_SFP -+ tristate "Accton as6812 32x sfp" -+ depends on I2C && I2C_MUX_ACCTON_AS6812_32x_CPLD -+ help -+ If you say yes here you get support for Accton as6812 32x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as6812_32x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index e11d273..4b682a1 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -11,4 +11,5 @@ obj-$(CONFIG_EEPROM_ACCTON_AS6712_32x_SFP) += accton_as6712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7512_32x_SFP) += accton_as7512_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7712_32x_SFP) += accton_as7712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5812_54x_SFP) += accton_as5812_54x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS6812_32x_SFP) += accton_as6812_32x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as6812_32x_sfp.c b/drivers/misc/eeprom/accton_as6812_32x_sfp.c -new file mode 100644 -index 0000000..1669fb8 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as6812_32x_sfp.c -@@ -0,0 +1,390 @@ -+/* -+ * 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; -+} -+ -+static int __init as6812_32x_sfp_init(void) -+{ -+ extern int platform_accton_as6812_32x(void); -+ if(!platform_accton_as6812_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as6812_32x_sfp_driver); -+} -+ -+static void __exit as6812_32x_sfp_exit(void) -+{ -+ i2c_del_driver(&as6812_32x_sfp_driver); -+} -+ -+module_i2c_driver(as6812_32x_sfp_driver); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as6812_32x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as6812_32x_sfp_init); -+module_exit(as6812_32x_sfp_exit); diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7512_32x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7512_32x-device-drivers.patch deleted file mode 100644 index e5028ae3..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7512_32x-device-drivers.patch +++ /dev/null @@ -1,2675 +0,0 @@ -Device driver patches for accton as7512 (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 1ff287b..c410426 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1465,6 +1465,43 @@ config SENSORS_ACCTON_AS6712_32x_PSU - This driver can also be built as a module. If so, the module will - be called accton_as6712_32x_psu. - -+config SENSORS_ACCTON_I2C_CPLD -+ tristate "Accton i2c cpld" -+ depends on I2C -+ help -+ If you say yes here you get support for Accton i2c cpld. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_i2c_cpld. -+ -+config SENSORS_ACCTON_AS7512_32x_FAN -+ tristate "Accton as7512 32x fan" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7512 32x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7512_32x_fan. -+ -+config SENSORS_ACCTON_AS7512_32x_PSU -+ tristate "Accton as7512 32x psu" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7512 32x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7512_32x_psu. -+ -+config SENSORS_YM2651Y -+ tristate "3Y Power YM-2651Y Power Module" -+ depends on I2C -+ help -+ If you say yes here you get support for 3Y Power YM-2651Y -+ Power Module. -+ -+ This driver can also be built as a module. If so, the module will -+ be called ym2651y. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index dbbb5ee..f8ee399 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -25,6 +25,9 @@ obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_FAN) += accton_as5712_54x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5712_54x_PSU) += accton_as5712_54x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_FAN) += accton_as6712_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_PSU) += accton_as6712_32x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7512_32x_FAN) += accton_as7512_32x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7512_32x_PSU) += accton_as7512_32x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_I2C_CPLD) += accton_i2c_cpld.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -@@ -136,6 +139,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o - obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o - obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o - obj-$(CONFIG_SENSORS_QUANTA_LY_HWMON) += quanta-ly-hwmon.o -+obj-$(CONFIG_SENSORS_YM2651Y) += ym2651y.o - - obj-$(CONFIG_PMBUS) += pmbus/ - -diff --git a/drivers/hwmon/accton_as7512_32x_fan.c b/drivers/hwmon/accton_as7512_32x_fan.c -new file mode 100644 -index 0000000..be323b5 ---- /dev/null -+++ b/drivers/hwmon/accton_as7512_32x_fan.c -@@ -0,0 +1,510 @@ -+/* -+ * A hwmon driver for the Accton as7512 32x fan -+ * -+ * Copyright (C) 2014 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 -+ -+#define DRVNAME "as7512_32x_fan" -+ -+static struct as7512_32x_fan_data *as7512_32x_fan_update_device(struct device *dev); -+static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+/* fan related data, the index should match sysfs_fan_attributes -+ */ -+static const u8 fan_reg[] = { -+ 0x0F, /* fan 1-6 present status */ -+ 0x10, /* fan 1-6 direction(0:B2F 1:F2B) */ -+ 0x11, /* fan PWM(for all fan) */ -+ 0x12, /* front fan 1 speed(rpm) */ -+ 0x13, /* front fan 2 speed(rpm) */ -+ 0x14, /* front fan 3 speed(rpm) */ -+ 0x15, /* front fan 4 speed(rpm) */ -+ 0x16, /* front fan 5 speed(rpm) */ -+ 0x17, /* front fan 6 speed(rpm) */ -+ 0x22, /* rear fan 1 speed(rpm) */ -+ 0x23, /* rear fan 2 speed(rpm) */ -+ 0x24, /* rear fan 3 speed(rpm) */ -+ 0x25, /* rear fan 4 speed(rpm) */ -+ 0x26, /* rear fan 5 speed(rpm) */ -+ 0x27, /* rear fan 6 speed(rpm) */ -+}; -+ -+/* Each client has this additional data */ -+struct as7512_32x_fan_data { -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+ char valid; /* != 0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ -+}; -+ -+enum fan_id { -+ FAN1_ID, -+ FAN2_ID, -+ FAN3_ID, -+ FAN4_ID, -+ FAN5_ID, -+ FAN6_ID -+}; -+ -+enum sysfs_fan_attributes { -+ FAN_PRESENT_REG, -+ FAN_DIRECTION_REG, -+ FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ -+ FAN1_FRONT_SPEED_RPM, -+ FAN2_FRONT_SPEED_RPM, -+ FAN3_FRONT_SPEED_RPM, -+ FAN4_FRONT_SPEED_RPM, -+ FAN5_FRONT_SPEED_RPM, -+ FAN6_FRONT_SPEED_RPM, -+ FAN1_REAR_SPEED_RPM, -+ FAN2_REAR_SPEED_RPM, -+ FAN3_REAR_SPEED_RPM, -+ FAN4_REAR_SPEED_RPM, -+ FAN5_REAR_SPEED_RPM, -+ FAN6_REAR_SPEED_RPM, -+ FAN1_DIRECTION, -+ FAN2_DIRECTION, -+ FAN3_DIRECTION, -+ FAN4_DIRECTION, -+ FAN5_DIRECTION, -+ FAN6_DIRECTION, -+ FAN1_PRESENT, -+ FAN2_PRESENT, -+ FAN3_PRESENT, -+ FAN4_PRESENT, -+ FAN5_PRESENT, -+ FAN6_PRESENT, -+ FAN1_FAULT, -+ FAN2_FAULT, -+ FAN3_FAULT, -+ FAN4_FAULT, -+ FAN5_FAULT, -+ FAN6_FAULT -+}; -+ -+/* Define attributes -+ */ -+#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) -+#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr -+ -+#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) -+#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr -+ -+#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE) -+#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan##index##_duty_cycle_percentage.dev_attr.attr -+ -+#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) -+#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr -+ -+#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ -+ static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) -+#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ -+ &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr -+ -+/* 6 fan fault attributes in this platform */ -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6); -+ -+#if 0 -+/* 6 fan direction attribute in this platform */ -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); -+#endif -+ -+/* 6 fan speed(rpm) attributes in this platform */ -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6); -+/* 6 fan present attributes in this platform */ -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); -+/* 1 fan duty cycle attribute in this platform */ -+DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(); -+ -+static struct attribute *as7512_32x_fan_attributes[] = { -+ /* fan related attributes */ -+ DECLARE_FAN_FAULT_ATTR(1), -+ DECLARE_FAN_FAULT_ATTR(2), -+ DECLARE_FAN_FAULT_ATTR(3), -+ DECLARE_FAN_FAULT_ATTR(4), -+ DECLARE_FAN_FAULT_ATTR(5), -+ DECLARE_FAN_FAULT_ATTR(6), -+#if 0 -+ DECLARE_FAN_DIRECTION_ATTR(1), -+ DECLARE_FAN_DIRECTION_ATTR(2), -+ DECLARE_FAN_DIRECTION_ATTR(3), -+ DECLARE_FAN_DIRECTION_ATTR(4), -+ DECLARE_FAN_DIRECTION_ATTR(5), -+ DECLARE_FAN_DIRECTION_ATTR(6), -+#endif -+ DECLARE_FAN_SPEED_RPM_ATTR(1), -+ DECLARE_FAN_SPEED_RPM_ATTR(2), -+ DECLARE_FAN_SPEED_RPM_ATTR(3), -+ DECLARE_FAN_SPEED_RPM_ATTR(4), -+ DECLARE_FAN_SPEED_RPM_ATTR(5), -+ DECLARE_FAN_SPEED_RPM_ATTR(6), -+ DECLARE_FAN_PRESENT_ATTR(1), -+ DECLARE_FAN_PRESENT_ATTR(2), -+ DECLARE_FAN_PRESENT_ATTR(3), -+ DECLARE_FAN_PRESENT_ATTR(4), -+ DECLARE_FAN_PRESENT_ATTR(5), -+ DECLARE_FAN_PRESENT_ATTR(6), -+ DECLARE_FAN_DUTY_CYCLE_ATTR(), -+ NULL -+}; -+ -+#define FAN_DUTY_CYCLE_REG_MASK 0x0F -+#define FAN_MAX_DUTY_CYCLE 100 -+#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 -+ -+static int as7512_32x_fan_read_value(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_byte_data(client, reg); -+} -+ -+static int as7512_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) -+{ -+ return i2c_smbus_write_byte_data(client, reg, value); -+} -+ -+/* fan utility functions -+ */ -+static u32 reg_val_to_duty_cycle(u8 reg_val) -+{ -+ reg_val &= FAN_DUTY_CYCLE_REG_MASK; -+ return (u32)(reg_val+1) * 625 / 100; -+} -+ -+static u8 duty_cycle_to_reg_val(u8 duty_cycle) -+{ -+ return ((u32)duty_cycle * 100 / 625) - 1; -+} -+ -+static u32 reg_val_to_speed_rpm(u8 reg_val) -+{ -+ return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; -+} -+ -+static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) -+{ -+ u8 mask = (1 << id); -+ -+ reg_val &= mask; -+ -+ return reg_val ? 1 : 0; -+} -+ -+static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) -+{ -+ u8 mask = (1 << id); -+ -+ reg_val &= mask; -+ -+ return reg_val ? 0 : 1; -+} -+ -+static u8 is_fan_fault(struct as7512_32x_fan_data *data, enum fan_id id) -+{ -+ u8 ret = 1; -+ int front_fan_index = FAN1_FRONT_SPEED_RPM + id; -+ int rear_fan_index = FAN1_REAR_SPEED_RPM + id; -+ -+ /* Check if the speed of front or rear fan is ZERO, -+ */ -+ if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && -+ reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { -+ ret = 0; -+ } -+ -+ return ret; -+} -+ -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ int error, value; -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7512_32x_fan_data *data = i2c_get_clientdata(client); -+ -+ error = kstrtoint(buf, 10, &value); -+ if (error) { -+ return error; -+ } -+ -+ if (value < 0 || value > FAN_MAX_DUTY_CYCLE) { -+ return -EINVAL; -+ } -+ -+ /* Disable the watchdog timer -+ */ -+ error = as7512_32x_fan_write_value(client, 0x33, 0); -+ -+ if (error != 0) { -+ dev_dbg(&client->dev, "Unable to disable the watchdog timer\n"); -+ return error; -+ } -+ -+ as7512_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); -+ data->valid = 0; -+ -+ return count; -+} -+ -+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); -+ struct as7512_32x_fan_data *data = as7512_32x_fan_update_device(dev); -+ ssize_t ret = 0; -+ -+ if (data->valid) { -+ switch (attr->index) { -+ case FAN_DUTY_CYCLE_PERCENTAGE: -+ { -+ u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); -+ ret = sprintf(buf, "%u\n", duty_cycle); -+ break; -+ } -+ case FAN1_FRONT_SPEED_RPM: -+ case FAN2_FRONT_SPEED_RPM: -+ case FAN3_FRONT_SPEED_RPM: -+ case FAN4_FRONT_SPEED_RPM: -+ case FAN5_FRONT_SPEED_RPM: -+ case FAN6_FRONT_SPEED_RPM: -+ case FAN1_REAR_SPEED_RPM: -+ case FAN2_REAR_SPEED_RPM: -+ case FAN3_REAR_SPEED_RPM: -+ case FAN4_REAR_SPEED_RPM: -+ case FAN5_REAR_SPEED_RPM: -+ case FAN6_REAR_SPEED_RPM: -+ ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); -+ break; -+ case FAN1_DIRECTION: -+ case FAN2_DIRECTION: -+ case FAN3_DIRECTION: -+ case FAN4_DIRECTION: -+ case FAN5_DIRECTION: -+ case FAN6_DIRECTION: -+ ret = sprintf(buf, "%d\n", -+ reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], -+ attr->index - FAN1_DIRECTION)); -+ break; -+ case FAN1_PRESENT: -+ case FAN2_PRESENT: -+ case FAN3_PRESENT: -+ case FAN4_PRESENT: -+ case FAN5_PRESENT: -+ case FAN6_PRESENT: -+ ret = sprintf(buf, "%d\n", -+ reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], -+ attr->index - FAN1_PRESENT)); -+ break; -+ case FAN1_FAULT: -+ case FAN2_FAULT: -+ case FAN3_FAULT: -+ case FAN4_FAULT: -+ case FAN5_FAULT: -+ case FAN6_FAULT: -+ ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static const struct attribute_group as7512_32x_fan_group = { -+ .attrs = as7512_32x_fan_attributes, -+}; -+ -+static struct as7512_32x_fan_data *as7512_32x_fan_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7512_32x_fan_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || -+ !data->valid) { -+ int i; -+ -+ dev_dbg(&client->dev, "Starting as7512_32x_fan update\n"); -+ data->valid = 0; -+ -+ /* Update fan data -+ */ -+ for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { -+ int status = as7512_32x_fan_read_value(client, fan_reg[i]); -+ -+ if (status < 0) { -+ data->valid = 0; -+ mutex_unlock(&data->update_lock); -+ dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); -+ return data; -+ } -+ else { -+ data->reg_val[i] = status; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int as7512_32x_fan_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7512_32x_fan_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7512_32x_fan_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, &as7512_32x_fan_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: fan '%s'\n", -+ dev_name(data->hwmon_dev), client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &as7512_32x_fan_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7512_32x_fan_remove(struct i2c_client *client) -+{ -+ struct as7512_32x_fan_data *data = i2c_get_clientdata(client); -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7512_32x_fan_group); -+ -+ return 0; -+} -+ -+/* Addresses to scan */ -+static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; -+ -+static const struct i2c_device_id as7512_32x_fan_id[] = { -+ { "as7512_32x_fan", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7512_32x_fan_id); -+ -+static struct i2c_driver as7512_32x_fan_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = DRVNAME, -+ }, -+ .probe = as7512_32x_fan_probe, -+ .remove = as7512_32x_fan_remove, -+ .id_table = as7512_32x_fan_id, -+ .address_list = normal_i2c, -+}; -+ -+static int __init as7512_32x_fan_init(void) -+{ -+ extern int platform_accton_as7512_32x(void); -+ if (!platform_accton_as7512_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7512_32x_fan_driver); -+} -+ -+static void __exit as7512_32x_fan_exit(void) -+{ -+ i2c_del_driver(&as7512_32x_fan_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7512_32x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as7512_32x_fan_init); -+module_exit(as7512_32x_fan_exit); -diff --git a/drivers/hwmon/accton_as7512_32x_psu.c b/drivers/hwmon/accton_as7512_32x_psu.c -new file mode 100644 -index 0000000..5873833 ---- /dev/null -+++ b/drivers/hwmon/accton_as7512_32x_psu.c -@@ -0,0 +1,291 @@ -+/* -+ * An hwmon driver for accton as7512_32x Power Module -+ * -+ * Copyright (C) 2014 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 as7512_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int accton_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 as7512_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[9]; /* Model name, read from eeprom */ -+}; -+ -+static struct as7512_32x_psu_data *as7512_32x_psu_update_device(struct device *dev); -+ -+enum as7512_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 *as7512_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 as7512_32x_psu_data *data = as7512_32x_psu_update_device(dev); -+ u8 status = 0; -+ -+ if (attr->index == PSU_PRESENT) { -+ status = !(data->status >> ((2 - data->index) + 2) & 0x1); -+ } -+ else { /* PSU_POWER_GOOD */ -+ status = (data->status >> (2 - data->index)) & 0x1; -+ } -+ -+ return sprintf(buf, "%d\n", status); -+} -+ -+static ssize_t show_model_name(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as7512_32x_psu_data *data = as7512_32x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s\n", data->model_name); -+} -+ -+static const struct attribute_group as7512_32x_psu_group = { -+ .attrs = as7512_32x_psu_attributes, -+}; -+ -+static int as7512_32x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7512_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 as7512_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, &as7512_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) { -+ data->index = 1; -+ } -+ else if (client->addr == 0x53) { -+ 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, &as7512_32x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7512_32x_psu_remove(struct i2c_client *client) -+{ -+ struct as7512_32x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7512_32x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id as7512_32x_psu_id[] = { -+ { "as7512_32x_psu", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7512_32x_psu_id); -+ -+static struct i2c_driver as7512_32x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7512_32x_psu", -+ }, -+ .probe = as7512_32x_psu_probe, -+ .remove = as7512_32x_psu_remove, -+ .id_table = as7512_32x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7512_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 as7512_32x_psu_data *as7512_32x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7512_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 power_good = 0; -+ -+ dev_dbg(&client->dev, "Starting as7512_32x update\n"); -+ -+ /* Read psu status */ -+ status = accton_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)); -+ power_good = (data->status >> (2 - data->index)) & 0x1; -+ -+ if (power_good) { -+ status = as7512_32x_psu_read_block(client, 0x20, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7512_32x_psu_init(void) -+{ -+ extern int platform_accton_as7512_32x(void); -+ if (!platform_accton_as7512_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7512_32x_psu_driver); -+} -+ -+static void __exit as7512_32x_psu_exit(void) -+{ -+ i2c_del_driver(&as7512_32x_psu_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7512_32x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as7512_32x_psu_init); -+module_exit(as7512_32x_psu_exit); -diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c -new file mode 100644 -index 0000000..96e3490 ---- /dev/null -+++ b/drivers/hwmon/accton_i2c_cpld.c -@@ -0,0 +1,216 @@ -+/* -+ * A hwmon driver for the accton_i2c_cpld -+ * -+ * Copyright (C) 2013 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 -+ -+static LIST_HEAD(cpld_client_list); -+static struct mutex list_lock; -+ -+struct cpld_client_node { -+ struct i2c_client *client; -+ struct list_head list; -+}; -+ -+/* Addresses scanned for accton_i2c_cpld -+ */ -+static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END }; -+ -+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 int accton_i2c_cpld_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr); -+ status = -EIO; -+ goto exit; -+ } -+ -+ dev_info(&client->dev, "chip found\n"); -+ accton_i2c_cpld_add_client(client); -+ -+ return 0; -+ -+exit: -+ return status; -+} -+ -+static int accton_i2c_cpld_remove(struct i2c_client *client) -+{ -+ accton_i2c_cpld_remove_client(client); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id accton_i2c_cpld_id[] = { -+ { "accton_i2c_cpld", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id); -+ -+static struct i2c_driver accton_i2c_cpld_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "accton_i2c_cpld", -+ }, -+ .probe = accton_i2c_cpld_probe, -+ .remove = accton_i2c_cpld_remove, -+ .id_table = accton_i2c_cpld_id, -+ .address_list = normal_i2c, -+}; -+ -+int accton_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(accton_i2c_cpld_read); -+ -+int accton_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(accton_i2c_cpld_write); -+ -+static int __init accton_i2c_cpld_init(void) -+{ -+ mutex_init(&list_lock); -+ return i2c_add_driver(&accton_i2c_cpld_driver); -+} -+ -+static void __exit accton_i2c_cpld_exit(void) -+{ -+ i2c_del_driver(&accton_i2c_cpld_driver); -+} -+ -+static struct dmi_system_id as7512_dmi_table[] = { -+ { -+ .ident = "Accton AS7512", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS7512"), -+ }, -+ }, -+ { -+ .ident = "Accton AS7512", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS7512"), -+ }, -+ }, -+}; -+ -+int platform_accton_as7512_32x(void) -+{ -+ return dmi_check_system(as7512_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as7512_32x); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_i2c_cpld driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(accton_i2c_cpld_init); -+module_exit(accton_i2c_cpld_exit); -diff --git a/drivers/hwmon/ym2651y.c b/drivers/hwmon/ym2651y.c -new file mode 100644 -index 0000000..2fe455b ---- /dev/null -+++ b/drivers/hwmon/ym2651y.c -@@ -0,0 +1,631 @@ -+/* -+ * An hwmon driver for the 3Y Power YM-2651Y Power Module -+ * -+ * Copyright (C) 2014 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 MAX_FAN_DUTY_CYCLE 100 -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x58, 0x5b, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct ym2651y_data { -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+ char valid; /* !=0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 capability; /* Register value */ -+ u16 status_word; /* Register value */ -+ u8 fan_fault; /* Register value */ -+ u8 over_temp; /* Register value */ -+ u16 v_out; /* Register value */ -+ u16 i_out; /* Register value */ -+ u16 p_out; /* Register value */ -+ u16 temp; /* Register value */ -+ u16 fan_speed; /* Register value */ -+ u16 fan_duty_cycle[2]; /* Register value */ -+ u8 fan_dir[5]; /* Register value */ -+ u8 pmbus_revision; /* Register value */ -+ u8 mfr_id[10]; /* Register value */ -+ u8 mfr_model[10]; /* Register value */ -+ u8 mfr_revsion[3]; /* Register value */ -+ u16 mfr_vin_min; /* Register value */ -+ u16 mfr_vin_max; /* Register value */ -+ u16 mfr_iin_max; /* Register value */ -+ u16 mfr_iout_max; /* Register value */ -+ u16 mfr_pin_max; /* Register value */ -+ u16 mfr_pout_max; /* Register value */ -+ u16 mfr_vout_min; /* Register value */ -+ u16 mfr_vout_max; /* Register value */ -+}; -+ -+static ssize_t show_byte(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_word(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_linear(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_ascii(struct device *dev, struct device_attribute *da, -+ char *buf); -+static struct ym2651y_data *ym2651y_update_device(struct device *dev); -+static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value); -+ -+enum ym2651y_sysfs_attributes { -+ PSU_POWER_ON = 0, -+ PSU_TEMP_FAULT, -+ PSU_POWER_GOOD, -+ PSU_FAN1_FAULT, -+ PSU_FAN_DIRECTION, -+ PSU_OVER_TEMP, -+ PSU_V_OUT, -+ PSU_I_OUT, -+ PSU_P_OUT, -+ PSU_TEMP1_INPUT, -+ PSU_FAN1_SPEED, -+ PSU_FAN1_DUTY_CYCLE, -+ PSU_PMBUS_REVISION, -+ PSU_MFR_ID, -+ PSU_MFR_MODEL, -+ PSU_MFR_REVISION, -+ PSU_MFR_VIN_MIN, -+ PSU_MFR_VIN_MAX, -+ PSU_MFR_VOUT_MIN, -+ PSU_MFR_VOUT_MAX, -+ PSU_MFR_IIN_MAX, -+ PSU_MFR_IOUT_MAX, -+ PSU_MFR_PIN_MAX, -+ PSU_MFR_POUT_MAX -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); -+static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); -+static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); -+static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); -+static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); -+static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_linear, NULL, PSU_V_OUT); -+static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); -+static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); -+static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); -+static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); -+static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE); -+static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION); -+static SENSOR_DEVICE_ATTR(psu_pmbus_revision,S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION); -+static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); -+static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); -+static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); -+static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN); -+static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX); -+static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN); -+static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX); -+static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX); -+static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX); -+static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX); -+static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX); -+ -+static struct attribute *ym2651y_attributes[] = { -+ &sensor_dev_attr_psu_power_on.dev_attr.attr, -+ &sensor_dev_attr_psu_temp_fault.dev_attr.attr, -+ &sensor_dev_attr_psu_power_good.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, -+ &sensor_dev_attr_psu_over_temp.dev_attr.attr, -+ &sensor_dev_attr_psu_v_out.dev_attr.attr, -+ &sensor_dev_attr_psu_i_out.dev_attr.attr, -+ &sensor_dev_attr_psu_p_out.dev_attr.attr, -+ &sensor_dev_attr_psu_temp1_input.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, -+ &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, -+ &sensor_dev_attr_psu_fan_dir.dev_attr.attr, -+ &sensor_dev_attr_psu_pmbus_revision.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_id.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_model.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr, -+ &sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr, -+ NULL -+}; -+ -+static ssize_t show_byte(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) : -+ sprintf(buf, "0\n"); -+} -+ -+static ssize_t show_word(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ u16 status = 0; -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ switch (attr->index) { -+ case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ -+ status = (data->status_word & 0x40) ? 0 : 1; -+ break; -+ case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ -+ status = (data->status_word & 0x4) >> 2; -+ break; -+ case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ -+ status = (data->status_word & 0x800) ? 0 : 1; -+ break; -+ } -+ -+ return sprintf(buf, "%d\n", status); -+} -+ -+static int two_complement_to_int(u16 data, u8 valid_bit, int mask) -+{ -+ u16 valid_data = data & mask; -+ bool is_negative = valid_data >> (valid_bit - 1); -+ -+ return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; -+} -+ -+static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct i2c_client *client = to_i2c_client(dev); -+ struct ym2651y_data *data = i2c_get_clientdata(client); -+ int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1; -+ long speed; -+ int error; -+ -+ error = kstrtol(buf, 10, &speed); -+ if (error) -+ return error; -+ -+ if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ data->fan_duty_cycle[nr] = speed; -+ ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t show_linear(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ -+ u16 value = 0; -+ int exponent, mantissa; -+ int multiplier = 1000; -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ switch (attr->index) { -+ case PSU_V_OUT: -+ value = data->v_out; -+ break; -+ case PSU_I_OUT: -+ value = data->i_out; -+ break; -+ case PSU_P_OUT: -+ value = data->p_out; -+ break; -+ case PSU_TEMP1_INPUT: -+ value = data->temp; -+ break; -+ case PSU_FAN1_SPEED: -+ value = data->fan_speed; -+ multiplier = 1; -+ break; -+ case PSU_FAN1_DUTY_CYCLE: -+ value = data->fan_duty_cycle[0]; -+ multiplier = 1; -+ break; -+ case PSU_MFR_VIN_MIN: -+ value = data->mfr_vin_min; -+ break; -+ case PSU_MFR_VIN_MAX: -+ value = data->mfr_vin_max; -+ break; -+ case PSU_MFR_VOUT_MIN: -+ value = data->mfr_vout_min; -+ break; -+ case PSU_MFR_VOUT_MAX: -+ value = data->mfr_vout_max; -+ break; -+ case PSU_MFR_PIN_MAX: -+ value = data->mfr_pin_max; -+ break; -+ case PSU_MFR_POUT_MAX: -+ value = data->mfr_pout_max; -+ break; -+ case PSU_MFR_IOUT_MAX: -+ value = data->mfr_iout_max; -+ break; -+ case PSU_MFR_IIN_MAX: -+ value = data->mfr_iin_max; -+ break; -+ } -+ -+ exponent = two_complement_to_int(value >> 11, 5, 0x1f); -+ mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); -+ -+ return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : -+ sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); -+} -+ -+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ u8 shift; -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; -+ -+ return sprintf(buf, "%d\n", data->fan_fault >> shift); -+} -+ -+static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ return sprintf(buf, "%d\n", data->over_temp >> 7); -+} -+ -+static ssize_t show_ascii(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct ym2651y_data *data = ym2651y_update_device(dev); -+ u8 *ptr = NULL; -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ switch (attr->index) { -+ case PSU_FAN_DIRECTION: /* psu_fan_dir */ -+ ptr = data->fan_dir + 1; /* Skip the first byte since it is the length of string. */ -+ break; -+ case PSU_MFR_ID: /* psu_mfr_id */ -+ ptr = data->mfr_id + 1; /* The first byte is the count byte of string. */; -+ break; -+ case PSU_MFR_MODEL: /* psu_mfr_model */ -+ ptr = data->mfr_model + 1; /* The first byte is the count byte of string. */ -+ break; -+ case PSU_MFR_REVISION: /* psu_mfr_revision */ -+ ptr = data->mfr_revsion + 1; /* The first byte is the count byte of string. */ -+ break; -+ default: -+ return 0; -+ } -+ -+ return sprintf(buf, "%s\n", ptr); -+} -+ -+static const struct attribute_group ym2651y_group = { -+ .attrs = ym2651y_attributes, -+}; -+ -+static int ym2651y_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct ym2651y_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_BYTE_DATA | -+ I2C_FUNC_SMBUS_WORD_DATA | -+ I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL); -+ if (!data) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ i2c_set_clientdata(client, data); -+ mutex_init(&data->update_lock); -+ -+ dev_info(&client->dev, "chip found\n"); -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &ym2651y_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: psu '%s'\n", -+ dev_name(data->hwmon_dev), client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &ym2651y_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int ym2651y_remove(struct i2c_client *client) -+{ -+ struct ym2651y_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &ym2651y_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static const struct i2c_device_id ym2651y_id[] = { -+ { "ym2651", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, ym2651y_id); -+ -+static struct i2c_driver ym2651y_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "ym2651", -+ }, -+ .probe = ym2651y_probe, -+ .remove = ym2651y_remove, -+ .id_table = ym2651y_id, -+ .address_list = normal_i2c, -+}; -+ -+static int ym2651y_read_byte(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_byte_data(client, reg); -+} -+ -+static int ym2651y_read_word(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_word_data(client, reg); -+} -+ -+static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value) -+{ -+ return i2c_smbus_write_word_data(client, reg, value); -+} -+ -+static int ym2651y_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+struct reg_data_byte { -+ u8 reg; -+ u8 *value; -+}; -+ -+struct reg_data_word { -+ u8 reg; -+ u16 *value; -+}; -+ -+static struct ym2651y_data *ym2651y_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct ym2651y_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid) { -+ int i, status; -+ u8 command; -+ struct reg_data_byte regs_byte[] = { {0x19, &data->capability}, -+ {0x7d, &data->over_temp}, -+ {0x81, &data->fan_fault}, -+ {0x98, &data->pmbus_revision}}; -+ struct reg_data_word regs_word[] = { {0x79, &data->status_word}, -+ {0x8b, &data->v_out}, -+ {0x8c, &data->i_out}, -+ {0x96, &data->p_out}, -+ {0x8d, &data->temp}, -+ {0x3b, &(data->fan_duty_cycle[0])}, -+ {0x3c, &(data->fan_duty_cycle[1])}, -+ {0x90, &data->fan_speed}, -+ {0xa0, &data->mfr_vin_min}, -+ {0xa1, &data->mfr_vin_max}, -+ {0xa2, &data->mfr_iin_max}, -+ {0xa3, &data->mfr_pin_max}, -+ {0xa4, &data->mfr_vout_min}, -+ {0xa5, &data->mfr_vout_max}, -+ {0xa6, &data->mfr_iout_max}, -+ {0xa7, &data->mfr_pout_max}}; -+ -+ dev_dbg(&client->dev, "Starting ym2651 update\n"); -+ data->valid = 0; -+ -+ /* Read byte data */ -+ for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { -+ status = ym2651y_read_byte(client, regs_byte[i].reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", -+ regs_byte[i].reg, status); -+ goto exit; -+ } -+ else { -+ *(regs_byte[i].value) = status; -+ } -+ } -+ -+ /* Read word data */ -+ for (i = 0; i < ARRAY_SIZE(regs_word); i++) { -+ status = ym2651y_read_word(client, regs_word[i].reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", -+ regs_word[i].reg, status); -+ goto exit; -+ } -+ else { -+ *(regs_word[i].value) = status; -+ } -+ } -+ -+ /* Read fan_direction */ -+ command = 0xC3; -+ status = ym2651y_read_block(client, command, data->fan_dir, -+ ARRAY_SIZE(data->fan_dir)-1); -+ data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0'; -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", command, status); -+ goto exit; -+ } -+ -+ /* Read mfr_id */ -+ command = 0x99; -+ status = ym2651y_read_block(client, command, data->mfr_id, -+ ARRAY_SIZE(data->mfr_id)-1); -+ data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0'; -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", command, status); -+ goto exit; -+ } -+ -+ /* Read mfr_model */ -+ command = 0x9a; -+ status = ym2651y_read_block(client, command, data->mfr_model, -+ ARRAY_SIZE(data->mfr_model)-1); -+ data->mfr_model[ARRAY_SIZE(data->mfr_model)-1] = '\0'; -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", command, status); -+ goto exit; -+ } -+ -+ /* Read mfr_revsion */ -+ command = 0x9b; -+ status = ym2651y_read_block(client, command, data->mfr_revsion, -+ ARRAY_SIZE(data->mfr_revsion)-1); -+ data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0'; -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "reg %d, err %d\n", command, status); -+ goto exit; -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init ym2651y_init(void) -+{ -+ return i2c_add_driver(&ym2651y_driver); -+} -+ -+static void __exit ym2651y_exit(void) -+{ -+ i2c_del_driver(&ym2651y_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("3Y Power YM-2651Y driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(ym2651y_init); -+module_exit(ym2651y_exit); -+ -diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig -index a5ccde1..fb48975 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -54,6 +54,13 @@ config LEDS_ACCTON_AS6712_32x - This option enables support for the LEDs on the Accton as6712 32x. - Say Y to enable LEDs on the Accton as6712 32x. - -+config LEDS_ACCTON_AS7512_32x -+ tristate "LED support for the Accton as7512 32x" -+ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD -+ help -+ This option enables support for the LEDs on the Accton as7512 32x. -+ Say Y to enable LEDs on the Accton as7512 32x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index d952f0f..dff0462 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -45,6 +45,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o - obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o - obj-$(CONFIG_LEDS_ACCTON_AS5712_54x) += leds-accton_as5712_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6712_32x) += leds-accton_as6712_32x.o -+obj-$(CONFIG_LEDS_ACCTON_AS7512_32x) += leds-accton_as7512_32x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as7512_32x.c b/drivers/leds/leds-accton_as7512_32x.c -new file mode 100644 -index 0000000..3dc5def ---- /dev/null -+++ b/drivers/leds/leds-accton_as7512_32x.c -@@ -0,0 +1,503 @@ -+/* -+ * A LED driver for the accton_as7512_32x_led -+ * -+ * Copyright (C) 2014 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 -+#include -+ -+extern int accton_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+#define DRVNAME "as7512_32x_led" -+#define NUM_OF_LED_REG 5 -+ -+struct accton_as7512_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[NUM_OF_LED_REG]; /* 5 LED registers */ -+}; -+ -+static struct accton_as7512_32x_led_data *ledctl = NULL; -+ -+/* LED related data -+ */ -+ -+#define LED_CNTRLER_I2C_ADDRESS (0x60) -+/* -+#define LED_TYPE_DIAG_REG_MASK (0x3) -+#define LED_MODE_DIAG_GREEN_VALUE (0x02) -+#define LED_MODE_DIAG_RED_VALUE (0x01) -+#define LED_MODE_DIAG_AMBER_VALUE (0x00) -+#define LED_MODE_DIAG_OFF_VALUE (0x03) -+#define LED_TYPE_DIAG_REG_MASK 0xFF -+#define LED_MODE_DIAG_GREEN_VALUE 0xFF -+#define LED_MODE_DIAG_RED_VALUE 0xFF -+#define LED_MODE_DIAG_OFF_VALUE 0 -+ -+#define LED_TYPE_LOC_REG_MASK 0xFF -+#define LED_MODE_LOC_ON_VALUE 0x0 -+#define LED_MODE_LOC_OFF_VALUE 0xFF -+*/ -+/* -+#define LED_TYPE_FAN_REG_MASK 0xFF -+#define LED_MODE_FAN_GREEN_VALUE 0xFF -+#define LED_MODE_FAN_RED_VALUE 0xFF -+#define LED_MODE_FAN_OFF_VALUE 0 -+*/ -+#define LED_BRIGHTNESS_ON_VALUE 0x0 -+#define LED_BRIGHTNESS_OFF_VALUE 0xFF -+ -+static const u8 led_reg[NUM_OF_LED_REG] = -+{ -+ 0x41, /* Diag LED-Green. */ -+ 0x42, /* Diag LED-Red. */ -+ 0x43, /* FAN LED-Green. */ -+ 0x44, /* FAN LED-Red. */ -+ 0x45, /* LOC LED. */ -+ //0x1C, /* FAN 1-4 LED */ -+ //0x1D /* FAN 5-6 LED */ -+}; -+ -+enum led_type { -+ LED_TYPE_DIAG_GREEN, -+ LED_TYPE_DIAG_RED, -+ LED_TYPE_LOC, -+ LED_TYPE_FAN_GREEN, -+ LED_TYPE_FAN_RED -+}; -+ -+struct led_reg { -+ u32 types; -+ u8 reg_addr; -+}; -+ -+enum led_light_mode { -+ LED_MODE_OFF = 0, -+ LED_MODE_GREEN, -+ LED_MODE_AMBER, -+ LED_MODE_RED, -+ LED_MODE_BLUE, -+ LED_MODE_GREEN_BLINK, -+ LED_MODE_AMBER_BLINK, -+ LED_MODE_RED_BLINK, -+ LED_MODE_BLUE_BLINK, -+ LED_MODE_AUTO, -+ LED_MODE_UNKNOWN -+}; -+ -+#if 0 -+struct led_type_mode { -+ enum led_type type; -+ enum led_light_mode mode; -+ int reg_bit_mask; -+ int mode_value; -+}; -+ -+struct led_type_mode led_type_mode_data[] = { -+{LED_TYPE_LOC, LED_MODE_OFF, LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_OFF_VALUE}, -+{LED_TYPE_LOC, LED_MODE_BLUE, LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_ON_VALUE}, -+{LED_TYPE_DIAG_GREEN, LED_MODE_OFF, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_OFF_VALUE}, -+{LED_TYPE_DIAG_GREEN, LED_MODE_GREEN, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_GREEN_VALUE}, -+{LED_TYPE_DIAG_RED, LED_MODE_OFF, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_OFF_VALUE}, -+{LED_TYPE_DIAG_RED, LED_MODE_RED, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_RED_VALUE}, -+{LED_TYPE_FAN_GREEN, LED_MODE_OFF, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_OFF_VALUE}, -+{LED_TYPE_FAN_GREEN, LED_MODE_GREEN, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_GREEN_VALUE}, -+{LED_TYPE_FAN_RED, LED_MODE_OFF, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_OFF_VALUE}, -+{LED_TYPE_FAN_RED, LED_MODE_RED, LED_TYPE_FAN_REG_MASK, LED_MODE_FAN_RED_VALUE}, -+}; -+#endif -+ -+/* -+static int accton_getLedReg(enum led_type type, u8 *reg) -+{ -+ int i; -+ for (i = 0; i < ARRAY_SIZE(led_reg_map); i++) { -+ if(led_reg_map[i].types & (type<<1)){ -+ *reg = led_reg_map[i].reg_addr; -+ return 0; -+ } -+ } -+ return 1; -+} -+*/ -+ -+#if 0 -+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 ((led_type_mode_data[i].reg_bit_mask & reg_val) == -+ led_type_mode_data[i].mode_value) -+ { -+ return led_type_mode_data[i].mode; -+ } -+ } -+ -+ return LED_MODE_UNKNOWN; -+} -+ -+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; -+ -+ reg_val = led_type_mode_data[i].mode_value | -+ (reg_val & (~led_type_mode_data[i].reg_bit_mask)); -+ break; -+ } -+ -+ return reg_val; -+} -+#endif -+ -+static int accton_as7512_32x_led_read_value(u8 reg) -+{ -+ return accton_i2c_cpld_read(LED_CNTRLER_I2C_ADDRESS, reg); -+} -+ -+static int accton_as7512_32x_led_write_value(u8 reg, u8 value) -+{ -+ return accton_i2c_cpld_write(LED_CNTRLER_I2C_ADDRESS, reg, value); -+} -+ -+static void accton_as7512_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_as7512_32x_led update\n"); -+ ledctl->valid = 0; -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as7512_32x_led_read_value(led_reg[i]); -+ -+ if (status < 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); -+} -+ -+#if 0 -+static void accton_as7512_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_as7512_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_as7512_32x_led_write_value(reg, reg_val); -+ -+ /* to prevent the slow-update issue */ -+ ledctl->valid = 0; -+ -+exit: -+ mutex_unlock(&ledctl->update_lock); -+} -+#endif -+ -+static void accton_as7512_32x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ if (LED_MODE_OFF == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[0], LED_BRIGHTNESS_OFF_VALUE); -+ accton_as7512_32x_led_write_value(led_reg[1], LED_BRIGHTNESS_OFF_VALUE); -+ return; -+ } -+ -+ if (LED_MODE_GREEN == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[0], LED_BRIGHTNESS_ON_VALUE); -+ accton_as7512_32x_led_write_value(led_reg[1], LED_BRIGHTNESS_OFF_VALUE); -+ return; -+ } -+ -+ if (LED_MODE_RED == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[0], LED_BRIGHTNESS_OFF_VALUE); -+ accton_as7512_32x_led_write_value(led_reg[1], LED_BRIGHTNESS_ON_VALUE); -+ return; -+ } -+ -+ if (LED_MODE_AMBER == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[0], LED_BRIGHTNESS_ON_VALUE); -+ accton_as7512_32x_led_write_value(led_reg[1], LED_BRIGHTNESS_ON_VALUE); -+ return; -+ } -+} -+ -+static enum led_brightness accton_as7512_32x_led_diag_get(struct led_classdev *cdev) -+{ -+ u8 is_green_reg_on, is_red_reg_on; -+ -+ accton_as7512_32x_led_update(); -+ -+ is_green_reg_on = (ledctl->reg_val[0] == LED_BRIGHTNESS_OFF_VALUE) ? 0 : 1; -+ is_red_reg_on = (ledctl->reg_val[1] == LED_BRIGHTNESS_OFF_VALUE) ? 0 : 1; -+ -+ if (is_green_reg_on && is_red_reg_on) { -+ return LED_MODE_AMBER; -+ } -+ -+ if (is_green_reg_on) { -+ return LED_MODE_GREEN; -+ } -+ -+ if (is_red_reg_on) { -+ return LED_MODE_RED; -+ } -+ -+ return LED_MODE_OFF; -+} -+ -+static void accton_as7512_32x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ if (LED_MODE_OFF == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[4], LED_BRIGHTNESS_OFF_VALUE); -+ return; -+ } -+ -+ if (LED_MODE_BLUE == (enum led_light_mode)led_light_mode) { -+ accton_as7512_32x_led_write_value(led_reg[4], LED_BRIGHTNESS_ON_VALUE); -+ return; -+ } -+} -+ -+static enum led_brightness accton_as7512_32x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as7512_32x_led_update(); -+ -+ if (ledctl->reg_val[0] == LED_BRIGHTNESS_OFF_VALUE) { -+ return LED_MODE_OFF; -+ } -+ -+ return LED_MODE_BLUE; -+} -+ -+static enum led_brightness accton_as7512_32x_led_auto_get(struct led_classdev *cdev) -+{ -+ return LED_MODE_AUTO; -+} -+ -+static struct led_classdev accton_as7512_32x_leds[] = { -+ [0] = { -+ .name = "accton_as7512_32x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7512_32x_led_diag_set, -+ .brightness_get = accton_as7512_32x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_RED, -+ }, -+ [1] = { -+ .name = "accton_as7512_32x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7512_32x_led_loc_set, -+ .brightness_get = accton_as7512_32x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_BLUE, -+ }, -+ [2] = { -+ .name = "accton_as7512_32x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = NULL, -+ .brightness_get = accton_as7512_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [3] = { -+ .name = "accton_as7512_32x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = NULL, -+ .brightness_get = accton_as7512_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [4] = { -+ .name = "accton_as7512_32x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = NULL, -+ .brightness_get = accton_as7512_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as7512_32x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7512_32x_leds); i++) { -+ led_classdev_suspend(&accton_as7512_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7512_32x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7512_32x_leds); i++) { -+ led_classdev_resume(&accton_as7512_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7512_32x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7512_32x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as7512_32x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as7512_32x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as7512_32x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as7512_32x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7512_32x_leds); i++) { -+ led_classdev_unregister(&accton_as7512_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as7512_32x_led_driver = { -+ .probe = accton_as7512_32x_led_probe, -+ .remove = accton_as7512_32x_led_remove, -+ .suspend = accton_as7512_32x_led_suspend, -+ .resume = accton_as7512_32x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as7512_32x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as7512_32x(void); -+ if (!platform_accton_as7512_32x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as7512_32x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as7512_32x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as7512_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_as7512_32x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as7512_32x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as7512_32x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as7512_32x_led_init); -+module_exit(accton_as7512_32x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as7512_32x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 6e025e9..8e959c7 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -91,6 +91,15 @@ config EEPROM_ACCTON_AS6712_32x_SFP - This driver can also be built as a module. If so, the module will - be called accton_as6712_32x_sfp. - -+config EEPROM_ACCTON_AS7512_32x_SFP -+ tristate "Accton as7512 32x sfp" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7512 32x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7512_32x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 9001de9..0386999 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -8,4 +8,5 @@ obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o - obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o - obj-$(CONFIG_EEPROM_ACCTON_AS5712_54x_SFP) += accton_as5712_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6712_32x_SFP) += accton_as6712_32x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS7512_32x_SFP) += accton_as7512_32x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as7512_32x_sfp.c b/drivers/misc/eeprom/accton_as7512_32x_sfp.c -new file mode 100644 -index 0000000..1468961 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as7512_32x_sfp.c -@@ -0,0 +1,356 @@ -+/* -+ * An hwmon driver for accton as7512_32x sfp -+ * -+ * Copyright (C) 2014 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) (1UL << (i)) -+ -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as7512_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 */ -+ u32 is_present; /* present status */ -+}; -+ -+static struct as7512_32x_sfp_data *as7512_32x_sfp_update_device(struct device *dev); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+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); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as7512_32x_sfp_sysfs_attributes { -+ SFP_PORT_NUMBER, -+ SFP_IS_PRESENT, -+ SFP_IS_PRESENT_ALL, -+ SFP_EEPROM -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+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 struct attribute *as7512_32x_sfp_attributes[] = { -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_eeprom.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 as7512_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], accton_i2c_cpld_read(0x60, 0x30), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(0x60, 0x31), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(0x60, 0x32), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(0x60, 0x33), 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 */ -+ struct as7512_32x_sfp_data *data = as7512_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return -EIO; -+ } -+ -+ return sprintf(buf, "%d\n", data->is_present); -+ } -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as7512_32x_sfp_data *data = as7512_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if (!data->is_present) { -+ return 0; -+ } -+ -+ memcpy(buf, data->eeprom, sizeof(data->eeprom)); -+ -+ return sizeof(data->eeprom); -+} -+ -+static const struct attribute_group as7512_32x_sfp_group = { -+ .attrs = as7512_32x_sfp_attributes, -+}; -+ -+static int as7512_32x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7512_32x_sfp_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7512_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, &as7512_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, &as7512_32x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7512_32x_sfp_remove(struct i2c_client *client) -+{ -+ struct as7512_32x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7512_32x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as7512_32x_sfp1, as7512_32x_sfp2, as7512_32x_sfp3, as7512_32x_sfp4, -+as7512_32x_sfp5, as7512_32x_sfp6, as7512_32x_sfp7, as7512_32x_sfp8, -+as7512_32x_sfp9, as7512_32x_sfp10,as7512_32x_sfp11,as7512_32x_sfp12, -+as7512_32x_sfp13,as7512_32x_sfp14,as7512_32x_sfp15,as7512_32x_sfp16, -+as7512_32x_sfp17,as7512_32x_sfp18,as7512_32x_sfp19,as7512_32x_sfp20, -+as7512_32x_sfp21,as7512_32x_sfp22,as7512_32x_sfp23,as7512_32x_sfp24, -+as7512_32x_sfp25,as7512_32x_sfp26,as7512_32x_sfp27,as7512_32x_sfp28, -+as7512_32x_sfp29,as7512_32x_sfp30,as7512_32x_sfp31,as7512_32x_sfp32 -+}; -+ -+static const struct i2c_device_id as7512_32x_sfp_id[] = { -+{ "as7512_32x_sfp1", as7512_32x_sfp1 }, { "as7512_32x_sfp2", as7512_32x_sfp2 }, -+{ "as7512_32x_sfp3", as7512_32x_sfp3 }, { "as7512_32x_sfp4", as7512_32x_sfp4 }, -+{ "as7512_32x_sfp5", as7512_32x_sfp5 }, { "as7512_32x_sfp6", as7512_32x_sfp6 }, -+{ "as7512_32x_sfp7", as7512_32x_sfp7 }, { "as7512_32x_sfp8", as7512_32x_sfp8 }, -+{ "as7512_32x_sfp9", as7512_32x_sfp9 }, { "as7512_32x_sfp10", as7512_32x_sfp10 }, -+{ "as7512_32x_sfp11", as7512_32x_sfp11 }, { "as7512_32x_sfp12", as7512_32x_sfp12 }, -+{ "as7512_32x_sfp13", as7512_32x_sfp13 }, { "as7512_32x_sfp14", as7512_32x_sfp14 }, -+{ "as7512_32x_sfp15", as7512_32x_sfp15 }, { "as7512_32x_sfp16", as7512_32x_sfp16 }, -+{ "as7512_32x_sfp17", as7512_32x_sfp17 }, { "as7512_32x_sfp18", as7512_32x_sfp18 }, -+{ "as7512_32x_sfp19", as7512_32x_sfp19 }, { "as7512_32x_sfp20", as7512_32x_sfp20 }, -+{ "as7512_32x_sfp21", as7512_32x_sfp21 }, { "as7512_32x_sfp22", as7512_32x_sfp22 }, -+{ "as7512_32x_sfp23", as7512_32x_sfp23 }, { "as7512_32x_sfp24", as7512_32x_sfp24 }, -+{ "as7512_32x_sfp25", as7512_32x_sfp25 }, { "as7512_32x_sfp26", as7512_32x_sfp26 }, -+{ "as7512_32x_sfp27", as7512_32x_sfp27 }, { "as7512_32x_sfp28", as7512_32x_sfp28 }, -+{ "as7512_32x_sfp29", as7512_32x_sfp29 }, { "as7512_32x_sfp30", as7512_32x_sfp30 }, -+{ "as7512_32x_sfp31", as7512_32x_sfp31 }, { "as7512_32x_sfp32", as7512_32x_sfp32 }, -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as7512_32x_sfp_id); -+ -+static struct i2c_driver as7512_32x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7512_32x_sfp", -+ }, -+ .probe = as7512_32x_sfp_probe, -+ .remove = as7512_32x_sfp_remove, -+ .id_table = as7512_32x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7512_32x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as7512_32x_sfp_data *as7512_32x_sfp_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7512_32x_sfp_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 = -1; -+ int i = 0; -+ u8 cpld_reg = 0x30 + (data->port/8); -+ -+ data->valid = 0; -+ -+ /* Read present status of the specified port number */ -+ data->is_present = 0; -+ status = accton_i2c_cpld_read(0x60, cpld_reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x60) reg(0x%x) err %d\n", cpld_reg, status); -+ goto exit; -+ } -+ -+ data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1; -+ -+ /* Read eeprom data based on port number */ -+ memset(data->eeprom, 0, sizeof(data->eeprom)); -+ -+ /* Check if the port is present */ -+ if (data->is_present) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { -+ status = as7512_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, -+ data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), -+ I2C_SMBUS_BLOCK_MAX); -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); -+ goto exit; -+ } -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7512_32x_sfp_init(void) -+{ -+ extern int platform_accton_as7512_32x(void); -+ if (!platform_accton_as7512_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7512_32x_sfp_driver); -+} -+ -+static void __exit as7512_32x_sfp_exit(void) -+{ -+ i2c_del_driver(&as7512_32x_sfp_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as7512_32x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as7512_32x_sfp_init); -+module_exit(as7512_32x_sfp_exit); diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7712_32x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7712_32x-device-drivers.patch deleted file mode 100644 index 41ff38bc..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7712_32x-device-drivers.patch +++ /dev/null @@ -1,1830 +0,0 @@ -Device driver patches for accton as7712 (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index c410426..2d4a7fb 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1502,6 +1502,24 @@ config SENSORS_YM2651Y - This driver can also be built as a module. If so, the module will - be called ym2651y. - -+config SENSORS_ACCTON_AS7712_32x_FAN -+ tristate "Accton as7712 32x fan" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7712 32x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7712_32x_fan. -+ -+config SENSORS_ACCTON_AS7712_32x_PSU -+ tristate "Accton as7712 32x psu" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7712 32x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7712_32x_psu. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index f8ee399..ea97f4a 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -27,6 +27,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_FAN) += accton_as6712_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS6712_32x_PSU) += accton_as6712_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS7512_32x_FAN) += accton_as7512_32x_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS7512_32x_PSU) += accton_as7512_32x_psu.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7712_32x_FAN) += accton_as7712_32x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7712_32x_PSU) += accton_as7712_32x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_I2C_CPLD) += accton_i2c_cpld.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o -diff --git a/drivers/hwmon/accton_as7712_32x_fan.c b/drivers/hwmon/accton_as7712_32x_fan.c -new file mode 100644 -index 0000000..74c577d ---- /dev/null -+++ b/drivers/hwmon/accton_as7712_32x_fan.c -@@ -0,0 +1,491 @@ -+/* -+ * A hwmon driver for the Accton as7712 32x fan -+ * -+ * Copyright (C) 2014 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 -+ -+#define DRVNAME "as7712_32x_fan" -+ -+static struct as7712_32x_fan_data *as7712_32x_fan_update_device(struct device *dev); -+static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+/* fan related data, the index should match sysfs_fan_attributes -+ */ -+static const u8 fan_reg[] = { -+ 0x0F, /* fan 1-6 present status */ -+ 0x10, /* fan 1-6 direction(0:B2F 1:F2B) */ -+ 0x11, /* fan PWM(for all fan) */ -+ 0x12, /* front fan 1 speed(rpm) */ -+ 0x13, /* front fan 2 speed(rpm) */ -+ 0x14, /* front fan 3 speed(rpm) */ -+ 0x15, /* front fan 4 speed(rpm) */ -+ 0x16, /* front fan 5 speed(rpm) */ -+ 0x17, /* front fan 6 speed(rpm) */ -+ 0x22, /* rear fan 1 speed(rpm) */ -+ 0x23, /* rear fan 2 speed(rpm) */ -+ 0x24, /* rear fan 3 speed(rpm) */ -+ 0x25, /* rear fan 4 speed(rpm) */ -+ 0x26, /* rear fan 5 speed(rpm) */ -+ 0x27, /* rear fan 6 speed(rpm) */ -+}; -+ -+/* Each client has this additional data */ -+struct as7712_32x_fan_data { -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+ char valid; /* != 0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ -+}; -+ -+enum fan_id { -+ FAN1_ID, -+ FAN2_ID, -+ FAN3_ID, -+ FAN4_ID, -+ FAN5_ID, -+ FAN6_ID -+}; -+ -+enum sysfs_fan_attributes { -+ FAN_PRESENT_REG, -+ FAN_DIRECTION_REG, -+ FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ -+ FAN1_FRONT_SPEED_RPM, -+ FAN2_FRONT_SPEED_RPM, -+ FAN3_FRONT_SPEED_RPM, -+ FAN4_FRONT_SPEED_RPM, -+ FAN5_FRONT_SPEED_RPM, -+ FAN6_FRONT_SPEED_RPM, -+ FAN1_REAR_SPEED_RPM, -+ FAN2_REAR_SPEED_RPM, -+ FAN3_REAR_SPEED_RPM, -+ FAN4_REAR_SPEED_RPM, -+ FAN5_REAR_SPEED_RPM, -+ FAN6_REAR_SPEED_RPM, -+ FAN1_DIRECTION, -+ FAN2_DIRECTION, -+ FAN3_DIRECTION, -+ FAN4_DIRECTION, -+ FAN5_DIRECTION, -+ FAN6_DIRECTION, -+ FAN1_PRESENT, -+ FAN2_PRESENT, -+ FAN3_PRESENT, -+ FAN4_PRESENT, -+ FAN5_PRESENT, -+ FAN6_PRESENT, -+ FAN1_FAULT, -+ FAN2_FAULT, -+ FAN3_FAULT, -+ FAN4_FAULT, -+ FAN5_FAULT, -+ FAN6_FAULT -+}; -+ -+/* Define attributes -+ */ -+#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) -+#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr -+ -+#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) -+#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr -+ -+#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE) -+#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan##index##_duty_cycle_percentage.dev_attr.attr -+ -+#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) -+#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr -+ -+#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ -+ static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) -+#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ -+ &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr -+ -+/* 6 fan fault attributes in this platform */ -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6); -+/* 6 fan speed(rpm) attributes in this platform */ -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6); -+/* 6 fan present attributes in this platform */ -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); -+/* 6 fan direction attribute in this platform */ -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(6); -+/* 1 fan duty cycle attribute in this platform */ -+DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(); -+ -+static struct attribute *as7712_32x_fan_attributes[] = { -+ /* fan related attributes */ -+ DECLARE_FAN_FAULT_ATTR(1), -+ DECLARE_FAN_FAULT_ATTR(2), -+ DECLARE_FAN_FAULT_ATTR(3), -+ DECLARE_FAN_FAULT_ATTR(4), -+ DECLARE_FAN_FAULT_ATTR(5), -+ DECLARE_FAN_FAULT_ATTR(6), -+ DECLARE_FAN_SPEED_RPM_ATTR(1), -+ DECLARE_FAN_SPEED_RPM_ATTR(2), -+ DECLARE_FAN_SPEED_RPM_ATTR(3), -+ DECLARE_FAN_SPEED_RPM_ATTR(4), -+ DECLARE_FAN_SPEED_RPM_ATTR(5), -+ DECLARE_FAN_SPEED_RPM_ATTR(6), -+ DECLARE_FAN_PRESENT_ATTR(1), -+ DECLARE_FAN_PRESENT_ATTR(2), -+ DECLARE_FAN_PRESENT_ATTR(3), -+ DECLARE_FAN_PRESENT_ATTR(4), -+ DECLARE_FAN_PRESENT_ATTR(5), -+ DECLARE_FAN_PRESENT_ATTR(6), -+ DECLARE_FAN_DIRECTION_ATTR(1), -+ DECLARE_FAN_DIRECTION_ATTR(2), -+ DECLARE_FAN_DIRECTION_ATTR(3), -+ DECLARE_FAN_DIRECTION_ATTR(4), -+ DECLARE_FAN_DIRECTION_ATTR(5), -+ DECLARE_FAN_DIRECTION_ATTR(6), -+ DECLARE_FAN_DUTY_CYCLE_ATTR(), -+ NULL -+}; -+ -+#define FAN_DUTY_CYCLE_REG_MASK 0xF -+#define FAN_MAX_DUTY_CYCLE 100 -+#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 -+ -+static int as7712_32x_fan_read_value(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_byte_data(client, reg); -+} -+ -+static int as7712_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) -+{ -+ return i2c_smbus_write_byte_data(client, reg, value); -+} -+ -+/* fan utility functions -+ */ -+static u32 reg_val_to_duty_cycle(u8 reg_val) -+{ -+ reg_val &= FAN_DUTY_CYCLE_REG_MASK; -+ return ((u32)(reg_val+1) * 625 + 75)/ 100; -+} -+ -+static u8 duty_cycle_to_reg_val(u8 duty_cycle) -+{ -+ return ((u32)duty_cycle * 100 / 625) - 1; -+} -+ -+static u32 reg_val_to_speed_rpm(u8 reg_val) -+{ -+ return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; -+} -+ -+static u8 reg_val_to_direction(u8 reg_val, enum fan_id id) -+{ -+ u8 mask = (1 << id); -+ -+ reg_val &= mask; -+ -+ return reg_val ? 1 : 0; -+} -+static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) -+{ -+ u8 mask = (1 << id); -+ -+ reg_val &= mask; -+ -+ return reg_val ? 0 : 1; -+} -+ -+static u8 is_fan_fault(struct as7712_32x_fan_data *data, enum fan_id id) -+{ -+ u8 ret = 1; -+ int front_fan_index = FAN1_FRONT_SPEED_RPM + id; -+ int rear_fan_index = FAN1_REAR_SPEED_RPM + id; -+ -+ /* Check if the speed of front or rear fan is ZERO, -+ */ -+ if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && -+ reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { -+ ret = 0; -+ } -+ -+ return ret; -+} -+ -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ int error, value; -+ struct i2c_client *client = to_i2c_client(dev); -+ -+ error = kstrtoint(buf, 10, &value); -+ if (error) -+ return error; -+ -+ if (value < 0 || value > FAN_MAX_DUTY_CYCLE) -+ return -EINVAL; -+ -+ as7712_32x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ -+ as7712_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); -+ return count; -+} -+ -+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); -+ struct as7712_32x_fan_data *data = as7712_32x_fan_update_device(dev); -+ ssize_t ret = 0; -+ -+ if (data->valid) { -+ switch (attr->index) { -+ case FAN_DUTY_CYCLE_PERCENTAGE: -+ { -+ u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); -+ ret = sprintf(buf, "%u\n", duty_cycle); -+ break; -+ } -+ case FAN1_FRONT_SPEED_RPM: -+ case FAN2_FRONT_SPEED_RPM: -+ case FAN3_FRONT_SPEED_RPM: -+ case FAN4_FRONT_SPEED_RPM: -+ case FAN5_FRONT_SPEED_RPM: -+ case FAN6_FRONT_SPEED_RPM: -+ case FAN1_REAR_SPEED_RPM: -+ case FAN2_REAR_SPEED_RPM: -+ case FAN3_REAR_SPEED_RPM: -+ case FAN4_REAR_SPEED_RPM: -+ case FAN5_REAR_SPEED_RPM: -+ case FAN6_REAR_SPEED_RPM: -+ ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); -+ break; -+ case FAN1_PRESENT: -+ case FAN2_PRESENT: -+ case FAN3_PRESENT: -+ case FAN4_PRESENT: -+ case FAN5_PRESENT: -+ case FAN6_PRESENT: -+ ret = sprintf(buf, "%d\n", -+ reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], -+ attr->index - FAN1_PRESENT)); -+ break; -+ case FAN1_FAULT: -+ case FAN2_FAULT: -+ case FAN3_FAULT: -+ case FAN4_FAULT: -+ case FAN5_FAULT: -+ case FAN6_FAULT: -+ ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); -+ break; -+ case FAN1_DIRECTION: -+ case FAN2_DIRECTION: -+ case FAN3_DIRECTION: -+ case FAN4_DIRECTION: -+ case FAN5_DIRECTION: -+ case FAN6_DIRECTION: -+ ret = sprintf(buf, "%d\n", -+ reg_val_to_direction(data->reg_val[FAN_DIRECTION_REG], -+ attr->index - FAN1_DIRECTION)); -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static const struct attribute_group as7712_32x_fan_group = { -+ .attrs = as7712_32x_fan_attributes, -+}; -+ -+static struct as7712_32x_fan_data *as7712_32x_fan_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7712_32x_fan_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || -+ !data->valid) { -+ int i; -+ -+ dev_dbg(&client->dev, "Starting as7712_32x_fan update\n"); -+ data->valid = 0; -+ -+ /* Update fan data -+ */ -+ for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { -+ int status = as7712_32x_fan_read_value(client, fan_reg[i]); -+ -+ if (status < 0) { -+ data->valid = 0; -+ mutex_unlock(&data->update_lock); -+ dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); -+ return data; -+ } -+ else { -+ data->reg_val[i] = status; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int as7712_32x_fan_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7712_32x_fan_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7712_32x_fan_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, &as7712_32x_fan_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: fan '%s'\n", -+ dev_name(data->hwmon_dev), client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &as7712_32x_fan_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7712_32x_fan_remove(struct i2c_client *client) -+{ -+ struct as7712_32x_fan_data *data = i2c_get_clientdata(client); -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7712_32x_fan_group); -+ -+ return 0; -+} -+ -+/* Addresses to scan */ -+static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; -+ -+static const struct i2c_device_id as7712_32x_fan_id[] = { -+ { "as7712_32x_fan", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7712_32x_fan_id); -+ -+static struct i2c_driver as7712_32x_fan_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = DRVNAME, -+ }, -+ .probe = as7712_32x_fan_probe, -+ .remove = as7712_32x_fan_remove, -+ .id_table = as7712_32x_fan_id, -+ .address_list = normal_i2c, -+}; -+ -+static int __init as7712_32x_fan_init(void) -+{ -+ extern int platform_accton_as7712_32x(void); -+ if (!platform_accton_as7712_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7712_32x_fan_driver); -+} -+ -+static void __exit as7712_32x_fan_exit(void) -+{ -+ i2c_del_driver(&as7712_32x_fan_driver); -+} -+ -+module_init(as7712_32x_fan_init); -+module_exit(as7712_32x_fan_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7712_32x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/hwmon/accton_as7712_32x_psu.c b/drivers/hwmon/accton_as7712_32x_psu.c -new file mode 100644 -index 0000000..f1f11f5 ---- /dev/null -+++ b/drivers/hwmon/accton_as7712_32x_psu.c -@@ -0,0 +1,384 @@ -+/* -+ * An hwmon driver for accton as7712_32x Power Module -+ * -+ * Copyright (C) 2014 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 -+#include -+ -+#define MAX_MODEL_NAME 16 -+ -+#define DC12V_FAN_DIR_OFFSET 0x34 -+#define DC12V_FAN_DIR_LEN 3 -+ -+static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); -+static int as7712_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as7712_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[MAX_MODEL_NAME]; /* Model name, read from eeprom */ -+ char fan_dir[DC12V_FAN_DIR_LEN+1]; /* DC12V fan direction */ -+}; -+ -+static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf); -+static struct as7712_32x_psu_data *as7712_32x_psu_update_device(struct device *dev); -+ -+enum as7712_32x_psu_sysfs_attributes { -+ PSU_PRESENT, -+ PSU_MODEL_NAME, -+ PSU_POWER_GOOD, -+ PSU_FAN_DIR /* For DC12V only */ -+}; -+ -+/* 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_string, NULL, PSU_MODEL_NAME); -+static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); -+static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_string, NULL, PSU_FAN_DIR); -+ -+static struct attribute *as7712_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, -+ &sensor_dev_attr_psu_fan_dir.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 as7712_32x_psu_data *data = as7712_32x_psu_update_device(dev); -+ u8 status = 0; -+ -+ if (!data->valid) { -+ return -EIO; -+ } -+ -+ if (attr->index == PSU_PRESENT) { -+ status = !(data->status >> (1-data->index) & 0x1); -+ } -+ else { /* PSU_POWER_GOOD */ -+ status = (data->status >> (3-data->index) & 0x1); -+ } -+ -+ return sprintf(buf, "%d\n", status); -+} -+ -+static ssize_t show_string(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); -+ struct as7712_32x_psu_data *data = as7712_32x_psu_update_device(dev); -+ char *ptr = NULL; -+ -+ if (!data->valid) { -+ return -EIO; -+ } -+ -+ if (attr->index == PSU_MODEL_NAME) { -+ ptr = data->model_name; -+ } -+ else { /* PSU_FAN_DIR */ -+ ptr = data->fan_dir; -+ } -+ -+ return sprintf(buf, "%s\n", ptr); -+} -+ -+static const struct attribute_group as7712_32x_psu_group = { -+ .attrs = as7712_32x_psu_attributes, -+}; -+ -+static int as7712_32x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7712_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 as7712_32x_psu_data), GFP_KERNEL); -+ if (!data) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ i2c_set_clientdata(client, data); -+ data->valid = 0; -+ data->index = dev_id->driver_data; -+ mutex_init(&data->update_lock); -+ -+ dev_info(&client->dev, "chip found\n"); -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &as7712_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; -+ } -+ -+ 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, &as7712_32x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7712_32x_psu_remove(struct i2c_client *client) -+{ -+ struct as7712_32x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7712_32x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum psu_index -+{ -+ as7712_32x_psu1, -+ as7712_32x_psu2 -+}; -+ -+static const struct i2c_device_id as7712_32x_psu_id[] = { -+ { "as7712_32x_psu1", as7712_32x_psu1 }, -+ { "as7712_32x_psu2", as7712_32x_psu2 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7712_32x_psu_id); -+ -+static struct i2c_driver as7712_32x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7712_32x_psu", -+ }, -+ .probe = as7712_32x_psu_probe, -+ .remove = as7712_32x_psu_remove, -+ .id_table = as7712_32x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7712_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; -+} -+ -+enum psu_type { -+ PSU_TYPE_AC_110V, -+ PSU_TYPE_DC_48V, -+ PSU_TYPE_DC_12V -+}; -+ -+struct model_name_info { -+ enum psu_type type; -+ u8 offset; -+ u8 length; -+ char* model_name; -+}; -+ -+struct model_name_info models[] = { -+{PSU_TYPE_AC_110V, 0x20, 8, "YM-2651Y"}, -+{PSU_TYPE_DC_48V, 0x20, 8, "YM-2651V"}, -+{PSU_TYPE_DC_12V, 0x00, 11, "PSU-12V-750"}, -+}; -+ -+static int as7712_32x_psu_model_name_get(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7712_32x_psu_data *data = i2c_get_clientdata(client); -+ int i, status; -+ -+ for (i = 0; i < ARRAY_SIZE(models); i++) { -+ memset(data->model_name, 0, sizeof(data->model_name)); -+ -+ status = as7712_32x_psu_read_block(client, models[i].offset, -+ data->model_name, models[i].length); -+ if (status < 0) { -+ data->model_name[0] = '\0'; -+ dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", -+ client->addr, models[i].offset); -+ return status; -+ } -+ else { -+ data->model_name[models[i].length] = '\0'; -+ } -+ -+ /* Determine if the model name is known, if not, read next index -+ */ -+ if (strncmp(data->model_name, models[i].model_name, models[i].length) == 0) { -+ return 0; -+ } -+ else { -+ data->model_name[0] = '\0'; -+ } -+ } -+ -+ return -ENODATA; -+} -+ -+static struct as7712_32x_psu_data *as7712_32x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7712_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 power_good = 0; -+ -+ data->valid = 0; -+ dev_dbg(&client->dev, "Starting as7712_32x update\n"); -+ -+ /* Read psu status */ -+ status = accton_i2c_cpld_read(0x60, 0x2); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status); -+ goto exit; -+ } -+ else { -+ data->status = status; -+ } -+ -+ /* Read model name */ -+ memset(data->model_name, 0, sizeof(data->model_name)); -+ memset(data->fan_dir, 0, sizeof(data->fan_dir)); -+ power_good = (data->status >> (3-data->index) & 0x1); -+ -+ if (power_good) { -+ if (as7712_32x_psu_model_name_get(dev) < 0) { -+ goto exit; -+ } -+ -+ if (strncmp(data->model_name, -+ models[PSU_TYPE_DC_12V].model_name, -+ models[PSU_TYPE_DC_12V].length) == 0) { -+ /* Read fan direction */ -+ status = as7712_32x_psu_read_block(client, DC12V_FAN_DIR_OFFSET, -+ data->fan_dir, DC12V_FAN_DIR_LEN); -+ -+ if (status < 0) { -+ data->fan_dir[0] = '\0'; -+ dev_dbg(&client->dev, "unable to read fan direction from (0x%x) offset(0x%x)\n", -+ client->addr, DC12V_FAN_DIR_OFFSET); -+ goto exit; -+ } -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7712_32x_psu_init(void) -+{ -+ extern int platform_accton_as7712_32x(void); -+ if (!platform_accton_as7712_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7712_32x_psu_driver); -+} -+ -+static void __exit as7712_32x_psu_exit(void) -+{ -+ i2c_del_driver(&as7712_32x_psu_driver); -+} -+ -+module_init(as7712_32x_psu_init); -+module_exit(as7712_32x_psu_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7712_32x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c -index 96e3490..3aeb08d 100644 ---- a/drivers/hwmon/accton_i2c_cpld.c -+++ b/drivers/hwmon/accton_i2c_cpld.c -@@ -201,6 +201,22 @@ int platform_accton_as7512_32x(void) - } - EXPORT_SYMBOL(platform_accton_as7512_32x); - -+static struct dmi_system_id as7712_dmi_table[] = { -+ { -+ .ident = "Accton AS7712", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS7712"), -+ }, -+ } -+}; -+ -+int platform_accton_as7712_32x(void) -+{ -+ return dmi_check_system(as7712_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as7712_32x); -+ - MODULE_AUTHOR("Brandon Chuang "); - MODULE_DESCRIPTION("accton_i2c_cpld driver"); - MODULE_LICENSE("GPL"); -diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig -index fb48975..48106f2 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -61,6 +61,13 @@ config LEDS_ACCTON_AS7512_32x - This option enables support for the LEDs on the Accton as7512 32x. - Say Y to enable LEDs on the Accton as7512 32x. - -+config LEDS_ACCTON_AS7712_32x -+ tristate "LED support for the Accton as7712 32x" -+ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD -+ help -+ This option enables support for the LEDs on the Accton as7712 32x. -+ Say Y to enable LEDs on the Accton as7712 32x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index dff0462..c4ea931 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -46,7 +46,7 @@ obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o - obj-$(CONFIG_LEDS_ACCTON_AS5712_54x) += leds-accton_as5712_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6712_32x) += leds-accton_as6712_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS7512_32x) += leds-accton_as7512_32x.o -- -+obj-$(CONFIG_LEDS_ACCTON_AS7712_32x) += leds-accton_as7712_32x.o - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o - -diff --git a/drivers/leds/leds-accton_as7712_32x.c b/drivers/leds/leds-accton_as7712_32x.c -new file mode 100644 -index 0000000..8b14437 ---- /dev/null -+++ b/drivers/leds/leds-accton_as7712_32x.c -@@ -0,0 +1,443 @@ -+/* -+ * A LED driver for the accton_as7712_32x_led -+ * -+ * Copyright (C) 2014 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 -+#include -+ -+extern int accton_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int accton_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 "accton_as7712_32x_led" -+ -+struct accton_as7712_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[1]; /* only 1 register*/ -+}; -+ -+static struct accton_as7712_32x_led_data *ledctl = NULL; -+ -+/* LED related data -+ */ -+ -+#define LED_CNTRLER_I2C_ADDRESS (0x60) -+ -+#define LED_TYPE_DIAG_REG_MASK (0x3) -+#define LED_MODE_DIAG_GREEN_VALUE (0x02) -+#define LED_MODE_DIAG_RED_VALUE (0x01) -+#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ -+#define LED_MODE_DIAG_OFF_VALUE (0x03) -+ -+ -+#define LED_TYPE_LOC_REG_MASK (0x80) -+#define LED_MODE_LOC_ON_VALUE (0) -+#define LED_MODE_LOC_OFF_VALUE (0x80) -+ -+enum led_type { -+ LED_TYPE_DIAG, -+ LED_TYPE_LOC, -+ LED_TYPE_FAN, -+ LED_TYPE_PSU1, -+ LED_TYPE_PSU2 -+}; -+ -+struct led_reg { -+ u32 types; -+ u8 reg_addr; -+}; -+ -+static const struct led_reg led_reg_map[] = { -+ {(1<update_lock); -+ -+ if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) -+ || !ledctl->valid) { -+ int i; -+ -+ dev_dbg(&ledctl->pdev->dev, "Starting accton_as7712_32x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as7712_32x_led_read_value(led_reg_map[i].reg_addr); -+ -+ if (status < 0) { -+ ledctl->valid = 0; -+ dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, 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_as7712_32x_led_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode, -+ enum led_type type) -+{ -+ int reg_val; -+ u8 reg ; -+ mutex_lock(&ledctl->update_lock); -+ -+ if( !accton_getLedReg(type, ®)) -+ { -+ dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); -+ } -+ -+ reg_val = accton_as7712_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_as7712_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_as7712_32x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as7712_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as7712_32x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as7712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as7712_32x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as7712_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as7712_32x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as7712_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static void accton_as7712_32x_led_auto_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+} -+ -+static enum led_brightness accton_as7712_32x_led_auto_get(struct led_classdev *cdev) -+{ -+ return LED_MODE_AUTO; -+} -+ -+static struct led_classdev accton_as7712_32x_leds[] = { -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as7712_32x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7712_32x_led_diag_set, -+ .brightness_get = accton_as7712_32x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_RED, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as7712_32x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7712_32x_led_loc_set, -+ .brightness_get = accton_as7712_32x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_BLUE, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as7712_32x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7712_32x_led_auto_set, -+ .brightness_get = accton_as7712_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as7712_32x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7712_32x_led_auto_set, -+ .brightness_get = accton_as7712_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as7712_32x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7712_32x_led_auto_set, -+ .brightness_get = accton_as7712_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as7712_32x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { -+ led_classdev_suspend(&accton_as7712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7712_32x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { -+ led_classdev_resume(&accton_as7712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7712_32x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as7712_32x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as7712_32x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as7712_32x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as7712_32x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7712_32x_leds); i++) { -+ led_classdev_unregister(&accton_as7712_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as7712_32x_led_driver = { -+ .probe = accton_as7712_32x_led_probe, -+ .remove = accton_as7712_32x_led_remove, -+ .suspend = accton_as7712_32x_led_suspend, -+ .resume = accton_as7712_32x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as7712_32x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as7712_32x(void); -+ if (!platform_accton_as7712_32x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as7712_32x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as7712_32x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as7712_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_as7712_32x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as7712_32x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as7712_32x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as7712_32x_led_init); -+module_exit(accton_as7712_32x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as7712_32x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 8e959c7..bd435a0 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -100,6 +100,15 @@ config EEPROM_ACCTON_AS7512_32x_SFP - This driver can also be built as a module. If so, the module will - be called accton_as7512_32x_sfp. - -+config EEPROM_ACCTON_AS7712_32x_SFP -+ tristate "Accton as7712 32x sfp" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7712 32x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7712_32x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 0386999..4ad6540 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -9,4 +9,5 @@ obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o - obj-$(CONFIG_EEPROM_ACCTON_AS5712_54x_SFP) += accton_as5712_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6712_32x_SFP) += accton_as6712_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS7512_32x_SFP) += accton_as7512_32x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS7712_32x_SFP) += accton_as7712_32x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as7712_32x_sfp.c b/drivers/misc/eeprom/accton_as7712_32x_sfp.c -new file mode 100644 -index 0000000..5953ae6 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as7712_32x_sfp.c -@@ -0,0 +1,356 @@ -+/* -+ * An hwmon driver for accton as7712_32x sfp -+ * -+ * Copyright (C) 2014 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) (1UL << (i)) -+ -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as7712_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 */ -+ u32 is_present; /* present status */ -+}; -+ -+static struct as7712_32x_sfp_data *as7712_32x_sfp_update_device(struct device *dev); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+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); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as7712_32x_sfp_sysfs_attributes { -+ SFP_PORT_NUMBER, -+ SFP_IS_PRESENT, -+ SFP_IS_PRESENT_ALL, -+ SFP_EEPROM -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+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 struct attribute *as7712_32x_sfp_attributes[] = { -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_eeprom.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 as7712_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], accton_i2c_cpld_read(0x60, 0x30), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(0x60, 0x31), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(0x60, 0x32), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(0x60, 0x33), 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 */ -+ struct as7712_32x_sfp_data *data = as7712_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return -EIO; -+ } -+ -+ return sprintf(buf, "%d\n", data->is_present); -+ } -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as7712_32x_sfp_data *data = as7712_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if (!data->is_present) { -+ return 0; -+ } -+ -+ memcpy(buf, data->eeprom, sizeof(data->eeprom)); -+ -+ return sizeof(data->eeprom); -+} -+ -+static const struct attribute_group as7712_32x_sfp_group = { -+ .attrs = as7712_32x_sfp_attributes, -+}; -+ -+static int as7712_32x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7712_32x_sfp_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7712_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, &as7712_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, &as7712_32x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7712_32x_sfp_remove(struct i2c_client *client) -+{ -+ struct as7712_32x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7712_32x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as7712_32x_sfp1, as7712_32x_sfp2, as7712_32x_sfp3, as7712_32x_sfp4, -+as7712_32x_sfp5, as7712_32x_sfp6, as7712_32x_sfp7, as7712_32x_sfp8, -+as7712_32x_sfp9, as7712_32x_sfp10,as7712_32x_sfp11,as7712_32x_sfp12, -+as7712_32x_sfp13,as7712_32x_sfp14,as7712_32x_sfp15,as7712_32x_sfp16, -+as7712_32x_sfp17,as7712_32x_sfp18,as7712_32x_sfp19,as7712_32x_sfp20, -+as7712_32x_sfp21,as7712_32x_sfp22,as7712_32x_sfp23,as7712_32x_sfp24, -+as7712_32x_sfp25,as7712_32x_sfp26,as7712_32x_sfp27,as7712_32x_sfp28, -+as7712_32x_sfp29,as7712_32x_sfp30,as7712_32x_sfp31,as7712_32x_sfp32 -+}; -+ -+static const struct i2c_device_id as7712_32x_sfp_id[] = { -+{ "as7712_32x_sfp1", as7712_32x_sfp1 }, { "as7712_32x_sfp2", as7712_32x_sfp2 }, -+{ "as7712_32x_sfp3", as7712_32x_sfp3 }, { "as7712_32x_sfp4", as7712_32x_sfp4 }, -+{ "as7712_32x_sfp5", as7712_32x_sfp5 }, { "as7712_32x_sfp6", as7712_32x_sfp6 }, -+{ "as7712_32x_sfp7", as7712_32x_sfp7 }, { "as7712_32x_sfp8", as7712_32x_sfp8 }, -+{ "as7712_32x_sfp9", as7712_32x_sfp9 }, { "as7712_32x_sfp10", as7712_32x_sfp10 }, -+{ "as7712_32x_sfp11", as7712_32x_sfp11 }, { "as7712_32x_sfp12", as7712_32x_sfp12 }, -+{ "as7712_32x_sfp13", as7712_32x_sfp13 }, { "as7712_32x_sfp14", as7712_32x_sfp14 }, -+{ "as7712_32x_sfp15", as7712_32x_sfp15 }, { "as7712_32x_sfp16", as7712_32x_sfp16 }, -+{ "as7712_32x_sfp17", as7712_32x_sfp17 }, { "as7712_32x_sfp18", as7712_32x_sfp18 }, -+{ "as7712_32x_sfp19", as7712_32x_sfp19 }, { "as7712_32x_sfp20", as7712_32x_sfp20 }, -+{ "as7712_32x_sfp21", as7712_32x_sfp21 }, { "as7712_32x_sfp22", as7712_32x_sfp22 }, -+{ "as7712_32x_sfp23", as7712_32x_sfp23 }, { "as7712_32x_sfp24", as7712_32x_sfp24 }, -+{ "as7712_32x_sfp25", as7712_32x_sfp25 }, { "as7712_32x_sfp26", as7712_32x_sfp26 }, -+{ "as7712_32x_sfp27", as7712_32x_sfp27 }, { "as7712_32x_sfp28", as7712_32x_sfp28 }, -+{ "as7712_32x_sfp29", as7712_32x_sfp29 }, { "as7712_32x_sfp30", as7712_32x_sfp30 }, -+{ "as7712_32x_sfp31", as7712_32x_sfp31 }, { "as7712_32x_sfp32", as7712_32x_sfp32 }, -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as7712_32x_sfp_id); -+ -+static struct i2c_driver as7712_32x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7712_32x_sfp", -+ }, -+ .probe = as7712_32x_sfp_probe, -+ .remove = as7712_32x_sfp_remove, -+ .id_table = as7712_32x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7712_32x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as7712_32x_sfp_data *as7712_32x_sfp_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7712_32x_sfp_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 = -1; -+ int i = 0; -+ u8 cpld_reg = 0x30 + (data->port/8); -+ -+ data->valid = 0; -+ -+ /* Read present status of the specified port number */ -+ data->is_present = 0; -+ status = accton_i2c_cpld_read(0x60, cpld_reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x60) reg(0x%x) err %d\n", cpld_reg, status); -+ goto exit; -+ } -+ -+ data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1; -+ -+ /* Read eeprom data based on port number */ -+ memset(data->eeprom, 0, sizeof(data->eeprom)); -+ -+ /* Check if the port is present */ -+ if (data->is_present) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { -+ status = as7712_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, -+ data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), -+ I2C_SMBUS_BLOCK_MAX); -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); -+ goto exit; -+ } -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7712_32x_sfp_init(void) -+{ -+ extern int platform_accton_as7712_32x(void); -+ if (!platform_accton_as7712_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7712_32x_sfp_driver); -+} -+ -+static void __exit as7712_32x_sfp_exit(void) -+{ -+ i2c_del_driver(&as7712_32x_sfp_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as7712_32x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as7712_32x_sfp_init); -+module_exit(as7712_32x_sfp_exit); diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7716_32x-device-drivers.patch b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7716_32x-device-drivers.patch deleted file mode 100644 index d4b5d41c..00000000 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/platform-accton-as7716_32x-device-drivers.patch +++ /dev/null @@ -1,1707 +0,0 @@ -Device driver patches for accton as7716-32x (fan/psu/cpld/led/sfp) - -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 968bd5f..bc10314 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1592,6 +1592,24 @@ config SENSORS_ACCTON_AS5512_54X_FAN - This driver can also be built as a module. If so, the module will - be called accton_as5512_54x_fan. - -+config SENSORS_ACCTON_AS7716_32x_FAN -+ tristate "Accton as7716 32x fan" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7716 32x fan. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7716_32x_fan. -+ -+config SENSORS_ACCTON_AS7716_32x_PSU -+ tristate "Accton as7716 32x psu" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7716 32x psu. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7716_32x_psu. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index b8ee7b0..851d90a 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -38,6 +38,8 @@ obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_FAN) += accton_as5812_54t_fan.o - obj-$(CONFIG_SENSORS_ACCTON_AS5812_54t_PSU) += accton_as5812_54t_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS5512_54X_PSU) += accton_as5512_54x_psu.o - obj-$(CONFIG_SENSORS_ACCTON_AS5512_54X_FAN) += accton_as5512_54x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7716_32x_FAN) += accton_as7716_32x_fan.o -+obj-$(CONFIG_SENSORS_ACCTON_AS7716_32x_PSU) += accton_as7716_32x_psu.o - obj-$(CONFIG_SENSORS_AD7314) += ad7314.o - obj-$(CONFIG_SENSORS_AD7414) += ad7414.o - obj-$(CONFIG_SENSORS_AD7418) += ad7418.o -diff --git a/drivers/hwmon/accton_as7716_32x_fan.c b/drivers/hwmon/accton_as7716_32x_fan.c -new file mode 100644 -index 0000000..924374c ---- /dev/null -+++ b/drivers/hwmon/accton_as7716_32x_fan.c -@@ -0,0 +1,452 @@ -+/* -+ * A hwmon driver for the Accton as7716 32x fan -+ * -+ * Copyright (C) 2014 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 -+ -+#define DRVNAME "as7716_32x_fan" -+ -+static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev); -+static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+/* fan related data, the index should match sysfs_fan_attributes -+ */ -+static const u8 fan_reg[] = { -+ 0x0F, /* fan 1-6 present status */ -+ 0x11, /* fan PWM(for all fan) */ -+ 0x12, /* front fan 1 speed(rpm) */ -+ 0x13, /* front fan 2 speed(rpm) */ -+ 0x14, /* front fan 3 speed(rpm) */ -+ 0x15, /* front fan 4 speed(rpm) */ -+ 0x16, /* front fan 5 speed(rpm) */ -+ 0x17, /* front fan 6 speed(rpm) */ -+ 0x22, /* rear fan 1 speed(rpm) */ -+ 0x23, /* rear fan 2 speed(rpm) */ -+ 0x24, /* rear fan 3 speed(rpm) */ -+ 0x25, /* rear fan 4 speed(rpm) */ -+ 0x26, /* rear fan 5 speed(rpm) */ -+ 0x27, /* rear fan 6 speed(rpm) */ -+}; -+ -+/* Each client has this additional data */ -+struct as7716_32x_fan_data { -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+ char valid; /* != 0 if registers are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ -+}; -+ -+enum fan_id { -+ FAN1_ID, -+ FAN2_ID, -+ FAN3_ID, -+ FAN4_ID, -+ FAN5_ID, -+ FAN6_ID -+}; -+ -+enum sysfs_fan_attributes { -+ FAN_PRESENT_REG, -+ FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ -+ FAN1_FRONT_SPEED_RPM, -+ FAN2_FRONT_SPEED_RPM, -+ FAN3_FRONT_SPEED_RPM, -+ FAN4_FRONT_SPEED_RPM, -+ FAN5_FRONT_SPEED_RPM, -+ FAN6_FRONT_SPEED_RPM, -+ FAN1_REAR_SPEED_RPM, -+ FAN2_REAR_SPEED_RPM, -+ FAN3_REAR_SPEED_RPM, -+ FAN4_REAR_SPEED_RPM, -+ FAN5_REAR_SPEED_RPM, -+ FAN6_REAR_SPEED_RPM, -+ FAN1_PRESENT, -+ FAN2_PRESENT, -+ FAN3_PRESENT, -+ FAN4_PRESENT, -+ FAN5_PRESENT, -+ FAN6_PRESENT, -+ FAN1_FAULT, -+ FAN2_FAULT, -+ FAN3_FAULT, -+ FAN4_FAULT, -+ FAN5_FAULT, -+ FAN6_FAULT -+}; -+ -+/* Define attributes -+ */ -+#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) -+#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr -+ -+#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION) -+#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr -+ -+#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE) -+#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan##index##_duty_cycle_percentage.dev_attr.attr -+ -+#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT) -+#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr -+ -+#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\ -+ static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM) -+#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \ -+ &sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr -+ -+/* 6 fan fault attributes in this platform */ -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6); -+/* 6 fan speed(rpm) attributes in this platform */ -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6); -+/* 6 fan present attributes in this platform */ -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5); -+DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6); -+/* 1 fan duty cycle attribute in this platform */ -+DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(); -+ -+static struct attribute *as7716_32x_fan_attributes[] = { -+ /* fan related attributes */ -+ DECLARE_FAN_FAULT_ATTR(1), -+ DECLARE_FAN_FAULT_ATTR(2), -+ DECLARE_FAN_FAULT_ATTR(3), -+ DECLARE_FAN_FAULT_ATTR(4), -+ DECLARE_FAN_FAULT_ATTR(5), -+ DECLARE_FAN_FAULT_ATTR(6), -+ DECLARE_FAN_SPEED_RPM_ATTR(1), -+ DECLARE_FAN_SPEED_RPM_ATTR(2), -+ DECLARE_FAN_SPEED_RPM_ATTR(3), -+ DECLARE_FAN_SPEED_RPM_ATTR(4), -+ DECLARE_FAN_SPEED_RPM_ATTR(5), -+ DECLARE_FAN_SPEED_RPM_ATTR(6), -+ DECLARE_FAN_PRESENT_ATTR(1), -+ DECLARE_FAN_PRESENT_ATTR(2), -+ DECLARE_FAN_PRESENT_ATTR(3), -+ DECLARE_FAN_PRESENT_ATTR(4), -+ DECLARE_FAN_PRESENT_ATTR(5), -+ DECLARE_FAN_PRESENT_ATTR(6), -+ DECLARE_FAN_DUTY_CYCLE_ATTR(), -+ NULL -+}; -+ -+#define FAN_DUTY_CYCLE_REG_MASK 0xF -+#define FAN_MAX_DUTY_CYCLE 100 -+#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 -+ -+static int as7716_32x_fan_read_value(struct i2c_client *client, u8 reg) -+{ -+ return i2c_smbus_read_byte_data(client, reg); -+} -+ -+static int as7716_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value) -+{ -+ return i2c_smbus_write_byte_data(client, reg, value); -+} -+ -+/* fan utility functions -+ */ -+static u32 reg_val_to_duty_cycle(u8 reg_val) -+{ -+ reg_val &= FAN_DUTY_CYCLE_REG_MASK; -+ return ((u32)(reg_val+1) * 625 + 75)/ 100; -+} -+ -+static u8 duty_cycle_to_reg_val(u8 duty_cycle) -+{ -+ return ((u32)duty_cycle * 100 / 625) - 1; -+} -+ -+static u32 reg_val_to_speed_rpm(u8 reg_val) -+{ -+ return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP; -+} -+ -+static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id) -+{ -+ u8 mask = (1 << id); -+ -+ reg_val &= mask; -+ -+ return reg_val ? 0 : 1; -+} -+ -+static u8 is_fan_fault(struct as7716_32x_fan_data *data, enum fan_id id) -+{ -+ u8 ret = 1; -+ int front_fan_index = FAN1_FRONT_SPEED_RPM + id; -+ int rear_fan_index = FAN1_REAR_SPEED_RPM + id; -+ -+ /* Check if the speed of front or rear fan is ZERO, -+ */ -+ if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) && -+ reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) { -+ ret = 0; -+ } -+ -+ return ret; -+} -+ -+static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ int error, value; -+ struct i2c_client *client = to_i2c_client(dev); -+ -+ error = kstrtoint(buf, 10, &value); -+ if (error) -+ return error; -+ -+ if (value < 0 || value > FAN_MAX_DUTY_CYCLE) -+ return -EINVAL; -+ -+ as7716_32x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */ -+ as7716_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); -+ return count; -+} -+ -+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); -+ struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev); -+ ssize_t ret = 0; -+ -+ if (data->valid) { -+ switch (attr->index) { -+ case FAN_DUTY_CYCLE_PERCENTAGE: -+ { -+ u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]); -+ ret = sprintf(buf, "%u\n", duty_cycle); -+ break; -+ } -+ case FAN1_FRONT_SPEED_RPM: -+ case FAN2_FRONT_SPEED_RPM: -+ case FAN3_FRONT_SPEED_RPM: -+ case FAN4_FRONT_SPEED_RPM: -+ case FAN5_FRONT_SPEED_RPM: -+ case FAN6_FRONT_SPEED_RPM: -+ case FAN1_REAR_SPEED_RPM: -+ case FAN2_REAR_SPEED_RPM: -+ case FAN3_REAR_SPEED_RPM: -+ case FAN4_REAR_SPEED_RPM: -+ case FAN5_REAR_SPEED_RPM: -+ case FAN6_REAR_SPEED_RPM: -+ ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); -+ break; -+ case FAN1_PRESENT: -+ case FAN2_PRESENT: -+ case FAN3_PRESENT: -+ case FAN4_PRESENT: -+ case FAN5_PRESENT: -+ case FAN6_PRESENT: -+ ret = sprintf(buf, "%d\n", -+ reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG], -+ attr->index - FAN1_PRESENT)); -+ break; -+ case FAN1_FAULT: -+ case FAN2_FAULT: -+ case FAN3_FAULT: -+ case FAN4_FAULT: -+ case FAN5_FAULT: -+ case FAN6_FAULT: -+ ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static const struct attribute_group as7716_32x_fan_group = { -+ .attrs = as7716_32x_fan_attributes, -+}; -+ -+static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7716_32x_fan_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || -+ !data->valid) { -+ int i; -+ -+ dev_dbg(&client->dev, "Starting as7716_32x_fan update\n"); -+ data->valid = 0; -+ -+ /* Update fan data -+ */ -+ for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) { -+ int status = as7716_32x_fan_read_value(client, fan_reg[i]); -+ -+ if (status < 0) { -+ data->valid = 0; -+ mutex_unlock(&data->update_lock); -+ dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status); -+ return data; -+ } -+ else { -+ data->reg_val[i] = status; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int as7716_32x_fan_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7716_32x_fan_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7716_32x_fan_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, &as7716_32x_fan_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: fan '%s'\n", -+ dev_name(data->hwmon_dev), client->name); -+ -+ return 0; -+ -+exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7716_32x_fan_remove(struct i2c_client *client) -+{ -+ struct as7716_32x_fan_data *data = i2c_get_clientdata(client); -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group); -+ -+ return 0; -+} -+ -+/* Addresses to scan */ -+static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END }; -+ -+static const struct i2c_device_id as7716_32x_fan_id[] = { -+ { "as7716_32x_fan", 0 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7716_32x_fan_id); -+ -+static struct i2c_driver as7716_32x_fan_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = DRVNAME, -+ }, -+ .probe = as7716_32x_fan_probe, -+ .remove = as7716_32x_fan_remove, -+ .id_table = as7716_32x_fan_id, -+ .address_list = normal_i2c, -+}; -+ -+static int __init as7716_32x_fan_init(void) -+{ -+ extern int platform_accton_as7716_32x(void); -+ if (!platform_accton_as7716_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7716_32x_fan_driver); -+} -+ -+static void __exit as7716_32x_fan_exit(void) -+{ -+ i2c_del_driver(&as7716_32x_fan_driver); -+} -+ -+module_init(as7716_32x_fan_init); -+module_exit(as7716_32x_fan_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7716_32x_fan driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/hwmon/accton_as7716_32x_psu.c b/drivers/hwmon/accton_as7716_32x_psu.c -new file mode 100644 -index 0000000..4fd15ae ---- /dev/null -+++ b/drivers/hwmon/accton_as7716_32x_psu.c -@@ -0,0 +1,293 @@ -+/* -+ * An hwmon driver for accton as7716_32x Power Module -+ * -+ * Copyright (C) 2014 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 -+#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 as7716_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int accton_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 as7716_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[9]; /* Model name, read from eeprom */ -+}; -+ -+static struct as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev); -+ -+enum as7716_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 *as7716_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 as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev); -+ u8 status = 0; -+ -+ if (attr->index == PSU_PRESENT) { -+ status = !(data->status >> (1-data->index) & 0x1); -+ } -+ else { /* PSU_POWER_GOOD */ -+ status = (data->status >> (3-data->index) & 0x1); -+ } -+ -+ return sprintf(buf, "%d\n", status); -+} -+ -+static ssize_t show_model_name(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev); -+ -+ return sprintf(buf, "%s\n", data->model_name); -+} -+ -+static const struct attribute_group as7716_32x_psu_group = { -+ .attrs = as7716_32x_psu_attributes, -+}; -+ -+static int as7716_32x_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7716_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 as7716_32x_psu_data), GFP_KERNEL); -+ if (!data) { -+ status = -ENOMEM; -+ goto exit; -+ } -+ -+ i2c_set_clientdata(client, data); -+ data->valid = 0; -+ data->index = dev_id->driver_data; -+ mutex_init(&data->update_lock); -+ -+ dev_info(&client->dev, "chip found\n"); -+ -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &as7716_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; -+ } -+ -+ 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, &as7716_32x_psu_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7716_32x_psu_remove(struct i2c_client *client) -+{ -+ struct as7716_32x_psu_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7716_32x_psu_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum psu_index -+{ -+ as7716_32x_psu1, -+ as7716_32x_psu2 -+}; -+ -+static const struct i2c_device_id as7716_32x_psu_id[] = { -+ { "as7716_32x_psu1", as7716_32x_psu1 }, -+ { "as7716_32x_psu2", as7716_32x_psu2 }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, as7716_32x_psu_id); -+ -+static struct i2c_driver as7716_32x_psu_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7716_32x_psu", -+ }, -+ .probe = as7716_32x_psu_probe, -+ .remove = as7716_32x_psu_remove, -+ .id_table = as7716_32x_psu_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7716_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 as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7716_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 power_good = 0; -+ -+ dev_dbg(&client->dev, "Starting as7716_32x update\n"); -+ -+ /* Read psu status */ -+ status = accton_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)); -+ power_good = (data->status >> (3-data->index) & 0x1); -+ -+ if (power_good) { -+ status = as7716_32x_psu_read_block(client, 0x20, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); -+ -+ 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[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7716_32x_psu_init(void) -+{ -+ extern int platform_accton_as7716_32x(void); -+ if (!platform_accton_as7716_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7716_32x_psu_driver); -+} -+ -+static void __exit as7716_32x_psu_exit(void) -+{ -+ i2c_del_driver(&as7716_32x_psu_driver); -+} -+ -+module_init(as7716_32x_psu_init); -+module_exit(as7716_32x_psu_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("as7716_32x_psu driver"); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c -index e50c599..89e3a0e 100644 ---- a/drivers/hwmon/accton_i2c_cpld.c -+++ b/drivers/hwmon/accton_i2c_cpld.c -@@ -271,6 +271,29 @@ int platform_accton_as5512_54x(void) - } - EXPORT_SYMBOL(platform_accton_as5512_54x); - -+static struct dmi_system_id as7716_dmi_table[] = { -+ { -+ .ident = "Accton AS7716", -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS7716"), -+ }, -+ }, -+ { -+ .ident = "Accton AS7716", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "Accton"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "AS7716"), -+ }, -+ }, -+}; -+ -+int platform_accton_as7716_32x(void) -+{ -+ return dmi_check_system(as7716_dmi_table); -+} -+EXPORT_SYMBOL(platform_accton_as7716_32x); -+ - MODULE_AUTHOR("Brandon Chuang "); - MODULE_DESCRIPTION("accton_i2c_cpld driver"); - MODULE_LICENSE("GPL"); -diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig -index 9ba4a1b..e29de21 100644 ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -96,6 +96,13 @@ config LEDS_ACCTON_AS5512_54X - This option enables support for the LEDs on the Accton as5512 54x. - Say Y to enable LEDs on the Accton as5512 54x. - -+config LEDS_ACCTON_AS7716_32x -+ tristate "LED support for the Accton as7716 32x" -+ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD -+ help -+ This option enables support for the LEDs on the Accton as7716 32x. -+ Say Y to enable LEDs on the Accton as7716 32x. -+ - config LEDS_LM3530 - tristate "LCD Backlight driver for LM3530" - depends on LEDS_CLASS -diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile -index ff3be6c..42f274a 100644 ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -51,6 +51,7 @@ obj-$(CONFIG_LEDS_ACCTON_AS5812_54x) += leds-accton_as5812_54x.o - obj-$(CONFIG_LEDS_ACCTON_AS6812_32x) += leds-accton_as6812_32x.o - obj-$(CONFIG_LEDS_ACCTON_AS5812_54t) += leds-accton_as5812_54t.o - obj-$(CONFIG_LEDS_ACCTON_AS5512_54X) += leds-accton_as5512_54x.o -+obj-$(CONFIG_LEDS_ACCTON_AS7716_32x) += leds-accton_as7716_32x.o - - # LED SPI Drivers - obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o -diff --git a/drivers/leds/leds-accton_as7716_32x.c b/drivers/leds/leds-accton_as7716_32x.c -new file mode 100644 -index 0000000..5a84897 ---- /dev/null -+++ b/drivers/leds/leds-accton_as7716_32x.c -@@ -0,0 +1,443 @@ -+/* -+ * A LED driver for the accton_as7716_32x_led -+ * -+ * Copyright (C) 2014 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 -+#include -+ -+extern int accton_i2c_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int accton_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 "accton_as7716_32x_led" -+ -+struct accton_as7716_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[1]; /* only 1 register*/ -+}; -+ -+static struct accton_as7716_32x_led_data *ledctl = NULL; -+ -+/* LED related data -+ */ -+ -+#define LED_CNTRLER_I2C_ADDRESS (0x60) -+ -+#define LED_TYPE_DIAG_REG_MASK (0x3) -+#define LED_MODE_DIAG_GREEN_VALUE (0x02) -+#define LED_MODE_DIAG_RED_VALUE (0x01) -+#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/ -+#define LED_MODE_DIAG_OFF_VALUE (0x03) -+ -+ -+#define LED_TYPE_LOC_REG_MASK (0x80) -+#define LED_MODE_LOC_ON_VALUE (0) -+#define LED_MODE_LOC_OFF_VALUE (0x80) -+ -+enum led_type { -+ LED_TYPE_DIAG, -+ LED_TYPE_LOC, -+ LED_TYPE_FAN, -+ LED_TYPE_PSU1, -+ LED_TYPE_PSU2 -+}; -+ -+struct led_reg { -+ u32 types; -+ u8 reg_addr; -+}; -+ -+static const struct led_reg led_reg_map[] = { -+ {(1<update_lock); -+ -+ if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) -+ || !ledctl->valid) { -+ int i; -+ -+ dev_dbg(&ledctl->pdev->dev, "Starting accton_as7716_32x_led update\n"); -+ -+ /* Update LED data -+ */ -+ for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { -+ int status = accton_as7716_32x_led_read_value(led_reg_map[i].reg_addr); -+ -+ if (status < 0) { -+ ledctl->valid = 0; -+ dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, 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_as7716_32x_led_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode, -+ enum led_type type) -+{ -+ int reg_val; -+ u8 reg ; -+ mutex_lock(&ledctl->update_lock); -+ -+ if( !accton_getLedReg(type, ®)) -+ { -+ dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); -+ } -+ -+ reg_val = accton_as7716_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_as7716_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_as7716_32x_led_diag_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG); -+} -+ -+static enum led_brightness accton_as7716_32x_led_diag_get(struct led_classdev *cdev) -+{ -+ accton_as7716_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]); -+} -+ -+static void accton_as7716_32x_led_loc_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+ accton_as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC); -+} -+ -+static enum led_brightness accton_as7716_32x_led_loc_get(struct led_classdev *cdev) -+{ -+ accton_as7716_32x_led_update(); -+ return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]); -+} -+ -+static void accton_as7716_32x_led_auto_set(struct led_classdev *led_cdev, -+ enum led_brightness led_light_mode) -+{ -+} -+ -+static enum led_brightness accton_as7716_32x_led_auto_get(struct led_classdev *cdev) -+{ -+ return LED_MODE_AUTO; -+} -+ -+static struct led_classdev accton_as7716_32x_leds[] = { -+ [LED_TYPE_DIAG] = { -+ .name = "accton_as7716_32x_led::diag", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7716_32x_led_diag_set, -+ .brightness_get = accton_as7716_32x_led_diag_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_RED, -+ }, -+ [LED_TYPE_LOC] = { -+ .name = "accton_as7716_32x_led::loc", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7716_32x_led_loc_set, -+ .brightness_get = accton_as7716_32x_led_loc_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_BLUE, -+ }, -+ [LED_TYPE_FAN] = { -+ .name = "accton_as7716_32x_led::fan", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7716_32x_led_auto_set, -+ .brightness_get = accton_as7716_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU1] = { -+ .name = "accton_as7716_32x_led::psu1", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7716_32x_led_auto_set, -+ .brightness_get = accton_as7716_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+ [LED_TYPE_PSU2] = { -+ .name = "accton_as7716_32x_led::psu2", -+ .default_trigger = "unused", -+ .brightness_set = accton_as7716_32x_led_auto_set, -+ .brightness_get = accton_as7716_32x_led_auto_get, -+ .flags = LED_CORE_SUSPENDRESUME, -+ .max_brightness = LED_MODE_AUTO, -+ }, -+}; -+ -+static int accton_as7716_32x_led_suspend(struct platform_device *dev, -+ pm_message_t state) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) { -+ led_classdev_suspend(&accton_as7716_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7716_32x_led_resume(struct platform_device *dev) -+{ -+ int i = 0; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) { -+ led_classdev_resume(&accton_as7716_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static int accton_as7716_32x_led_probe(struct platform_device *pdev) -+{ -+ int ret, i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) { -+ ret = led_classdev_register(&pdev->dev, &accton_as7716_32x_leds[i]); -+ -+ if (ret < 0) -+ break; -+ } -+ -+ /* Check if all LEDs were successfully registered */ -+ if (i != ARRAY_SIZE(accton_as7716_32x_leds)){ -+ int j; -+ -+ /* only unregister the LEDs that were successfully registered */ -+ for (j = 0; j < i; j++) { -+ led_classdev_unregister(&accton_as7716_32x_leds[i]); -+ } -+ } -+ -+ return ret; -+} -+ -+static int accton_as7716_32x_led_remove(struct platform_device *pdev) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) { -+ led_classdev_unregister(&accton_as7716_32x_leds[i]); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver accton_as7716_32x_led_driver = { -+ .probe = accton_as7716_32x_led_probe, -+ .remove = accton_as7716_32x_led_remove, -+ .suspend = accton_as7716_32x_led_suspend, -+ .resume = accton_as7716_32x_led_resume, -+ .driver = { -+ .name = DRVNAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init accton_as7716_32x_led_init(void) -+{ -+ int ret; -+ -+ extern int platform_accton_as7716_32x(void); -+ if (!platform_accton_as7716_32x()) { -+ return -ENODEV; -+ } -+ -+ ret = platform_driver_register(&accton_as7716_32x_led_driver); -+ if (ret < 0) { -+ goto exit; -+ } -+ -+ ledctl = kzalloc(sizeof(struct accton_as7716_32x_led_data), GFP_KERNEL); -+ if (!ledctl) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&accton_as7716_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_as7716_32x_led_driver); -+ kfree(ledctl); -+ goto exit; -+ } -+ -+exit: -+ return ret; -+} -+ -+static void __exit accton_as7716_32x_led_exit(void) -+{ -+ platform_device_unregister(ledctl->pdev); -+ platform_driver_unregister(&accton_as7716_32x_led_driver); -+ kfree(ledctl); -+} -+ -+module_init(accton_as7716_32x_led_init); -+module_exit(accton_as7716_32x_led_exit); -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton_as7716_32x_led driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig -index 70a3f59..97f811f 100644 ---- a/drivers/misc/eeprom/Kconfig -+++ b/drivers/misc/eeprom/Kconfig -@@ -145,6 +145,15 @@ config EEPROM_ACCTON_AS5512_54X_SFP - This driver can also be built as a module. If so, the module will - be called accton_5512_54x_sfp. - -+config EEPROM_ACCTON_AS7716_32x_SFP -+ tristate "Accton as7716 32x sfp" -+ depends on I2C && SENSORS_ACCTON_I2C_CPLD -+ help -+ If you say yes here you get support for Accton as7716 32x sfp. -+ -+ This driver can also be built as a module. If so, the module will -+ be called accton_as7716_32x_sfp. -+ - config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - help -diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile -index 907f836..b59d70d 100644 ---- a/drivers/misc/eeprom/Makefile -+++ b/drivers/misc/eeprom/Makefile -@@ -14,4 +14,5 @@ obj-$(CONFIG_EEPROM_ACCTON_AS5812_54x_SFP) += accton_as5812_54x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS6812_32x_SFP) += accton_as6812_32x_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5812_54t_SFP) += accton_as5812_54t_sfp.o - obj-$(CONFIG_EEPROM_ACCTON_AS5512_54X_SFP) += accton_as5512_54x_sfp.o -+obj-$(CONFIG_EEPROM_ACCTON_AS7716_32x_SFP) += accton_as7716_32x_sfp.o - obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -diff --git a/drivers/misc/eeprom/accton_as7716_32x_sfp.c b/drivers/misc/eeprom/accton_as7716_32x_sfp.c -new file mode 100644 -index 0000000..432e9b7 ---- /dev/null -+++ b/drivers/misc/eeprom/accton_as7716_32x_sfp.c -@@ -0,0 +1,356 @@ -+/* -+ * An hwmon driver for accton as7716_32x sfp -+ * -+ * Copyright (C) 2014 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) (1UL << (i)) -+ -+ -+/* Addresses scanned -+ */ -+static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; -+ -+/* Each client has this additional data -+ */ -+struct as7716_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 */ -+ u32 is_present; /* present status */ -+}; -+ -+static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev); -+static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); -+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); -+extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); -+ -+enum as7716_32x_sfp_sysfs_attributes { -+ SFP_PORT_NUMBER, -+ SFP_IS_PRESENT, -+ SFP_IS_PRESENT_ALL, -+ SFP_EEPROM -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER); -+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 struct attribute *as7716_32x_sfp_attributes[] = { -+ &sensor_dev_attr_sfp_port_number.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present.dev_attr.attr, -+ &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, -+ &sensor_dev_attr_sfp_eeprom.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 as7716_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], accton_i2c_cpld_read(0x60, 0x30), 1); -+ /* SFP_PRESENT Ports 9-16 */ -+ VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(0x60, 0x31), 1); -+ /* SFP_PRESENT Ports 17-24 */ -+ VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(0x60, 0x32), 1); -+ /* SFP_PRESENT Ports 25-32 */ -+ VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(0x60, 0x33), 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 */ -+ struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return -EIO; -+ } -+ -+ return sprintf(buf, "%d\n", data->is_present); -+ } -+} -+ -+static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ struct as7716_32x_sfp_data *data = as7716_32x_sfp_update_device(dev); -+ -+ if (!data->valid) { -+ return 0; -+ } -+ -+ if (!data->is_present) { -+ return 0; -+ } -+ -+ memcpy(buf, data->eeprom, sizeof(data->eeprom)); -+ -+ return sizeof(data->eeprom); -+} -+ -+static const struct attribute_group as7716_32x_sfp_group = { -+ .attrs = as7716_32x_sfp_attributes, -+}; -+ -+static int as7716_32x_sfp_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) -+{ -+ struct as7716_32x_sfp_data *data; -+ int status; -+ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { -+ status = -EIO; -+ goto exit; -+ } -+ -+ data = kzalloc(sizeof(struct as7716_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, &as7716_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, &as7716_32x_sfp_group); -+exit_free: -+ kfree(data); -+exit: -+ -+ return status; -+} -+ -+static int as7716_32x_sfp_remove(struct i2c_client *client) -+{ -+ struct as7716_32x_sfp_data *data = i2c_get_clientdata(client); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+enum port_numbers { -+as7716_32x_sfp1, as7716_32x_sfp2, as7716_32x_sfp3, as7716_32x_sfp4, -+as7716_32x_sfp5, as7716_32x_sfp6, as7716_32x_sfp7, as7716_32x_sfp8, -+as7716_32x_sfp9, as7716_32x_sfp10,as7716_32x_sfp11,as7716_32x_sfp12, -+as7716_32x_sfp13,as7716_32x_sfp14,as7716_32x_sfp15,as7716_32x_sfp16, -+as7716_32x_sfp17,as7716_32x_sfp18,as7716_32x_sfp19,as7716_32x_sfp20, -+as7716_32x_sfp21,as7716_32x_sfp22,as7716_32x_sfp23,as7716_32x_sfp24, -+as7716_32x_sfp25,as7716_32x_sfp26,as7716_32x_sfp27,as7716_32x_sfp28, -+as7716_32x_sfp29,as7716_32x_sfp30,as7716_32x_sfp31,as7716_32x_sfp32 -+}; -+ -+static const struct i2c_device_id as7716_32x_sfp_id[] = { -+{ "as7716_32x_sfp1", as7716_32x_sfp1 }, { "as7716_32x_sfp2", as7716_32x_sfp2 }, -+{ "as7716_32x_sfp3", as7716_32x_sfp3 }, { "as7716_32x_sfp4", as7716_32x_sfp4 }, -+{ "as7716_32x_sfp5", as7716_32x_sfp5 }, { "as7716_32x_sfp6", as7716_32x_sfp6 }, -+{ "as7716_32x_sfp7", as7716_32x_sfp7 }, { "as7716_32x_sfp8", as7716_32x_sfp8 }, -+{ "as7716_32x_sfp9", as7716_32x_sfp9 }, { "as7716_32x_sfp10", as7716_32x_sfp10 }, -+{ "as7716_32x_sfp11", as7716_32x_sfp11 }, { "as7716_32x_sfp12", as7716_32x_sfp12 }, -+{ "as7716_32x_sfp13", as7716_32x_sfp13 }, { "as7716_32x_sfp14", as7716_32x_sfp14 }, -+{ "as7716_32x_sfp15", as7716_32x_sfp15 }, { "as7716_32x_sfp16", as7716_32x_sfp16 }, -+{ "as7716_32x_sfp17", as7716_32x_sfp17 }, { "as7716_32x_sfp18", as7716_32x_sfp18 }, -+{ "as7716_32x_sfp19", as7716_32x_sfp19 }, { "as7716_32x_sfp20", as7716_32x_sfp20 }, -+{ "as7716_32x_sfp21", as7716_32x_sfp21 }, { "as7716_32x_sfp22", as7716_32x_sfp22 }, -+{ "as7716_32x_sfp23", as7716_32x_sfp23 }, { "as7716_32x_sfp24", as7716_32x_sfp24 }, -+{ "as7716_32x_sfp25", as7716_32x_sfp25 }, { "as7716_32x_sfp26", as7716_32x_sfp26 }, -+{ "as7716_32x_sfp27", as7716_32x_sfp27 }, { "as7716_32x_sfp28", as7716_32x_sfp28 }, -+{ "as7716_32x_sfp29", as7716_32x_sfp29 }, { "as7716_32x_sfp30", as7716_32x_sfp30 }, -+{ "as7716_32x_sfp31", as7716_32x_sfp31 }, { "as7716_32x_sfp32", as7716_32x_sfp32 }, -+{} -+}; -+MODULE_DEVICE_TABLE(i2c, as7716_32x_sfp_id); -+ -+static struct i2c_driver as7716_32x_sfp_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "as7716_32x_sfp", -+ }, -+ .probe = as7716_32x_sfp_probe, -+ .remove = as7716_32x_sfp_remove, -+ .id_table = as7716_32x_sfp_id, -+ .address_list = normal_i2c, -+}; -+ -+static int as7716_32x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data, -+ int data_len) -+{ -+ int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); -+ -+ if (unlikely(result < 0)) -+ goto abort; -+ if (unlikely(result != data_len)) { -+ result = -EIO; -+ goto abort; -+ } -+ -+ result = 0; -+ -+abort: -+ return result; -+} -+ -+static struct as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as7716_32x_sfp_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 = -1; -+ int i = 0; -+ u8 cpld_reg = 0x30 + (data->port/8); -+ -+ data->valid = 0; -+ -+ /* Read present status of the specified port number */ -+ data->is_present = 0; -+ status = accton_i2c_cpld_read(0x60, cpld_reg); -+ -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld(0x60) reg(0x%x) err %d\n", cpld_reg, status); -+ goto exit; -+ } -+ -+ data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1; -+ -+ /* Read eeprom data based on port number */ -+ memset(data->eeprom, 0, sizeof(data->eeprom)); -+ -+ /* Check if the port is present */ -+ if (data->is_present) { -+ /* read eeprom */ -+ for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) { -+ status = as7716_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX, -+ data->eeprom+(i*I2C_SMBUS_BLOCK_MAX), -+ I2C_SMBUS_BLOCK_MAX); -+ if (status < 0) { -+ dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port); -+ goto exit; -+ } -+ } -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ -+ return data; -+} -+ -+static int __init as7716_32x_sfp_init(void) -+{ -+ extern int platform_accton_as7716_32x(void); -+ if (!platform_accton_as7716_32x()) { -+ return -ENODEV; -+ } -+ -+ return i2c_add_driver(&as7716_32x_sfp_driver); -+} -+ -+static void __exit as7716_32x_sfp_exit(void) -+{ -+ i2c_del_driver(&as7716_32x_sfp_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("accton as7716_32x_sfp driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as7716_32x_sfp_init); -+module_exit(as7716_32x_sfp_exit); - diff --git a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/series b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/series index 08b76145..be705fac 100644 --- a/packages/base/any/kernels/3.2.65-1+deb7u2/patches/series +++ b/packages/base/any/kernels/3.2.65-1+deb7u2/patches/series @@ -234,22 +234,11 @@ network-core-proto-down.patch network-bonding-clag-proto-down.patch network-bridge-disable-multiple-sub-intfs-on-same-port.patch network-virtio-proto-down.patch -#driver-hwmon-cpr-4011-4mxx.patch -#platform-accton-as5712_54x-device-drivers.patch -#platform-accton-as6712_32x-device-drivers.patch overlayfs_notify.patch -#platform-accton-as7512_32x-device-drivers.patch driver-pca954x-i2c-mux-deselect-on-exit-config-option.patch -#platform-accton-as7712_32x-device-drivers.patch -#platform-accton-as5812_54x-device-drivers.patch -#platform-accton-as6812_32x-device-drivers.patch -#platform-accton-as5812_54t-device-drivers.patch driver-mfd-lpc-ich.patch driver-watchdog-itco-wd.patch -#platform-accton-as5512_54x-device-drivers.patch -#platform-accton-as7716_32x-device-drivers.patch driver-broadcom-tigon3.patch -#mgmt-port-init-config.patch arch-intel-reboot-cf9-cold.patch drivers-hwmon-adm1021-detect.patch drivers-i2c-busses-i2c-isch-timeout.patch