diff --git a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/arm-iproc-all.config b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/arm-iproc-all.config index 6d420e0f..e70dd3b3 100644 --- a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/arm-iproc-all.config +++ b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/arm-iproc-all.config @@ -868,7 +868,7 @@ CONFIG_EEPROM_AT25=y # CONFIG_EEPROM_93CX6 is not set # CONFIG_EEPROM_93XX46 is not set # CONFIG_EEPROM_SFF_8436 is not set -CONFIG_EEPROM_OPTOE=y +CONFIG_EEPROM_ACCTON_AS4610_SFP=y # CONFIG_CB710_CORE is not set # CONFIG_IWMC3200TOP is not set @@ -1497,7 +1497,7 @@ CONFIG_SENSORS_W83781D=y # CONFIG_SENSORS_W83L786NG is not set # CONFIG_SENSORS_W83627HF is not set # CONFIG_SENSORS_W83627EHF is not set -CONFIG_SENSORS_ACCTON_AS4610_CPLD=y +CONFIG_SENSORS_ACCTON_I2C_CPLD=y CONFIG_SENSORS_ACCTON_AS4610_FAN=y CONFIG_SENSORS_ACCTON_AS4610_PSU=y CONFIG_SENSORS_YM2651Y=y diff --git a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/arch_arm_boot_dts_accton_as4610_54.dts.patch b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/arch_arm_boot_dts_accton_as4610_54.dts.patch index 5d5e0485..7737f9ef 100644 --- a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/arch_arm_boot_dts_accton_as4610_54.dts.patch +++ b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/arch_arm_boot_dts_accton_as4610_54.dts.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/arch/arm/boot/dts/accton_as4610_54.dts -@@ -0,0 +1,256 @@ +@@ -0,0 +1,250 @@ +/* + * Accton AS4610 54 Device Tree Source + * @@ -117,7 +117,7 @@ + cpld@1,0 { + #address-cells = <1>; + #size-cells = <1>; -+ compatible = "accton,as4610_54_cpld"; ++ compatible = "accton,as4610-54-cpld"; + label = "cpld"; + reg = <0x30>; + }; @@ -142,8 +142,8 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; -+ optoe@50 { -+ compatible = "optoe2"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; + label = "port49"; + }; @@ -154,8 +154,8 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; -+ optoe@50 { -+ compatible = "optoe2"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; + label = "port50"; + }; @@ -166,8 +166,8 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; -+ optoe@50 { -+ compatible = "optoe2"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; + label = "port51"; + }; @@ -178,8 +178,8 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; -+ optoe@50 { -+ compatible = "optoe2"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; + label = "port52"; + }; @@ -190,10 +190,9 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; -+ optoe@50 { -+ compatible = "optoe1"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; -+ label = "port53"; + }; + }; + @@ -202,10 +201,9 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; -+ optoe@50 { -+ compatible = "optoe1"; ++ sfp_eeprom@50 { ++ compatible = "at,24c04"; + reg = <0x50>; -+ label = "port54"; + }; + }; + @@ -214,21 +212,17 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; -+ psu1_eeprom@50 { -+ compatible = "accton,as4610_psu1"; ++ psu_eeprom@50 { ++ compatible = "at,24c02"; + reg = <0x50>; ++ label = "psu1_eeprom"; ++ read-only; + }; -+ psu1_pmbus@58 { -+ compatible = "3y-power,ym1921"; -+ reg = <0x58>; -+ }; -+ psu2_eeprom@51 { -+ compatible = "accton,as4610_psu2"; ++ psu_eeprom@51 { ++ compatible = "at,24c02"; + reg = <0x51>; -+ }; -+ psu2_pmbus@59 { -+ compatible = "3y-power,ym1921"; -+ reg = <0x59>; ++ label = "psu2_eeprom"; ++ read-only; + }; + }; + diff --git a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/platform-accton-as4610-device-drivers.patch b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/platform-accton-as4610-device-drivers.patch index 3964bfac..66ba4882 100644 --- a/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/platform-accton-as4610-device-drivers.patch +++ b/packages/base/any/kernels/3.2-lts/configs/arm-iproc-all/patches/platform-accton-as4610-device-drivers.patch @@ -1,5 +1,162 @@ Device driver patches for accton as4610 54 (fan/psu/cpld/led/sfp) +diff --git a/arch/arm/boot/dts/accton_as4610_54.dts b/arch/arm/boot/dts/accton_as4610_54.dts +index 9276c0a..8848f8c 100644 +--- a/arch/arm/boot/dts/accton_as4610_54.dts ++++ b/arch/arm/boot/dts/accton_as4610_54.dts +@@ -105,46 +105,46 @@ + }; + + i2c0: i2c@18038000 { +- compatible = "iproc-smb"; +- reg = <0x18038000 0x1000>; ++ compatible = "iproc-smb"; ++ reg = <0x18038000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = < 127 >; ++ clock-frequency = <400000>; ++ cpld@1,0 { + #address-cells = <1>; +- #size-cells = <0>; +- interrupts = < 127 >; +- clock-frequency = <400000>; +- cpld@1,0 { +- #address-cells = <1>; +- #size-cells = <1>; +- compatible = "accton,as4610-54-cpld"; +- label = "cpld"; +- reg = <0x30>; +- }; ++ #size-cells = <1>; ++ compatible = "accton,as4610_54_cpld"; ++ label = "cpld"; ++ reg = <0x30>; ++ }; + }; + + i2c1: i2c@1803b000 { +- compatible = "iproc-smb"; +- reg = <0x1803b000 0x1000>; ++ compatible = "iproc-smb"; ++ reg = <0x1803b000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = < 128 >; ++ clock-frequency = <100000>; ++ mux@70 { ++ compatible = "ti,pca9548"; ++ reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; +- interrupts = < 128 >; +- clock-frequency = <100000>; +- mux@70 { +- compatible = "ti,pca9548"; +- reg = <0x70>; ++ deselect-on-exit; ++ ++ // SFP+ 1 ++ i2c@0 { + #address-cells = <1>; + #size-cells = <0>; +- deselect-on-exit; +- +- // SFP+ 1 +- i2c@0 { +- #address-cells = <1>; +- #size-cells = <0>; +- reg = <0>; +- sfp_eeprom@50 { +- compatible = "at,24c04"; +- reg = <0x50>; +- label = "port49"; +- }; ++ reg = <0>; ++ sfp_eeprom@50 { ++ compatible = "at,as4610_sfp1"; ++ reg = <0x50>; ++ label = "port49"; + }; ++ }; + + // SFP+ 2 + i2c@1 { +@@ -152,7 +152,7 @@ + #size-cells = <0>; + reg = <1>; + sfp_eeprom@50 { +- compatible = "at,24c04"; ++ compatible = "accton,as4610_sfp2"; + reg = <0x50>; + label = "port50"; + }; +@@ -164,7 +164,7 @@ + #size-cells = <0>; + reg = <2>; + sfp_eeprom@50 { +- compatible = "at,24c04"; ++ compatible = "accton,as4610_sfp3"; + reg = <0x50>; + label = "port51"; + }; +@@ -176,7 +176,7 @@ + #size-cells = <0>; + reg = <3>; + sfp_eeprom@50 { +- compatible = "at,24c04"; ++ compatible = "accton,as4610_sfp4"; + reg = <0x50>; + label = "port52"; + }; +@@ -188,7 +188,7 @@ + #size-cells = <0>; + reg = <4>; + sfp_eeprom@50 { +- compatible = "at,24c04"; ++ compatible = "accton,as4610_sfp5"; + reg = <0x50>; + }; + }; +@@ -199,7 +199,7 @@ + #size-cells = <0>; + reg = <5>; + sfp_eeprom@50 { +- compatible = "at,24c04"; ++ compatible = "accton,as4610_sfp6"; + reg = <0x50>; + }; + }; +@@ -209,17 +209,21 @@ + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; +- psu_eeprom@50 { +- compatible = "at,24c02"; ++ psu1_eeprom@50 { ++ compatible = "accton,as4610_psu1"; + reg = <0x50>; +- label = "psu1_eeprom"; +- read-only; + }; +- psu_eeprom@51 { +- compatible = "at,24c02"; ++ psu1_pmbus@58 { ++ compatible = "3y-power,ym1921"; ++ reg = <0x58>; ++ }; ++ psu2_eeprom@51 { ++ compatible = "accton,as4610_psu2"; + reg = <0x51>; +- label = "psu2_eeprom"; +- read-only; ++ }; ++ psu2_pmbus@59 { ++ compatible = "3y-power,ym1921"; ++ reg = <0x59>; + }; + }; + diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5c984a6..df89e25 100644 --- a/drivers/hwmon/Kconfig @@ -8,18 +165,18 @@ index 5c984a6..df89e25 100644 help Support for the A/D converter on MC13783 PMIC. -+config SENSORS_ACCTON_AS4610_CPLD -+ tristate "Accton as4610 cpld" ++config SENSORS_ACCTON_I2C_CPLD ++ tristate "Accton i2c cpld" + depends on I2C + help -+ If you say yes here you get support for Accton as4610 cpld. ++ 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_as4610_cpld. ++ be called accton_i2c_cpld. + +config SENSORS_ACCTON_AS4610_FAN + tristate "Accton as4610 fan" -+ depends on I2C && SENSORS_ACCTON_AS4610_CPLD ++ depends on I2C && SENSORS_ACCTON_I2C_CPLD + help + If you say yes here you get support for Accton as4610 fan. + @@ -28,7 +185,7 @@ index 5c984a6..df89e25 100644 + +config SENSORS_ACCTON_AS4610_PSU + tristate "Accton as4610 psu" -+ depends on I2C && SENSORS_ACCTON_AS4610_CPLD ++ depends on I2C && SENSORS_ACCTON_I2C_CPLD + help + If you say yes here you get support for Accton as4610 psu. + @@ -56,7 +213,7 @@ index ff3a18e..39c9888 100644 obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o -+obj-$(CONFIG_SENSORS_ACCTON_AS4610_CPLD) += accton_as4610_cpld.o ++obj-$(CONFIG_SENSORS_ACCTON_I2C_CPLD) += accton_i2c_cpld.o +obj-$(CONFIG_SENSORS_ACCTON_AS4610_FAN) += accton_as4610_fan.o +obj-$(CONFIG_SENSORS_ACCTON_AS4610_PSU) += accton_as4610_psu.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o @@ -70,616 +227,12 @@ index ff3a18e..39c9888 100644 obj-$(CONFIG_PMBUS) += pmbus/ -diff --git a/drivers/hwmon/accton_as4610_cpld.c b/drivers/hwmon/accton_as4610_cpld.c -new file mode 100644 -index 0000000..26b3ae5 ---- /dev/null -+++ b/drivers/hwmon/accton_as4610_cpld.c -@@ -0,0 +1,598 @@ -+/* -+ * 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 as4610_54 CPLD -+ * -+ * 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 -+#include -+#include -+ -+#define I2C_RW_RETRY_COUNT 10 -+#define I2C_RW_RETRY_INTERVAL 60 /* ms */ -+ -+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_type { -+ as4610_54_cpld -+}; -+ -+struct as4610_54_cpld_data { -+ enum cpld_type type; -+ struct device *hwmon_dev; -+ struct mutex update_lock; -+}; -+ -+static const struct i2c_device_id as4610_54_cpld_id[] = { -+ { "as4610_54_cpld", as4610_54_cpld }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, as4610_54_cpld_id); -+ -+#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index -+#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index -+#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index -+#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index -+ -+enum as4610_54_cpld1_sysfs_attributes { -+ CPLD_VERSION, -+ PRODUCT_ID, -+ ACCESS, -+ MODULE_PRESENT_ALL, -+ MODULE_RXLOS_ALL, -+ /* transceiver attributes */ -+ TRANSCEIVER_PRESENT_ATTR_ID(1), -+ TRANSCEIVER_PRESENT_ATTR_ID(2), -+ TRANSCEIVER_PRESENT_ATTR_ID(3), -+ TRANSCEIVER_PRESENT_ATTR_ID(4), -+ TRANSCEIVER_PRESENT_ATTR_ID(5), -+ TRANSCEIVER_PRESENT_ATTR_ID(6), -+ TRANSCEIVER_TXDISABLE_ATTR_ID(1), -+ TRANSCEIVER_TXDISABLE_ATTR_ID(2), -+ TRANSCEIVER_TXDISABLE_ATTR_ID(3), -+ TRANSCEIVER_TXDISABLE_ATTR_ID(4), -+ TRANSCEIVER_RXLOS_ATTR_ID(1), -+ TRANSCEIVER_RXLOS_ATTR_ID(2), -+ TRANSCEIVER_RXLOS_ATTR_ID(3), -+ TRANSCEIVER_RXLOS_ATTR_ID(4), -+ TRANSCEIVER_TXFAULT_ATTR_ID(1), -+ TRANSCEIVER_TXFAULT_ATTR_ID(2), -+ TRANSCEIVER_TXFAULT_ATTR_ID(3), -+ TRANSCEIVER_TXFAULT_ATTR_ID(4), -+}; -+ -+/* sysfs attributes for hwmon -+ */ -+static ssize_t show_status(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_present_all(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t access(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count); -+static ssize_t show_version(struct device *dev, struct device_attribute *da, -+ char *buf); -+static ssize_t show_product_id(struct device *dev, struct device_attribute *attr, -+ char *buf); -+static int as4610_54_cpld_read_internal(struct i2c_client *client, u8 reg); -+static int as4610_54_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); -+ -+/* transceiver attributes */ -+#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index) -+#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr -+ -+#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ -+ static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \ -+ static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index) -+ -+#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \ -+ &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ -+ &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr -+ -+static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); -+static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); -+static SENSOR_DEVICE_ATTR(product_id, S_IRUGO, show_product_id, NULL, PRODUCT_ID); -+/* transceiver attributes */ -+static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); -+static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5); -+DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6); -+ -+DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); -+DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); -+DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); -+DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); -+ -+static struct attribute *as4610_54_cpld_attributes[] = { -+ &sensor_dev_attr_version.dev_attr.attr, -+ &sensor_dev_attr_access.dev_attr.attr, -+ /* transceiver attributes */ -+ &sensor_dev_attr_module_present_all.dev_attr.attr, -+ &sensor_dev_attr_module_rx_los_all.dev_attr.attr, -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(1), -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(2), -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(3), -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(4), -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(5), -+ DECLARE_TRANSCEIVER_PRESENT_ATTR(6), -+ DECLARE_SFP_TRANSCEIVER_ATTR(1), -+ DECLARE_SFP_TRANSCEIVER_ATTR(2), -+ DECLARE_SFP_TRANSCEIVER_ATTR(3), -+ DECLARE_SFP_TRANSCEIVER_ATTR(4), -+ NULL -+}; -+ -+static const struct attribute_group as4610_54_cpld_group = { -+ .attrs = as4610_54_cpld_attributes, -+}; -+ -+static ssize_t show_present_all(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ int i, status, present = 0; -+ u8 values[3] = {0}; -+ u8 regs[] = {0x2, 0x3, 0x21}; -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as4610_54_cpld_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ for (i = 0; i < ARRAY_SIZE(regs); i++) { -+ status = as4610_54_cpld_read_internal(client, regs[i]); -+ -+ if (status < 0) { -+ goto exit; -+ } -+ -+ switch (i) { -+ case 0: -+ present |= (status & BIT(6)) >> 6; /* port 25/49 */ -+ present |= (status & BIT(2)) >> 1; /* port 26/50 */ -+ break; -+ case 1: -+ present |= (status & BIT(6)) >> 4; /* port 27/51 */ -+ present |= (status & BIT(2)) << 1; /* port 28/52 */ -+ break; -+ case 2: -+ present |= (status & BIT(0)) << 4; /* port 29/53 */ -+ present |= (status & BIT(4)) << 1; /* port 30/54 */ -+ break; -+ default: -+ break; -+ } -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ return sprintf(buf, "%.2x\n", present); -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return status; -+} -+ -+static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, -+ char *buf) -+{ -+ int i, status, rx_los = 0; -+ u8 values[2] = {0}; -+ u8 regs[] = {0x2, 0x3}; -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as4610_54_cpld_data *data = i2c_get_clientdata(client); -+ -+ mutex_lock(&data->update_lock); -+ -+ for (i = 0; i < ARRAY_SIZE(regs); i++) { -+ status = as4610_54_cpld_read_internal(client, regs[i]); -+ -+ if (status < 0) { -+ goto exit; -+ } -+ switch (i) { -+ case 0: -+ rx_los |= (status & BIT(4)) >> 4; /* port 25/49 rx_los */ -+ rx_los |= (status & BIT(0)) << 1; /* port 26/50 rx_los */ -+ break; -+ case 1: -+ rx_los |= (status & BIT(4)) >> 2; /* port 27/51 rx_los */ -+ rx_los |= (status & BIT(0)) << 3; /* port 28/52 rx_los */ -+ break; -+ default: -+ break; -+ } -+ } -+ -+ mutex_unlock(&data->update_lock); -+ -+ /* Return values 25/49 -> 28/52 in order */ -+ return sprintf(buf, "%.2x\n", rx_los); -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return status; -+} -+ -+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 i2c_client *client = to_i2c_client(dev); -+ struct as4610_54_cpld_data *data = i2c_get_clientdata(client); -+ int status = 0; -+ u8 reg = 0, mask = 0, revert = 0; -+ -+ switch (attr->index) { -+ case MODULE_PRESENT_1: -+ reg = 0x2; -+ mask = 0x40; -+ break; -+ case MODULE_PRESENT_2: -+ reg = 0x2; -+ mask = 0x4; -+ break; -+ case MODULE_PRESENT_3: -+ reg = 0x3; -+ mask = 0x40; -+ break; -+ case MODULE_PRESENT_4: -+ reg = 0x3; -+ mask = 0x4; -+ break; -+ case MODULE_PRESENT_5: -+ reg = 0x21; -+ mask = 0x1; -+ break; -+ case MODULE_PRESENT_6: -+ reg = 0x21; -+ mask = 0x10; -+ break; -+ case MODULE_TXFAULT_1: -+ reg = 0x2; -+ mask = 0x20; -+ break; -+ case MODULE_TXFAULT_2: -+ reg = 0x2; -+ mask = 0x2; -+ break; -+ case MODULE_TXFAULT_3: -+ reg = 0x3; -+ mask = 0x20; -+ break; -+ case MODULE_TXFAULT_4: -+ reg = 0x3; -+ mask = 0x2; -+ break; -+ case MODULE_RXLOS_1: -+ reg = 0x2; -+ mask = 0x10; -+ break; -+ case MODULE_RXLOS_2: -+ reg = 0x2; -+ mask = 0x1; -+ break; -+ case MODULE_RXLOS_3: -+ reg = 0x3; -+ mask = 0x10; -+ break; -+ case MODULE_RXLOS_4: -+ reg = 0x3; -+ mask = 0x1; -+ break; -+ default: -+ return 0; -+ } -+ -+ mutex_lock(&data->update_lock); -+ status = as4610_54_cpld_read_internal(client, reg); -+ if (unlikely(status < 0)) { -+ goto exit; -+ } -+ mutex_unlock(&data->update_lock); -+ -+ return sprintf(buf, "%d\n", !!(status & mask)); -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return status; -+} -+ -+static ssize_t access(struct device *dev, struct device_attribute *da, -+ const char *buf, size_t count) -+{ -+ int status; -+ u32 addr, val; -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as4610_54_cpld_data *data = i2c_get_clientdata(client); -+ -+ if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { -+ return -EINVAL; -+ } -+ -+ if (addr > 0xFF || val > 0xFF) { -+ return -EINVAL; -+ } -+ -+ mutex_lock(&data->update_lock); -+ status = as4610_54_cpld_write_internal(client, addr, val); -+ if (unlikely(status < 0)) { -+ goto exit; -+ } -+ mutex_unlock(&data->update_lock); -+ return count; -+ -+exit: -+ mutex_unlock(&data->update_lock); -+ return status; -+} -+ -+static void as4610_54_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 as4610_54_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_product_id(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\n", (val & 0xF)); -+} -+ -+static ssize_t show_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, 0xB); -+ -+ if (val < 0) { -+ dev_dbg(&client->dev, "cpld(0x%x) reg(0xB) err %d\n", client->addr, val); -+ } -+ -+ return sprintf(buf, "%d", val); -+} -+ -+/* I2C init/probing/exit functions */ -+static int as4610_54_cpld_probe(struct i2c_client *client, -+ const struct i2c_device_id *id) -+{ -+ struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); -+ struct as4610_54_cpld_data *data; -+ int ret = -ENODEV; -+ const struct attribute_group *group = NULL; -+ -+ if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) -+ goto exit; -+ -+ data = kzalloc(sizeof(struct as4610_54_cpld_data), GFP_KERNEL); -+ if (!data) { -+ ret = -ENOMEM; -+ goto exit; -+ } -+ -+ i2c_set_clientdata(client, data); -+ mutex_init(&data->update_lock); -+ data->type = id->driver_data; -+ /* Bring QSFPs out of reset */ -+ as4610_54_cpld_write_internal(client, 0x2A, 0); -+ -+ ret = sysfs_create_group(&client->dev.kobj, &as4610_54_cpld_group); -+ if (ret) { -+ goto exit_free; -+ } -+ -+ as4610_54_cpld_add_client(client); -+ return 0; -+ -+exit_free: -+ kfree(data); -+exit: -+ return ret; -+} -+ -+static int as4610_54_cpld_remove(struct i2c_client *client) -+{ -+ struct as4610_54_cpld_data *data = i2c_get_clientdata(client); -+ const struct attribute_group *group = NULL; -+ -+ as4610_54_cpld_remove_client(client); -+ -+ /* Remove sysfs hooks */ -+ sysfs_remove_group(&client->dev.kobj, &as4610_54_cpld_group); -+ kfree(data); -+ -+ return 0; -+} -+ -+static int as4610_54_cpld_read_internal(struct i2c_client *client, u8 reg) -+{ -+ int status = 0, retry = I2C_RW_RETRY_COUNT; -+ -+ while (retry) { -+ status = i2c_smbus_read_byte_data(client, reg); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ return status; -+} -+ -+static int as4610_54_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) -+{ -+ int status = 0, retry = I2C_RW_RETRY_COUNT; -+ -+ while (retry) { -+ status = i2c_smbus_write_byte_data(client, reg, value); -+ if (unlikely(status < 0)) { -+ msleep(I2C_RW_RETRY_INTERVAL); -+ retry--; -+ continue; -+ } -+ -+ break; -+ } -+ -+ return status; -+} -+ -+int as4610_54_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 = as4610_54_cpld_read_internal(cpld_node->client, reg); -+ break; -+ } -+ } -+ -+ mutex_unlock(&list_lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL(as4610_54_cpld_read); -+ -+int as4610_54_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 = as4610_54_cpld_write_internal(cpld_node->client, reg, value); -+ break; -+ } -+ } -+ -+ mutex_unlock(&list_lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL(as4610_54_cpld_write); -+ -+static struct i2c_driver as4610_54_cpld_driver = { -+ .driver = { -+ .name = "as4610_54_cpld", -+ .owner = THIS_MODULE, -+ }, -+ .probe = as4610_54_cpld_probe, -+ .remove = as4610_54_cpld_remove, -+ .id_table = as4610_54_cpld_id, -+}; -+ -+static int __init as4610_54_cpld_init(void) -+{ -+ mutex_init(&list_lock); -+ return i2c_add_driver(&as4610_54_cpld_driver); -+} -+ -+static void __exit as4610_54_cpld_exit(void) -+{ -+ i2c_del_driver(&as4610_54_cpld_driver); -+} -+ -+MODULE_AUTHOR("Brandon Chuang "); -+MODULE_DESCRIPTION("Accton I2C CPLD driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(as4610_54_cpld_init); -+module_exit(as4610_54_cpld_exit); -+ diff --git a/drivers/hwmon/accton_as4610_fan.c b/drivers/hwmon/accton_as4610_fan.c new file mode 100644 -index 0000000..612d5df +index 0000000..3934bcd --- /dev/null +++ b/drivers/hwmon/accton_as4610_fan.c -@@ -0,0 +1,345 @@ +@@ -0,0 +1,344 @@ +/* + * A hwmon driver for the Accton as4610 fan + * @@ -719,10 +272,9 @@ index 0000000..612d5df +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 as4610_54_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + -+/* fan related data, the index should match sysfs_fan_attributes */ ++/* fan related data, the index should match sysfs_fan_attributes ++ */ +static const u8 fan_reg[] = { + 0x2B, /* fan PWM(for all fan) */ + 0x2D, /* fan 1 speed(rpm) */ @@ -795,91 +347,91 @@ index 0000000..612d5df + +static int as4610_fan_read_value(u8 reg) +{ -+ return as4610_54_cpld_read(AS4610_CPLD_SLAVE_ADDR, reg); ++ return accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, reg); +} + +static int as4610_fan_write_value(u8 reg, u8 value) +{ -+ return as4610_54_cpld_write(AS4610_CPLD_SLAVE_ADDR, reg, value); ++ return accton_i2c_cpld_write(AS4610_CPLD_SLAVE_ADDR, 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 * 125 + 5)/10); ++ reg_val &= FAN_DUTY_CYCLE_REG_MASK; ++ return (u32)((reg_val * 125 + 5)/10); +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ -+ return ((u32)duty_cycle * 10 / 125); ++ return ((u32)duty_cycle * 10 / 125); +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ -+ /* Count Frequency is 1.515KHz= 0.66ms -+ * Count Period = 400 cycle = 400*0.66ms = 264ms -+ * R.P.M value = read value x3.79*60/2 -+ * 3.79 = 1000ms/264ms -+ * 60 = 1min =60s -+ * 2 = 1 rotation of fan has two pulses. -+ */ -+ return (u32)reg_val * 379 * 60 / 2 / 100; ++ /* Count Frequency is 1.515KHz= 0.66ms ++ * Count Period = 400 cycle = 400*0.66ms = 264ms ++ * R.P.M value = read value x3.79*60/2 ++ * 3.79 = 1000ms/264ms ++ * 60 = 1min =60s ++ * 2 = 1 rotation of fan has two pulses. ++ */ ++ return (u32)reg_val * 379 * 60 / 2 / 100; +} + +static u8 is_fan_fault(struct as4610_fan_data *data, enum fan_id id) +{ -+ u8 mask = (id == FAN1_ID) ? 0x20 : 0x10; ++ u8 mask = (id == FAN1_ID) ? 0x20 : 0x10; + -+ return !(data->reg_val[FAN_FAULT] & mask); ++ return !(data->reg_val[FAN_FAULT] & mask); +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ -+ int error, value; ++ int error, value; + -+ error = kstrtoint(buf, 10, &value); -+ if (error) -+ return error; ++ error = kstrtoint(buf, 10, &value); ++ if (error) ++ return error; + -+ if (value < 0 || value > FAN_MAX_DUTY_CYCLE) -+ return -EINVAL; ++ if (value < 0 || value > FAN_MAX_DUTY_CYCLE) ++ return -EINVAL; + -+ as4610_fan_write_value(fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value)); -+ return count; ++ as4610_fan_write_value(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 as4610_fan_data *data = as4610_fan_update_device(dev); -+ ssize_t ret = 0; ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); ++ struct as4610_fan_data *data = as4610_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[attr->index]); -+ ret = sprintf(buf, "%u\n", duty_cycle); -+ break; -+ } -+ case FAN1_SPEED_RPM: -+ case FAN2_SPEED_RPM: -+ ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); -+ break; -+ case FAN1_FAULT: -+ case FAN2_FAULT: -+ ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); -+ break; -+ default: -+ break; -+ } -+ } ++ if (data->valid) { ++ switch (attr->index) { ++ case FAN_DUTY_CYCLE_PERCENTAGE: ++ { ++ u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[attr->index]); ++ ret = sprintf(buf, "%u\n", duty_cycle); ++ break; ++ } ++ case FAN1_SPEED_RPM: ++ case FAN2_SPEED_RPM: ++ ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index])); ++ break; ++ case FAN1_FAULT: ++ case FAN2_FAULT: ++ ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT)); ++ break; ++ default: ++ break; ++ } ++ } + -+ return ret; ++ return ret; +} + +static const struct attribute_group as4610_fan_group = { @@ -888,73 +440,73 @@ index 0000000..612d5df + +static struct as4610_fan_data *as4610_fan_update_device(struct device *dev) +{ -+ mutex_lock(&fan_data->update_lock); ++ mutex_lock(&fan_data->update_lock); + -+ if (time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) || -+ !fan_data->valid) { -+ int i; ++ if (time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) || ++ !fan_data->valid) { ++ int i; + -+ dev_dbg(fan_data->hwmon_dev, "Starting as4610_fan update\n"); -+ fan_data->valid = 0; ++ dev_dbg(fan_data->hwmon_dev, "Starting as4610_fan update\n"); ++ fan_data->valid = 0; + -+ /* Update fan data -+ */ -+ for (i = 0; i < ARRAY_SIZE(fan_data->reg_val); i++) { -+ int status = as4610_fan_read_value(fan_reg[i]); ++ /* Update fan data ++ */ ++ for (i = 0; i < ARRAY_SIZE(fan_data->reg_val); i++) { ++ int status = as4610_fan_read_value(fan_reg[i]); + -+ if (status < 0) { -+ fan_data->valid = 0; -+ mutex_unlock(&fan_data->update_lock); -+ dev_dbg(fan_data->hwmon_dev, "reg %d, err %d\n", fan_reg[i], status); -+ return fan_data; -+ } -+ else { -+ fan_data->reg_val[i] = status; -+ } -+ } ++ if (status < 0) { ++ fan_data->valid = 0; ++ mutex_unlock(&fan_data->update_lock); ++ dev_dbg(fan_data->hwmon_dev, "reg %d, err %d\n", fan_reg[i], status); ++ return fan_data; ++ } ++ else { ++ fan_data->reg_val[i] = status; ++ } ++ } + -+ fan_data->last_updated = jiffies; -+ fan_data->valid = 1; -+ } ++ fan_data->last_updated = jiffies; ++ fan_data->valid = 1; ++ } + -+ mutex_unlock(&fan_data->update_lock); ++ mutex_unlock(&fan_data->update_lock); + -+ return fan_data; ++ return fan_data; +} + +static int as4610_fan_probe(struct platform_device *pdev) +{ -+ int status = -1; ++ int status = -1; + -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&pdev->dev.kobj, &as4610_fan_group); -+ if (status) { -+ goto exit; ++ /* Register sysfs hooks */ ++ status = sysfs_create_group(&pdev->dev.kobj, &as4610_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; -+ } ++ 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_as4610_fan\n"); ++ dev_info(&pdev->dev, "accton_as4610_fan\n"); + -+ return 0; ++ return 0; + +exit_remove: -+ sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group); ++ sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group); +exit: -+ return status; ++ return status; +} + +static int as4610_fan_remove(struct platform_device *pdev) +{ -+ hwmon_device_unregister(fan_data->hwmon_dev); -+ sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group); ++ hwmon_device_unregister(fan_data->hwmon_dev); ++ sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group); + -+ return 0; ++ return 0; +} + +static const struct i2c_device_id as4610_fan_id[] = { @@ -974,48 +526,48 @@ index 0000000..612d5df + +static int __init as4610_fan_init(void) +{ -+ int ret; ++ int ret; + -+ if (as4610_number_of_system_fan() == 0) { -+ return -ENODEV; -+ } ++ if (as4610_number_of_system_fan() == 0) { ++ return -ENODEV; ++ } + -+ ret = platform_driver_register(&as4610_fan_driver); -+ if (ret < 0) { -+ goto exit; -+ } ++ ret = platform_driver_register(&as4610_fan_driver); ++ if (ret < 0) { ++ goto exit; ++ } + -+ fan_data = kzalloc(sizeof(struct as4610_fan_data), GFP_KERNEL); -+ if (!fan_data) { -+ ret = -ENOMEM; -+ platform_driver_unregister(&as4610_fan_driver); -+ goto exit; -+ } ++ fan_data = kzalloc(sizeof(struct as4610_fan_data), GFP_KERNEL); ++ if (!fan_data) { ++ ret = -ENOMEM; ++ platform_driver_unregister(&as4610_fan_driver); ++ goto exit; ++ } + -+ mutex_init(&fan_data->update_lock); -+ fan_data->valid = 0; ++ 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(&as4610_fan_driver); -+ kfree(fan_data); -+ goto exit; -+ } ++ 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(&as4610_fan_driver); ++ kfree(fan_data); ++ goto exit; ++ } + +exit: -+ return ret; ++ return ret; +} + +static void __exit as4610_fan_exit(void) +{ -+ if (!fan_data) { -+ return; -+ } ++ if (!fan_data) { ++ return; ++ } + -+ platform_device_unregister(fan_data->pdev); -+ platform_driver_unregister(&as4610_fan_driver); -+ kfree(fan_data); ++ platform_device_unregister(fan_data->pdev); ++ platform_driver_unregister(&as4610_fan_driver); ++ kfree(fan_data); +} + +late_initcall(as4610_fan_init); @@ -1027,7 +579,7 @@ index 0000000..612d5df + diff --git a/drivers/hwmon/accton_as4610_psu.c b/drivers/hwmon/accton_as4610_psu.c new file mode 100644 -index 0000000..68f0348 +index 0000000..1f0d79d --- /dev/null +++ b/drivers/hwmon/accton_as4610_psu.c @@ -0,0 +1,286 @@ @@ -1070,7 +622,7 @@ index 0000000..68f0348 +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 as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data,int data_len); -+extern int as4610_54_cpld_read(unsigned short cpld_addr, u8 reg); ++extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ @@ -1103,100 +655,100 @@ index 0000000..68f0348 +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *as4610_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 ++ &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 as4610_psu_data *data = as4610_psu_update_device(dev); -+ u8 status = 0; ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); ++ struct as4610_psu_data *data = as4610_psu_update_device(dev); ++ u8 status = 0; + -+ if (attr->index == PSU_PRESENT) { -+ status = (data->status >> (data->index*2) & 0x1); -+ } -+ else { /* PSU_POWER_GOOD */ -+ status = (data->status >> (data->index*2 + 1) & 0x1); -+ } ++ if (attr->index == PSU_PRESENT) { ++ status = (data->status >> (data->index*2) & 0x1); ++ } ++ else { /* PSU_POWER_GOOD */ ++ status = (data->status >> (data->index*2 + 1) & 0x1); ++ } + -+ return sprintf(buf, "%d\n", status); ++ return sprintf(buf, "%d\n", status); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, -+ char *buf) ++ char *buf) +{ -+ struct as4610_psu_data *data = as4610_psu_update_device(dev); ++ struct as4610_psu_data *data = as4610_psu_update_device(dev); + -+ return sprintf(buf, "%s\n", data->model_name); ++ return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as4610_psu_group = { -+ .attrs = as4610_psu_attributes, ++ .attrs = as4610_psu_attributes, +}; + +static int as4610_psu_probe(struct i2c_client *client, -+ const struct i2c_device_id *dev_id) ++ const struct i2c_device_id *dev_id) +{ -+ struct as4610_psu_data *data; -+ int status; ++ struct as4610_psu_data *data; ++ int status; + -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ status = -EIO; -+ goto exit; -+ } ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ status = -EIO; ++ goto exit; ++ } + -+ data = kzalloc(sizeof(struct as4610_psu_data), GFP_KERNEL); -+ if (!data) { -+ status = -ENOMEM; -+ goto exit; -+ } ++ data = kzalloc(sizeof(struct as4610_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); ++ 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"); ++ dev_info(&client->dev, "chip found\n"); + -+ /* Register sysfs hooks */ -+ status = sysfs_create_group(&client->dev.kobj, &as4610_psu_group); -+ if (status) { -+ goto exit_free; -+ } ++ /* Register sysfs hooks */ ++ status = sysfs_create_group(&client->dev.kobj, &as4610_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; -+ } ++ 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); ++ dev_info(&client->dev, "%s: psu '%s'\n", ++ dev_name(data->hwmon_dev), client->name); + -+ return 0; ++ return 0; + +exit_remove: -+ sysfs_remove_group(&client->dev.kobj, &as4610_psu_group); ++ sysfs_remove_group(&client->dev.kobj, &as4610_psu_group); +exit_free: -+ kfree(data); ++ kfree(data); +exit: + -+ return status; ++ return status; +} + +static int as4610_psu_remove(struct i2c_client *client) +{ -+ struct as4610_psu_data *data = i2c_get_clientdata(client); ++ struct as4610_psu_data *data = i2c_get_clientdata(client); + -+ hwmon_device_unregister(data->hwmon_dev); -+ sysfs_remove_group(&client->dev.kobj, &as4610_psu_group); -+ kfree(data); ++ hwmon_device_unregister(data->hwmon_dev); ++ sysfs_remove_group(&client->dev.kobj, &as4610_psu_group); ++ kfree(data); + -+ return 0; ++ return 0; +} + +enum psu_index @@ -1226,88 +778,88 @@ index 0000000..68f0348 +static int as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data, + int count) +{ -+ int status = 0; ++ int status = 0; + -+ while (count) { -+ status = i2c_smbus_read_byte_data(client, command); -+ if (unlikely(status < 0)) { -+ break; -+ } ++ while (count) { ++ status = i2c_smbus_read_byte_data(client, command); ++ if (unlikely(status < 0)) { ++ break; ++ } + -+ *data = (u8)status; -+ data += 1; -+ command += 1; -+ count -= 1; -+ } ++ *data = (u8)status; ++ data += 1; ++ command += 1; ++ count -= 1; ++ } + -+ return status; ++ return status; +} + +static struct as4610_psu_data *as4610_psu_update_device(struct device *dev) +{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct as4610_psu_data *data = i2c_get_clientdata(client); ++ struct i2c_client *client = to_i2c_client(dev); ++ struct as4610_psu_data *data = i2c_get_clientdata(client); + -+ mutex_lock(&data->update_lock); ++ mutex_lock(&data->update_lock); + -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) -+ || !data->valid) { -+ int status; -+ int present = 0; ++ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) ++ || !data->valid) { ++ int status; ++ int present = 0; + -+ data->valid = 0; -+ data->status = 0; -+ dev_dbg(&client->dev, "Starting as4610 update\n"); ++ data->valid = 0; ++ data->status = 0; ++ dev_dbg(&client->dev, "Starting as4610 update\n"); + -+ /* Read psu status */ -+ status = as4610_54_cpld_read(0x30, 0x11); ++ /* Read psu status */ ++ status = accton_i2c_cpld_read(0x30, 0x11); + -+ if (status < 0) { -+ dev_dbg(&client->dev, "cpld reg 0x30 err %d\n", status); -+ goto exit; -+ } -+ else { -+ data->status = status; -+ } ++ if (status < 0) { ++ dev_dbg(&client->dev, "cpld reg 0x30 err %d\n", status); ++ goto exit; ++ } ++ else { ++ data->status = status; ++ } + -+ /* Read model name */ -+ memset(data->model_name, 0, sizeof(data->model_name)); -+ present = (data->status >> (data->index*2) & 0x1); ++ /* Read model name */ ++ memset(data->model_name, 0, sizeof(data->model_name)); ++ present = (data->status >> (data->index*2) & 0x1); + -+ if (present) { -+ int len = ARRAY_SIZE(data->model_name)-1; ++ if (present) { ++ int len = ARRAY_SIZE(data->model_name)-1; + -+ status = as4610_psu_read_data(client, 0x20, data->model_name, -+ ARRAY_SIZE(data->model_name)-1); ++ status = as4610_psu_read_data(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); -+ goto exit; -+ } -+ else { -+ data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; -+ } -+ } ++ if (status < 0) { ++ data->model_name[0] = '\0'; ++ dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr); ++ goto exit; ++ } ++ else { ++ data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0'; ++ } ++ } + -+ data->last_updated = jiffies; -+ data->valid = 1; -+ } ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } + +exit: -+ mutex_unlock(&data->update_lock); ++ mutex_unlock(&data->update_lock); + -+ return data; ++ return data; +} + +static int __init as4610_psu_init(void) +{ -+ return i2c_add_driver(&as4610_psu_driver); ++ return i2c_add_driver(&as4610_psu_driver); +} + +static void __exit as4610_psu_exit(void) +{ -+ i2c_del_driver(&as4610_psu_driver); ++ i2c_del_driver(&as4610_psu_driver); +} + +module_init(as4610_psu_init); @@ -1317,6 +869,297 @@ index 0000000..68f0348 +MODULE_DESCRIPTION("as4610_psu driver"); +MODULE_LICENSE("GPL"); + +diff --git a/drivers/hwmon/accton_i2c_cpld.c b/drivers/hwmon/accton_i2c_cpld.c +new file mode 100644 +index 0000000..0b9762e +--- /dev/null ++++ b/drivers/hwmon/accton_i2c_cpld.c +@@ -0,0 +1,285 @@ ++/* ++ * 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 ++#include ++ ++static LIST_HEAD(cpld_client_list); ++static struct mutex list_lock; ++ ++enum cpld_device_id { ++ as4610_30_cpld, ++ as4610_54_cpld ++}; ++ ++struct cpld_data { ++ enum cpld_device_id id; ++}; ++ ++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 u8 cpld_product_id_offset(enum cpld_device_id id) ++{ ++ switch (id) { ++ case as4610_30_cpld: ++ case as4610_54_cpld: ++ return 0x1; ++ } ++ ++ return 0; ++} ++ ++static int cpld_has_product_id(const struct i2c_device_id *dev_id) ++{ ++ return (dev_id->driver_data == as4610_30_cpld) || ++ (dev_id->driver_data == as4610_54_cpld); ++} ++ ++static ssize_t show_cpld_product_id(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ int val = 0; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct cpld_data *data = i2c_get_clientdata(client); ++ ++ val = i2c_smbus_read_byte_data(client, cpld_product_id_offset(data->id)); ++ if (val < 0) { ++ dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", client->addr, cpld_product_id_offset(data->id), val); ++ } ++ ++ return sprintf(buf, "%d\n", (val & 0xF)); ++} ++ ++static u8 cpld_version_offset(enum cpld_device_id id) ++{ ++ switch (id) { ++ case as4610_30_cpld: ++ case as4610_54_cpld: ++ return 0xB; ++ } ++ ++ return 0; ++} ++ ++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); ++ struct cpld_data *data = i2c_get_clientdata(client); ++ ++ val = i2c_smbus_read_byte_data(client, cpld_version_offset(data->id)); ++ if (val < 0) { ++ dev_dbg(&client->dev, "cpld(0x%x) reg(0xB) err %d\n", client->addr, val); ++ } ++ ++ return sprintf(buf, "%d\n", val); ++} ++ ++static void accton_i2c_cpld_add_client(struct i2c_client *client, enum cpld_device_id id) ++{ ++ struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); ++ struct cpld_data *data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL); ++ ++ if (!node) { ++ dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); ++ return; ++ } ++ ++ if (!data) { ++ dev_dbg(&client->dev, "Can't allocate cpld_client_data (0x%x)\n", client->addr); ++ return; ++ } ++ ++ data->id = id; ++ i2c_set_clientdata(client, data); ++ 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 struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL); ++static struct device_attribute pid = __ATTR(product_id, 0600, show_cpld_product_id, NULL); ++ ++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; ++ } ++ ++ status = sysfs_create_file(&client->dev.kobj, &ver.attr); ++ if (status) { ++ goto exit; ++ } ++ ++ if (cpld_has_product_id(dev_id)) { ++ status = sysfs_create_file(&client->dev.kobj, &pid.attr); ++ if (status) { ++ goto exit; ++ } ++ } ++ ++ dev_info(&client->dev, "chip found\n"); ++ accton_i2c_cpld_add_client(client, (enum cpld_device_id)dev_id->driver_data); ++ ++ return 0; ++ ++exit: ++ return status; ++} ++ ++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; ++} ++ ++static const struct i2c_device_id accton_i2c_cpld_id[] = { ++ { "as4610_30_cpld", as4610_30_cpld }, ++ { "as4610_54_cpld", as4610_54_cpld }, ++ { /* LIST END */} ++}; ++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); ++} ++ ++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/lm77.c b/drivers/hwmon/lm77.c index 8dfc678..b9eafcf 100644 --- a/drivers/hwmon/lm77.c @@ -1997,7 +1840,7 @@ index ff203a4..97d4d54 100644 +config LEDS_ACCTON_AS4610 + tristate "LED support for the Accton as4610" -+ depends on LEDS_CLASS && SENSORS_ACCTON_AS4610_CPLD ++ depends on LEDS_CLASS && SENSORS_ACCTON_I2C_CPLD + help + This option enables support for the LEDs on the Accton as4610. + Say Y to enable LEDs on the Accton as4610. @@ -2019,7 +1862,7 @@ index e4f6bf5..db2dab8 100644 obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-accton_as4610.c b/drivers/leds/leds-accton_as4610.c new file mode 100644 -index 0000000..7a8bf34 +index 0000000..0c4b535 --- /dev/null +++ b/drivers/leds/leds-accton_as4610.c @@ -0,0 +1,678 @@ @@ -2056,8 +1899,8 @@ index 0000000..7a8bf34 +#include +#include + -+extern int as4610_54_cpld_read (unsigned short cpld_addr, u8 reg); -+extern int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); ++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); @@ -2173,12 +2016,12 @@ index 0000000..7a8bf34 + +static int as4610_led_read_value(u8 reg) +{ -+ return as4610_54_cpld_read(0x30, reg); ++ return accton_i2c_cpld_read(0x30, reg); +} + +static int as4610_led_write_value(u8 reg, u8 value) +{ -+ return as4610_54_cpld_write(0x30, reg, value); ++ return accton_i2c_cpld_write(0x30, reg, value); +} + +static void as4610_led_update(void) @@ -2710,14 +2553,14 @@ index 7c7b208..1c62b33 100644 This driver can also be built as a module. If so, the module will be called sff_8436. + -+config EEPROM_OPTOE -+ tristate "Optoe driver" -+ depends on I2C ++config EEPROM_ACCTON_AS4610_SFP ++ tristate "Accton as4610 sfp" ++ depends on I2C && SENSORS_ACCTON_I2C_CPLD + help -+ If you say yes here you get support for Optoe. ++ If you say yes here you get support for Accton as4610 sfp. + + This driver can also be built as a module. If so, the module will -+ be called optoe. ++ be called accton_as4610_sfp. endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile @@ -2728,1167 +2571,1290 @@ index 9edd559..12f7cae 100644 obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o obj-$(CONFIG_EEPROM_SFF_8436) += sff_8436_eeprom.o -+obj-$(CONFIG_EEPROM_OPTOE) += optoe.o -diff --git a/drivers/misc/eeprom/optoe.c b/drivers/misc/eeprom/optoe.c ++obj-$(CONFIG_EEPROM_ACCTON_AS4610_SFP) += accton_as4610_sfp.o +diff --git a/drivers/misc/eeprom/accton_as4610_sfp.c b/drivers/misc/eeprom/accton_as4610_sfp.c new file mode 100644 -index 0000000..b3064f0 +index 0000000..39c17ec --- /dev/null -+++ b/drivers/misc/eeprom/optoe.c -@@ -0,0 +1,1146 @@ ++++ b/drivers/misc/eeprom/accton_as4610_sfp.c +@@ -0,0 +1,1269 @@ +/* -+ * optoe.c - A driver to read and write the EEPROM on optical transceivers -+ * (SFP, QSFP and similar I2C based devices) ++ * SFP driver for accton as4610 sfp + * -+ * Copyright (C) 2014 Cumulus networks Inc. -+ * Copyright (C) 2017 Finisar Corp. ++ * 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 Freeoftware Foundation; either version 2 of the License, or ++ * 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. + */ + -+/* -+ * Description: -+ * a) Optical transceiver EEPROM read/write transactions are just like -+ * the at24 eeproms managed by the at24.c i2c driver -+ * b) The register/memory layout is up to 256 128 byte pages defined by -+ * a "pages valid" register and switched via a "page select" -+ * register as explained in below diagram. -+ * c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128 -+ * bytes of address space, and always references the same -+ * location, independent of the page select register. -+ * All mapped pages are mapped into the upper 128 bytes -+ * (offset 128-255) of the i2c address. -+ * d) Devices with one I2C address (eg QSFP) use I2C address 0x50 -+ * (A0h in the spec), and map all pages in the upper 128 bytes -+ * of that address. -+ * e) Devices with two I2C addresses (eg SFP) have 256 bytes of data -+ * at I2C address 0x50, and 256 bytes of data at I2C address -+ * 0x51 (A2h in the spec). Page selection and paged access -+ * only apply to this second I2C address (0x51). -+ * e) The address space is presented, by the driver, as a linear -+ * address space. For devices with one I2C client at address -+ * 0x50 (eg QSFP), offset 0-127 are in the lower -+ * half of address 50/A0h/client[0]. Offset 128-255 are in -+ * page 0, 256-383 are page 1, etc. More generally, offset -+ * 'n' resides in page (n/128)-1. ('page -1' is the lower -+ * half, offset 0-127). -+ * f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP), -+ * the address space places offset 0-127 in the lower -+ * half of 50/A0/client[0], offset 128-255 in the upper -+ * half. Offset 256-383 is in the lower half of 51/A2/client[1]. -+ * Offset 384-511 is in page 0, in the upper half of 51/A2/... -+ * Offset 512-639 is in page 1, in the upper half of 51/A2/... -+ * Offset 'n' is in page (n/128)-3 (for n > 383) -+ * -+ * One I2c addressed (eg QSFP) Memory Map -+ * -+ * 2-Wire Serial Address: 1010000x -+ * -+ * Lower Page 00h (128 bytes) -+ * ===================== -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * | | -+ * |Page Select Byte(127)| -+ * ===================== -+ * | -+ * | -+ * | -+ * | -+ * V -+ * ------------------------------------------------------------ -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * | | | | -+ * V V V V -+ * ------------ -------------- --------------- -------------- -+ * | | | | | | | | -+ * | Upper | | Upper | | Upper | | Upper | -+ * | Page 00h | | Page 01h | | Page 02h | | Page 03h | -+ * | | | (Optional) | | (Optional) | | (Optional | -+ * | | | | | | | for Cable | -+ * | | | | | | | Assemblies) | -+ * | ID | | AST | | User | | | -+ * | Fields | | Table | | EEPROM Data | | | -+ * | | | | | | | | -+ * | | | | | | | | -+ * | | | | | | | | -+ * ------------ -------------- --------------- -------------- -+ * -+ * The SFF 8436 (QSFP) spec only defines the 4 pages described above. -+ * In anticipation of future applications and devices, this driver -+ * supports access to the full architected range, 256 pages. -+ * -+ **/ -+ -+/* #define DEBUG 1 */ -+ -+#undef EEPROM_CLASS -+#ifdef CONFIG_EEPROM_CLASS -+#define EEPROM_CLASS -+#endif -+#ifdef CONFIG_EEPROM_CLASS_MODULE -+#define EEPROM_CLASS -+#endif -+ -+#include -+#include +#include -+#include -+#include -+#include -+#include +#include +#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#ifdef EEPROM_CLASS -+#include ++#define DRIVER_NAME "as4610_sfp" /* Platform dependent */ ++ ++#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 + -+#include ++#define NUM_OF_SFP_PORT 32 ++#define EEPROM_NAME "sfp_eeprom" ++#define EEPROM_SIZE 256 /* 256 byte eeprom */ ++#define BIT_INDEX(i) (1ULL << (i)) ++#define USE_I2C_BLOCK_READ 0 /* Platform dependent */ ++#define I2C_RW_RETRY_COUNT 3 ++#define I2C_RW_RETRY_INTERVAL 100 /* ms */ + -+/* The maximum length of a port name */ -+#define MAX_PORT_NAME_LEN 20 ++#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) ++#define SFP_EEPROM_A2_I2C_ADDR (0xA2 >> 1) + -+struct optoe_platform_data { -+ u32 byte_len; /* size (sum of all addr) */ -+ u16 page_size; /* for writes */ -+ u8 flags; -+ void *dummy1; /* backward compatibility */ -+ void *dummy2; /* backward compatibility */ ++#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 + -+#ifdef EEPROM_CLASS -+ struct eeprom_platform_data *eeprom_data; -+#endif -+ char port_name[MAX_PORT_NAME_LEN]; ++#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 show_port_number(struct device *dev, struct device_attribute *da, char *buf); ++static ssize_t show_port_type(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 sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); ++static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); ++static ssize_t sfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count); ++static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count);; ++static ssize_t sfp_show_ddm_implemented(struct device *dev, struct device_attribute *da, char *buf); ++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); ++ ++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 +}; + -+/* fundamental unit of addressing for EEPROM */ -+#define OPTOE_PAGE_SIZE 128 -+/* -+ * Single address devices (eg QSFP) have 256 pages, plus the unpaged -+ * low 128 bytes. If the device does not support paging, it is -+ * only 2 'pages' long. -+ */ -+#define OPTOE_ARCH_PAGES 256 -+#define ONE_ADDR_EEPROM_SIZE ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) -+#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE) -+/* -+ * Dual address devices (eg SFP) have 256 pages, plus the unpaged -+ * low 128 bytes, plus 256 bytes at 0x50. If the device does not -+ * support paging, it is 4 'pages' long. -+ */ -+#define TWO_ADDR_EEPROM_SIZE ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE) -+#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE) -+#define TWO_ADDR_NO_0X51_SIZE (2 * OPTOE_PAGE_SIZE) ++/* 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); ++static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS); ++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); + -+/* a few constants to find our way around the EEPROM */ -+#define OPTOE_PAGE_SELECT_REG 0x7F -+#define ONE_ADDR_PAGEABLE_REG 0x02 -+#define ONE_ADDR_NOT_PAGEABLE (1<<2) -+#define TWO_ADDR_PAGEABLE_REG 0x40 -+#define TWO_ADDR_PAGEABLE (1<<4) -+#define TWO_ADDR_0X51_REG 92 -+#define TWO_ADDR_0X51_SUPP (1<<6) -+#define OPTOE_ID_REG 0 -+#define OPTOE_READ_OP 0 -+#define OPTOE_WRITE_OP 1 -+#define OPTOE_EOF 0 /* used for access beyond end of device */ -+ -+struct optoe_data { -+ struct optoe_platform_data chip; -+ int use_smbus; -+ char port_name[MAX_PORT_NAME_LEN]; -+ -+ /* -+ * Lock protects against activities from other Linux tasks, -+ * but not from changes by other I2C masters. -+ */ -+ struct mutex lock; -+ struct bin_attribute bin; -+ struct attribute_group attr_group; -+ -+ u8 *writebuf; -+ unsigned int write_max; -+ -+ unsigned int num_addresses; -+ -+#ifdef EEPROM_CLASS -+ struct eeprom_device *eeprom_dev; -+#endif -+ -+ /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */ -+ int dev_class; -+ -+ struct i2c_client *client[]; ++/* 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_los.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_disable.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_fault.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 +}; + -+ -+/* -+ * This parameter is to help this driver avoid blocking other drivers out -+ * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C -+ * clock, one 256 byte read takes about 1/43 second which is excessive; -+ * but the 1/170 second it takes at 400 kHz may be quite reasonable; and -+ * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. -+ * -+ * This value is forced to be a power of two so that writes align on pages. -+ */ -+static unsigned int io_limit = OPTOE_PAGE_SIZE; -+ -+/* -+ * specs often allow 5 msec for a page write, sometimes 20 msec; -+ * it's important to recover from write timeouts. -+ */ -+static unsigned int write_timeout = 25; -+ -+/* -+ * flags to distinguish one-address (QSFP family) from two-address (SFP family) -+ * If the family is not known, figure it out when the device is accessed -+ */ -+#define ONE_ADDR 1 -+#define TWO_ADDR 2 -+ -+static const struct i2c_device_id optoe_ids[] = { -+ { "optoe1", ONE_ADDR }, -+ { "optoe2", TWO_ADDR }, -+ { "sff8436", ONE_ADDR }, -+ { "24c04", TWO_ADDR }, -+ { /* END OF LIST */ } ++/* 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_all, S_IRUGO, sfp_show_tx_rx_status, NULL, RX_LOS_ALL); ++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 +}; -+MODULE_DEVICE_TABLE(i2c, optoe_ids); + -+/*-------------------------------------------------------------------------*/ ++/* SFP ddm attributes for sysfs */ ++static struct attribute *sfp_ddm_attributes[] = { ++ NULL ++}; ++ ++/* Platform dependent +++ */ ++enum port_numbers { ++as4610_sfp1, /* Port 25 for as4610_30, Port 49 for as4610_54 */ ++as4610_sfp2, /* Port 26 for as4610_30, Port 50 for as4610_54 */ ++as4610_sfp3, /* Port 27 for as4610_30, Port 51 for as4610_54 */ ++as4610_sfp4, /* Port 28 for as4610_30, Port 52 for as4610_54 */ ++as4610_sfp5, /* Port 29 for as4610_30, Port 53 for as4610_54 */ ++as4610_sfp6, /* Port 30 for as4610_30, Port 54 for as4610_54 */ ++}; ++ ++static const struct i2c_device_id sfp_device_id[] = { ++{"as4610_sfp1", as4610_sfp1}, ++{"as4610_sfp2", as4610_sfp2}, ++{"as4610_sfp3", as4610_sfp3}, ++{"as4610_sfp4", as4610_sfp4}, ++{"as4610_sfp5", as4610_sfp5}, ++{"as4610_sfp6", as4610_sfp6}, ++{ /* LIST END */ } ++}; ++MODULE_DEVICE_TABLE(i2c, sfp_device_id); ++/* Platform dependent --- */ ++ +/* -+ * This routine computes the addressing information to be used for -+ * a given r/w request. -+ * -+ * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), -+ * the page, and the offset. -+ * -+ * Handles both single address (eg QSFP) and two address (eg SFP). -+ * For SFP, offset 0-255 are on client[0], >255 is on client[1] -+ * Offset 256-383 are on the lower half of client[1] -+ * Pages are accessible on the upper half of client[1]. -+ * Offset >383 are in 128 byte pages mapped into the upper half -+ * -+ * For QSFP, all offsets are on client[0] -+ * offset 0-127 are on the lower half of client[0] (no paging) -+ * Pages are accessible on the upper half of client[1]. -+ * Offset >127 are in 128 byte pages mapped into the upper half -+ * -+ * Callers must not read/write beyond the end of a client or a page -+ * without recomputing the client/page. Hence offset (within page) -+ * plus length must be less than or equal to 128. (Note that this -+ * routine does not have access to the length of the call, hence -+ * cannot do the validity check.) -+ * -+ * Offset within Lower Page 00h and Upper Page 00h are not recomputed ++ * 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; + -+static uint8_t optoe_translate_offset(struct optoe_data *optoe, -+ loff_t *offset, struct i2c_client **client) ++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; ++}; ++ ++static ssize_t show_port_number(struct device *dev, struct device_attribute *da, ++ char *buf) +{ -+ unsigned int page = 0; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sfp_port_data *data = i2c_get_clientdata(client); + -+ *client = optoe->client[0]; -+ -+ /* if SFP style, offset > 255, shift to i2c addr 0x51 */ -+ if (optoe->dev_class == TWO_ADDR) { -+ if (*offset > 255) { -+ /* like QSFP, but shifted to client[1] */ -+ *client = optoe->client[1]; -+ *offset -= 256; -+ } -+ } -+ -+ /* -+ * if offset is in the range 0-128... -+ * page doesn't matter (using lower half), return 0. -+ * offset is already correct (don't add 128 to get to paged area) -+ */ -+ if (*offset < OPTOE_PAGE_SIZE) -+ return page; -+ -+ /* note, page will always be positive since *offset >= 128 */ -+ page = (*offset >> 7)-1; -+ /* 0x80 places the offset in the top half, offset is last 7 bits */ -+ *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f); -+ -+ return page; /* note also returning client and offset */ ++ return sprintf(buf, "sfp %d\n", data->port); +} + -+static ssize_t optoe_eeprom_read(struct optoe_data *optoe, -+ struct i2c_client *client, -+ char *buf, unsigned int offset, size_t count) ++/* Platform dependent +++ */ ++static struct sfp_port_data *sfp_update_present(struct i2c_client *client) +{ -+ struct i2c_msg msg[2]; -+ u8 msgbuf[2]; -+ unsigned long timeout, read_time; -+ int status, i; -+ -+ memset(msg, 0, sizeof(msg)); -+ -+ switch (optoe->use_smbus) { -+ case I2C_SMBUS_I2C_BLOCK_DATA: -+ /*smaller eeproms can work given some SMBus extension calls */ -+ if (count > I2C_SMBUS_BLOCK_MAX) -+ count = I2C_SMBUS_BLOCK_MAX; -+ break; -+ case I2C_SMBUS_WORD_DATA: -+ /* Check for odd length transaction */ -+ count = (count == 1) ? 1 : 2; -+ break; -+ case I2C_SMBUS_BYTE_DATA: -+ count = 1; -+ break; -+ default: -+ /* -+ * When we have a better choice than SMBus calls, use a -+ * combined I2C message. Write address; then read up to -+ * io_limit data bytes. msgbuf is u8 and will cast to our -+ * needs. -+ */ -+ i = 0; -+ msgbuf[i++] = offset; -+ -+ msg[0].addr = client->addr; -+ msg[0].buf = msgbuf; -+ msg[0].len = i; -+ -+ msg[1].addr = client->addr; -+ msg[1].flags = I2C_M_RD; -+ msg[1].buf = buf; -+ msg[1].len = count; -+ } -+ -+ /* -+ * Reads fail if the previous write didn't complete yet. We may -+ * loop a few times until this one succeeds, waiting at least -+ * long enough for one entire page write to work. -+ */ -+ timeout = jiffies + msecs_to_jiffies(write_timeout); -+ do { -+ read_time = jiffies; -+ -+ switch (optoe->use_smbus) { -+ case I2C_SMBUS_I2C_BLOCK_DATA: -+ status = i2c_smbus_read_i2c_block_data(client, offset, -+ count, buf); -+ break; -+ case I2C_SMBUS_WORD_DATA: -+ status = i2c_smbus_read_word_data(client, offset); -+ if (status >= 0) { -+ buf[0] = status & 0xff; -+ if (count == 2) -+ buf[1] = status >> 8; -+ status = count; -+ } -+ break; -+ case I2C_SMBUS_BYTE_DATA: -+ status = i2c_smbus_read_byte_data(client, offset); -+ if (status >= 0) { -+ buf[0] = status; -+ status = count; -+ } -+ break; -+ default: -+ status = i2c_transfer(client->adapter, msg, 2); -+ if (status == 2) -+ status = count; -+ } -+ -+ dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", -+ count, offset, status, jiffies); -+ -+ if (status == count) /* happy path */ -+ return count; -+ -+ if (status == -ENXIO) /* no module present */ -+ return status; -+ -+ /* REVISIT: at HZ=100, this is sloooow */ -+ usleep_range(1000, 2000); -+ } while (time_before(read_time, timeout)); -+ -+ return -ETIMEDOUT; -+} -+ -+static ssize_t optoe_eeprom_write(struct optoe_data *optoe, -+ struct i2c_client *client, -+ const char *buf, -+ unsigned int offset, size_t count) -+{ -+ struct i2c_msg msg; -+ ssize_t status; -+ unsigned long timeout, write_time; -+ unsigned int next_page_start; ++ struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0; ++ int status = -1; ++ u8 regs[] = {0x2, 0x3, 0x21}; + -+ /* write max is at most a page -+ * (In this driver, write_max is actually one byte!) -+ */ -+ if (count > optoe->write_max) -+ count = optoe->write_max; ++ DEBUG_PRINT("Starting sfp present status update"); ++ mutex_lock(&data->update_lock); + -+ /* shorten count if necessary to avoid crossing page boundary */ -+ next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE); -+ if (offset + count > next_page_start) -+ count = next_page_start - offset; ++ /* Read present status of port 49~54 */ ++ data->present = 0; + -+ switch (optoe->use_smbus) { -+ case I2C_SMBUS_I2C_BLOCK_DATA: -+ /*smaller eeproms can work given some SMBus extension calls */ -+ if (count > I2C_SMBUS_BLOCK_MAX) -+ count = I2C_SMBUS_BLOCK_MAX; ++ for (i = 0; i < ARRAY_SIZE(regs); i++) { ++ status = accton_i2c_cpld_read(0x30, regs[i]); ++ ++ if (status < 0) { ++ DEBUG_PRINT("cpld(0x30) reg(0x%x) err %d", regs[i], status); ++ goto exit; ++ } ++ ++ DEBUG_PRINT("Present status = 0x%lx", data->present); ++ switch (i) { ++ case 0: ++ data->present |= (status & BIT_INDEX(6)) >> 6; /* port 25/49 */ ++ data->present |= (status & BIT_INDEX(2)) >> 1; /* port 26/50 */ ++ break; ++ case 1: ++ data->present |= (status & BIT_INDEX(6)) >> 4; /* port 27/51 */ ++ data->present |= (status & BIT_INDEX(2)) << 1; /* port 28/52 */ ++ break; ++ case 2: ++ data->present |= (status & BIT_INDEX(0)) << 4; /* port 29/53 */ ++ data->present |= (status & BIT_INDEX(4)) << 1; /* port 30/54 */ ++ break; ++ default: ++ break; ++ } ++ } ++ ++ DEBUG_PRINT("Present status = 0x%lx", data->present); ++exit: ++ mutex_unlock(&data->update_lock); ++ return (status < 0) ? ERR_PTR(status) : 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; ++ int status = -1; ++ u8 regs[] = {0x2, 0x3, 0x21}; ++ ++ if (time_before(jiffies, data->msa->last_updated + HZ + HZ / 2) && data->msa->valid) { ++ return data; ++ } ++ ++ DEBUG_PRINT("Starting as4610 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 49~52(SFP port) */ ++ for (i = 0; i < ARRAY_SIZE(regs); i++) { ++ status = accton_i2c_cpld_read(0x30, regs[i]); ++ ++ if (status < 0) { ++ DEBUG_PRINT("cpld(0x30) reg(0x%x) err %d", regs[i], status); ++ goto exit; ++ } ++ ++ DEBUG_PRINT("TX_FAULT = 0x%lx, TX_DISABLE = 0x%lx, TX_FAULT = 0x%lx", ++ data->msa->status[0], data->msa->status[1], data->msa->status[2]); ++ switch (i) { ++ case 0: ++ /* port 25/49 */ ++ data->msa->status[0] |= (status & BIT_INDEX(5)) >> 5; /* tx_fail */ ++ data->msa->status[2] |= (status & BIT_INDEX(4)) >> 4; /* rx_los */ ++ /* port 26/50 */ ++ data->msa->status[0] |= (status & BIT_INDEX(1)) << 0; /* tx_fail */ ++ data->msa->status[2] |= (status & BIT_INDEX(0)) << 1; /* rx_los */ ++ break; ++ case 1: ++ /* port 27/51 */ ++ data->msa->status[0] |= (status & BIT_INDEX(5)) >> 3; /* tx_fail */ ++ data->msa->status[2] |= (status & BIT_INDEX(4)) >> 2; /* rx_los */ ++ /* port 28/52 */ ++ data->msa->status[0] |= (status & BIT_INDEX(1)) << 1; /* tx_fail */ ++ data->msa->status[2] |= (status & BIT_INDEX(0)) << 2; /* rx_los */ ++ break; ++ default: ++ break; ++ } ++ } ++ ++ DEBUG_PRINT("TX_FAULT = 0x%lx, TX_DISABLE = 0x%lx, TX_FAULT = 0x%lx", ++ data->msa->status[0], data->msa->status[1], data->msa->status[2]); ++ data->msa->valid = 1; ++ data->msa->last_updated = jiffies; ++ ++exit: ++ mutex_unlock(&data->update_lock); ++ return data; ++} ++/* Platform dependent --- */ ++ ++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); ++ ++ if (data->driver_type == DRIVER_TYPE_QSFP) { ++ return qsfp_set_tx_disable(dev, da, buf, count); ++ } ++ ++ return 0; ++} ++ ++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)) ? 1 : 0; /* Platform dependent */ ++} ++ ++/* Platform dependent +++ */ ++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) { ++ struct sfp_port_data *data = sfp_update_present(client); ++ ++ if (IS_ERR(data)) { ++ return PTR_ERR(data); ++ } ++ ++ /* Return values 25/49 -> 30/54 in order */ ++ return sprintf(buf, "%.2x\n", (u8)(data->present)); ++ } ++ 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); ++ } ++} ++/* Platform dependent --- */ ++ ++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)) { ++ 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)) { ++ 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 (status < 0) ? ERR_PTR(status) : data; ++} ++ ++static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, ++ char *buf) ++{ ++ int present; ++ 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); ++ ++ present = sfp_is_port_present(client, data->port); ++ if (IS_ERR_VALUE(present)) { ++ return present; ++ } ++ ++ if (present == 0) { ++ /* port is not present */ ++ return -ENXIO; ++ } ++ ++ data = qsfp_update_tx_rx_status(dev); ++ if (IS_ERR(data)) { ++ return PTR_ERR(data); ++ } ++ ++ switch (attr->index) { ++ case TX_FAULT: ++ val = !!(data->qsfp->status[2] & 0xF); + break; -+ case I2C_SMBUS_WORD_DATA: -+ /* Check for odd length transaction */ -+ count = (count == 1) ? 1 : 2; ++ case TX_FAULT1: ++ case TX_FAULT2: ++ case TX_FAULT3: ++ case TX_FAULT4: ++ val = !!(data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)); + break; -+ case I2C_SMBUS_BYTE_DATA: -+ count = 1; ++ case TX_DISABLE: ++ val = data->qsfp->status[1] & 0xF; ++ break; ++ case TX_DISABLE1: ++ case TX_DISABLE2: ++ case TX_DISABLE3: ++ case TX_DISABLE4: ++ val = !!(data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)); ++ break; ++ case RX_LOS: ++ val = !!(data->qsfp->status[0] & 0xF); ++ break; ++ case RX_LOS1: ++ case RX_LOS2: ++ case RX_LOS3: ++ case RX_LOS4: ++ val = !!(data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)); + break; + default: -+ /* If we'll use I2C calls for I/O, set up the message */ -+ msg.addr = client->addr; -+ msg.flags = 0; -+ -+ /* msg.buf is u8 and casts will mask the values */ -+ msg.buf = optoe->writebuf; -+ -+ msg.buf[i++] = offset; -+ memcpy(&msg.buf[i], buf, count); -+ msg.len = i + count; + 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 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; ++ } ++ ++ if (!status) { ++ /* port is not present */ ++ return -ENXIO; ++ } ++ ++ 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 (attr->index == TX_DISABLE) { ++ data->qsfp->status[1] = disable & 0xF; ++ } ++ else {/* TX_DISABLE1 ~ TX_DISABLE4*/ ++ 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; ++ } ++ ++ if (status == 0) { ++ /* port is not present */ ++ return -ENODEV; ++ } ++ ++ 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)); ++} ++ ++/* Platform dependent +++ */ ++static ssize_t sfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, ++ char *buf) ++{ ++ u8 val = 0, index = 0; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sfp_port_data *data = i2c_get_clientdata(client); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); ++ ++ if (data->driver_type == DRIVER_TYPE_QSFP) { ++ return qsfp_show_tx_rx_status(dev, da, buf); ++ } ++ ++ data = sfp_update_tx_rx_status(dev); ++ if (IS_ERR(data)) { ++ return PTR_ERR(data); ++ } ++ ++ if(attr->index == RX_LOS_ALL) { ++ /** Return values 25/49 -> 28/52 in order */ ++ return sprintf(buf, "%.2x\n", (u8)(data->msa->status[2])); ++ } ++ ++ switch (attr->index) { ++ case TX_FAULT: ++ index = 0; ++ break; ++ case TX_DISABLE: ++ index = 1; ++ break; ++ case RX_LOS: ++ index = 2; ++ break; ++ default: ++ return 0; ++ } ++ ++ val = (data->msa->status[index] & BIT_INDEX(data->port)) ? 1 : 0; ++ return sprintf(buf, "%d\n", val); ++} ++/* Platform dependent --- */ ++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; ++ } ++ + /* -+ * Reads fail if the previous write didn't complete yet. We may -+ * loop a few times until this one succeeds, waiting at least -+ * long enough for one entire page write to work. ++ * Write data to chip, protecting against concurrent updates ++ * from this host, but not from other I2C masters. + */ -+ timeout = jiffies + msecs_to_jiffies(write_timeout); -+ do { -+ write_time = jiffies; -+ -+ switch (optoe->use_smbus) { -+ case I2C_SMBUS_I2C_BLOCK_DATA: -+ status = i2c_smbus_write_i2c_block_data(client, -+ offset, count, buf); -+ if (status == 0) -+ status = count; -+ break; -+ case I2C_SMBUS_WORD_DATA: -+ if (count == 2) { -+ status = i2c_smbus_write_word_data(client, -+ offset, (u16)((buf[0])|(buf[1] << 8))); -+ } else { -+ /* count = 1 */ -+ status = i2c_smbus_write_byte_data(client, -+ offset, buf[0]); -+ } -+ if (status == 0) -+ status = count; -+ break; -+ case I2C_SMBUS_BYTE_DATA: -+ status = i2c_smbus_write_byte_data(client, offset, -+ buf[0]); -+ if (status == 0) -+ status = count; -+ break; -+ default: -+ status = i2c_transfer(client->adapter, &msg, 1); -+ if (status == 1) -+ status = count; -+ break; -+ } -+ -+ dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", -+ count, offset, (long int) status, jiffies); -+ -+ if (status == count) -+ return count; -+ -+ /* REVISIT: at HZ=100, this is sloooow */ -+ usleep_range(1000, 2000); -+ } while (time_before(write_time, timeout)); -+ -+ return -ETIMEDOUT; -+} -+ -+ -+static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, -+ char *buf, loff_t off, -+ size_t count, int opcode) -+{ -+ struct i2c_client *client; -+ ssize_t retval = 0; -+ uint8_t page = 0; -+ loff_t phy_offset = off; -+ int ret = 0; -+ -+ page = optoe_translate_offset(optoe, &phy_offset, &client); -+ dev_dbg(&client->dev, -+ "%s off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", -+ __func__, off, page, phy_offset, (long int) count, opcode); -+ if (page > 0) { -+ ret = optoe_eeprom_write(optoe, client, &page, -+ OPTOE_PAGE_SELECT_REG, 1); -+ if (ret < 0) { -+ dev_dbg(&client->dev, -+ "Write page register for page %d failed ret:%d!\n", -+ page, ret); -+ return ret; -+ } -+ } ++ mutex_lock(&data->update_lock); + + while (count) { -+ ssize_t status; ++ ssize_t status; + -+ if (opcode == OPTOE_READ_OP) { -+ status = optoe_eeprom_read(optoe, client, -+ buf, phy_offset, count); -+ } else { -+ status = optoe_eeprom_write(optoe, client, -+ buf, phy_offset, count); -+ } ++ status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { -+ if (retval == 0) ++ if (retval == 0) { + retval = status; ++ } + break; + } + buf += status; -+ phy_offset += status; ++ off += status; + count -= status; + retval += status; + } + -+ -+ if (page > 0) { -+ /* return the page register to page 0 (why?) */ -+ page = 0; -+ ret = optoe_eeprom_write(optoe, client, &page, -+ OPTOE_PAGE_SELECT_REG, 1); -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "Restore page register to 0 failed:%d!\n", ret); -+ /* error only if nothing has been transferred */ -+ if (retval == 0) -+ retval = ret; -+ } -+ } ++ mutex_unlock(&data->update_lock); + return retval; +} + -+/* -+ * Figure out if this access is within the range of supported pages. -+ * Note this is called on every access because we don't know if the -+ * module has been replaced since the last call. -+ * If/when modules support more pages, this is the routine to update -+ * to validate and allow access to additional pages. -+ * -+ * Returns updated len for this access: -+ * - entire access is legal, original len is returned. -+ * - access begins legal but is too long, len is truncated to fit. -+ * - initial offset exceeds supported pages, return OPTOE_EOF (zero) -+ */ -+static ssize_t optoe_page_legal(struct optoe_data *optoe, -+ loff_t off, size_t len) ++ ++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 i2c_client *client = optoe->client[0]; -+ u8 regval; -+ int status; -+ size_t maxlen; ++ int present; ++ 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)); + -+ if (off < 0) -+ return -EINVAL; -+ if (optoe->dev_class == TWO_ADDR) { -+ /* SFP case */ -+ /* if only using addr 0x50 (first 256 bytes) we're good */ -+ if ((off + len) <= TWO_ADDR_NO_0X51_SIZE) -+ return len; -+ /* if offset exceeds possible pages, we're not good */ -+ if (off >= TWO_ADDR_EEPROM_SIZE) -+ return OPTOE_EOF; -+ /* in between, are pages supported? */ -+ status = optoe_eeprom_read(optoe, client, ®val, -+ TWO_ADDR_PAGEABLE_REG, 1); -+ if (status < 0) -+ return status; /* error out (no module?) */ -+ if (regval & TWO_ADDR_PAGEABLE) { -+ /* Pages supported, trim len to the end of pages */ -+ maxlen = TWO_ADDR_EEPROM_SIZE - off; -+ } else { -+ /* pages not supported, trim len to unpaged size */ -+ if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE) -+ return OPTOE_EOF; -+ -+ /* will be accessing addr 0x51, is that supported? */ -+ /* byte 92, bit 6 implies DDM support, 0x51 support */ -+ status = optoe_eeprom_read(optoe, client, ®val, -+ TWO_ADDR_0X51_REG, 1); -+ if (status < 0) -+ return status; -+ if (regval & TWO_ADDR_0X51_SUPP) { -+ /* addr 0x51 is OK */ -+ maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off; -+ } else { -+ /* addr 0x51 NOT supported, trim to 256 max */ -+ if (off >= TWO_ADDR_NO_0X51_SIZE) -+ return OPTOE_EOF; -+ maxlen = TWO_ADDR_NO_0X51_SIZE - off; -+ } -+ } -+ len = (len > maxlen) ? maxlen : len; -+ dev_dbg(&client->dev, -+ "page_legal, SFP, off %lld len %ld\n", -+ off, (long int) len); -+ } else { -+ /* QSFP case */ -+ /* if no pages needed, we're good */ -+ if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE) -+ return len; -+ /* if offset exceeds possible pages, we're not good */ -+ if (off >= ONE_ADDR_EEPROM_SIZE) -+ return OPTOE_EOF; -+ /* in between, are pages supported? */ -+ status = optoe_eeprom_read(optoe, client, ®val, -+ ONE_ADDR_PAGEABLE_REG, 1); -+ if (status < 0) -+ return status; /* error out (no module?) */ -+ if (regval & ONE_ADDR_NOT_PAGEABLE) { -+ /* pages not supported, trim len to unpaged size */ -+ if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE) -+ return OPTOE_EOF; -+ maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off; -+ } else { -+ /* Pages supported, trim len to the end of pages */ -+ maxlen = ONE_ADDR_EEPROM_SIZE - off; -+ } -+ len = (len > maxlen) ? maxlen : len; -+ dev_dbg(&client->dev, -+ "page_legal, QSFP, off %lld len %ld\n", -+ off, (long int) len); ++ present = sfp_is_port_present(data->client, data->port); ++ if (IS_ERR_VALUE(present)) { ++ return present; + } -+ return len; ++ ++ if (present == 0) { ++ /* port is not present */ ++ return -ENODEV; ++ } ++ ++ return sfp_port_write(data, buf, off, count); +} + -+static ssize_t optoe_read_write(struct optoe_data *optoe, -+ char *buf, loff_t off, size_t len, int opcode) ++static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, ++ int data_len) +{ -+ struct i2c_client *client = optoe->client[0]; -+ int chunk; -+ int status = 0; -+ ssize_t retval; -+ size_t pending_len = 0, chunk_len = 0; -+ loff_t chunk_offset = 0, chunk_start_offset = 0; ++#if USE_I2C_BLOCK_READ ++ int status, retry = I2C_RW_RETRY_COUNT; + -+ dev_dbg(&client->dev, -+ "%s: off %lld len:%ld, opcode:%s\n", -+ __func__, off, (long int) len, -+ (opcode == OPTOE_READ_OP) ? "r" : "w"); -+ if (unlikely(!len)) -+ return len; ++ 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, status); ++ 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(&optoe->lock); ++ mutex_lock(&data->update_lock); + -+ /* -+ * Confirm this access fits within the device suppored addr range -+ */ -+ status = optoe_page_legal(optoe, off, len); -+ if ((status == OPTOE_EOF) || (status < 0)) { -+ mutex_unlock(&optoe->lock); -+ return status; -+ } -+ len = status; ++ while (count) { ++ ssize_t status; + -+ /* -+ * For each (128 byte) chunk involved in this request, issue a -+ * separate call to sff_eeprom_update_client(), to -+ * ensure that each access recalculates the client/page -+ * and writes the page register as needed. -+ * Note that chunk to page mapping is confusing, is different for -+ * QSFP and SFP, and never needs to be done. Don't try! -+ */ -+ pending_len = len; /* amount remaining to transfer */ -+ retval = 0; /* amount transferred */ -+ for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { -+ -+ /* -+ * Compute the offset and number of bytes to be read/write -+ * -+ * 1. start at offset 0 (within the chunk), and read/write -+ * the entire chunk -+ * 2. start at offset 0 (within the chunk) and read/write less -+ * than entire chunk -+ * 3. start at an offset not equal to 0 and read/write the rest -+ * of the chunk -+ * 4. start at an offset not equal to 0 and read/write less than -+ * (end of chunk - offset) -+ */ -+ chunk_start_offset = chunk * OPTOE_PAGE_SIZE; -+ -+ if (chunk_start_offset < off) { -+ chunk_offset = off; -+ if ((off + pending_len) < (chunk_start_offset + -+ OPTOE_PAGE_SIZE)) -+ chunk_len = pending_len; -+ else -+ chunk_len = OPTOE_PAGE_SIZE - off; -+ } else { -+ chunk_offset = chunk_start_offset; -+ if (pending_len > OPTOE_PAGE_SIZE) -+ chunk_len = OPTOE_PAGE_SIZE; -+ else -+ chunk_len = pending_len; -+ } -+ -+ dev_dbg(&client->dev, -+ "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", -+ off, (long int) len, chunk_start_offset, chunk_offset, -+ (long int) chunk_len, (long int) pending_len); -+ -+ /* -+ * note: chunk_offset is from the start of the EEPROM, -+ * not the start of the chunk -+ */ -+ status = optoe_eeprom_update_client(optoe, buf, -+ chunk_offset, chunk_len, opcode); -+ if (status != chunk_len) { -+ /* This is another 'no device present' path */ -+ dev_dbg(&client->dev, -+ "o_u_c: chunk %d c_offset %lld c_len %ld failed %d!\n", -+ chunk, chunk_offset, (long int) chunk_len, status); -+ if (status > 0) -+ retval += status; -+ if (retval == 0) ++ status = sfp_eeprom_read(data->client, off, buf, count); ++ if (status <= 0) { ++ if (retval == 0) { + retval = status; ++ } + break; + } ++ + buf += status; -+ pending_len -= status; ++ off += status; ++ count -= status; + retval += status; + } -+ mutex_unlock(&optoe->lock); + ++ mutex_unlock(&data->update_lock); + return retval; ++ +} + -+static ssize_t optoe_bin_read(struct file *filp, struct kobject *kobj, ++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 i2c_client *client = to_i2c_client(container_of(kobj, -+ struct device, kobj)); -+ struct optoe_data *optoe = i2c_get_clientdata(client); ++ int present; ++ struct sfp_port_data *data; ++ DEBUG_PRINT("offset = (%d), count = (%d)", off, count); ++ data = dev_get_drvdata(container_of(kobj, struct device, kobj)); + -+ return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP); ++ present = sfp_is_port_present(data->client, data->port); ++ if (IS_ERR_VALUE(present)) { ++ return present; ++ } ++ ++ if (present == 0) { ++ /* port is not present */ ++ return -ENODEV; ++ } ++ ++ return sfp_port_read(data, buf, off, count); +} + -+ -+static ssize_t optoe_bin_write(struct file *filp, struct kobject *kobj, -+ struct bin_attribute *attr, -+ char *buf, loff_t off, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(container_of(kobj, -+ struct device, kobj)); -+ struct optoe_data *optoe = i2c_get_clientdata(client); -+ -+ return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP); -+} -+ -+static int optoe_remove(struct i2c_client *client) -+{ -+ struct optoe_data *optoe; -+ int i; -+ -+ optoe = i2c_get_clientdata(client); -+ sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); -+ sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); -+ -+ for (i = 1; i < optoe->num_addresses; i++) -+ i2c_unregister_device(optoe->client[i]); -+ -+#ifdef EEPROM_CLASS -+ eeprom_device_unregister(optoe->eeprom_dev); -+#endif -+ -+ kfree(optoe->writebuf); -+ kfree(optoe); -+ return 0; -+} -+ -+static ssize_t show_dev_class(struct device *dev, -+ struct device_attribute *dattr, char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct optoe_data *optoe = i2c_get_clientdata(client); -+ ssize_t count; -+ -+ mutex_lock(&optoe->lock); -+ count = sprintf(buf, "%d\n", optoe->dev_class); -+ mutex_unlock(&optoe->lock); -+ -+ return count; -+} -+ -+static ssize_t set_dev_class(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct optoe_data *optoe = i2c_get_clientdata(client); -+ int dev_class; -+ -+ /* -+ * dev_class is actually the number of i2c addresses used, thus -+ * legal values are "1" (QSFP class) and "2" (SFP class) -+ */ -+ -+ if (kstrtoint(buf, 0, &dev_class) != 0 || -+ dev_class < 1 || dev_class > 2) -+ return -EINVAL; -+ -+ mutex_lock(&optoe->lock); -+ optoe->dev_class = dev_class; -+ mutex_unlock(&optoe->lock); -+ -+ return count; -+} -+ -+/* -+ * if using the EEPROM CLASS driver, we don't report a port_name, -+ * the EEPROM CLASS drive handles that. Hence all this code is -+ * only compiled if we are NOT using the EEPROM CLASS driver. -+ */ -+#ifndef EEPROM_CLASS -+ -+static ssize_t show_port_name(struct device *dev, -+ struct device_attribute *dattr, char *buf) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct optoe_data *optoe = i2c_get_clientdata(client); -+ ssize_t count; -+ -+ mutex_lock(&optoe->lock); -+ count = sprintf(buf, "%s\n", optoe->port_name); -+ mutex_unlock(&optoe->lock); -+ -+ return count; -+} -+ -+static ssize_t set_port_name(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct optoe_data *optoe = i2c_get_clientdata(client); -+ char port_name[MAX_PORT_NAME_LEN]; -+ -+ /* no checking, this value is not used except by show_port_name */ -+ -+ if (sscanf(buf, "%19s", port_name) != 1) -+ return -EINVAL; -+ -+ mutex_lock(&optoe->lock); -+ strcpy(optoe->port_name, port_name); -+ mutex_unlock(&optoe->lock); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name); -+#endif /* if NOT defined EEPROM_CLASS, the common case */ -+ -+static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class); -+ -+static struct attribute *optoe_attrs[] = { -+#ifndef EEPROM_CLASS -+ &dev_attr_port_name.attr, -+#endif -+ &dev_attr_dev_class.attr, -+ NULL, -+}; -+ -+static struct attribute_group optoe_attr_group = { -+ .attrs = optoe_attrs, -+}; -+ -+static int optoe_probe(struct i2c_client *client, -+ const struct i2c_device_id *id) ++static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +{ + int err; -+ int use_smbus = 0; -+ struct optoe_platform_data chip; -+ struct optoe_data *optoe; -+ int num_addresses = 0; -+ char port_name[MAX_PORT_NAME_LEN]; + -+ if (client->addr != 0x50) { -+ dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", -+ client->addr); -+ err = -EINVAL; -+ goto exit; -+ } ++ 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; + -+ if (client->dev.platform_data) { -+ chip = *(struct optoe_platform_data *)client->dev.platform_data; -+ /* take the port name from the supplied platform data */ -+#ifdef EEPROM_CLASS -+ strncpy(port_name, chip.eeprom_data->label, MAX_PORT_NAME_LEN); -+#else -+ memcpy(port_name, chip.port_name, MAX_PORT_NAME_LEN); -+#endif -+ dev_dbg(&client->dev, -+ "probe, chip provided, flags:0x%x; name: %s\n", -+ chip.flags, client->name); -+ } else { -+ if (!id->driver_data) { -+ err = -ENODEV; -+ goto exit; -+ } -+ dev_dbg(&client->dev, "probe, building chip\n"); -+ strcpy(port_name, "unitialized"); -+ chip.flags = 0; -+#ifdef EEPROM_CLASS -+ chip.eeprom_data = NULL; -+#endif -+ } -+ -+ /* Use I2C operations unless we're stuck with SMBus extensions. */ -+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { -+ if (i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { -+ use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; -+ } else if (i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_READ_WORD_DATA)) { -+ use_smbus = I2C_SMBUS_WORD_DATA; -+ } else if (i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_READ_BYTE_DATA)) { -+ use_smbus = I2C_SMBUS_BYTE_DATA; -+ } else { -+ err = -EPFNOSUPPORT; -+ goto exit; -+ } -+ } -+ -+ -+ /* -+ * Make room for two i2c clients -+ */ -+ num_addresses = 2; -+ -+ optoe = kzalloc(sizeof(struct optoe_data) + -+ num_addresses * sizeof(struct i2c_client *), -+ GFP_KERNEL); -+ if (!optoe) { -+ err = -ENOMEM; -+ goto exit; -+ } -+ -+ mutex_init(&optoe->lock); -+ -+ /* determine whether this is a one-address or two-address module */ -+ if ((strcmp(client->name, "optoe1") == 0) || -+ (strcmp(client->name, "sff8436") == 0)) { -+ /* one-address (eg QSFP) family */ -+ optoe->dev_class = ONE_ADDR; -+ chip.byte_len = ONE_ADDR_EEPROM_SIZE; -+ num_addresses = 1; -+ } else if ((strcmp(client->name, "optoe2") == 0) || -+ (strcmp(client->name, "24c04") == 0)) { -+ /* SFP family */ -+ optoe->dev_class = TWO_ADDR; -+ chip.byte_len = TWO_ADDR_EEPROM_SIZE; -+ } else { /* those were the only two choices */ -+ err = -EINVAL; -+ goto exit; -+ } -+ -+ dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class); -+ optoe->use_smbus = use_smbus; -+ optoe->chip = chip; -+ optoe->num_addresses = num_addresses; -+ memcpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN); -+ -+ /* -+ * Export the EEPROM bytes through sysfs, since that's convenient. -+ * By default, only root should see the data (maybe passwords etc) -+ */ -+ sysfs_bin_attr_init(&optoe->bin); -+ optoe->bin.attr.name = "eeprom"; -+ optoe->bin.attr.mode = 0444; -+ optoe->bin.read = optoe_bin_read; -+ optoe->bin.size = chip.byte_len; -+ -+ if (!use_smbus || -+ (i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || -+ i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_WRITE_WORD_DATA) || -+ i2c_check_functionality(client->adapter, -+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { -+ /* -+ * NOTE: AN-2079 -+ * Finisar recommends that the host implement 1 byte writes -+ * only since this module only supports 32 byte page boundaries. -+ * 2 byte writes are acceptable for PE and Vout changes per -+ * Application Note AN-2071. -+ */ -+ unsigned int write_max = 1; -+ -+ optoe->bin.write = optoe_bin_write; -+ optoe->bin.attr.mode |= 0200; -+ -+ if (write_max > io_limit) -+ write_max = io_limit; -+ if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) -+ write_max = I2C_SMBUS_BLOCK_MAX; -+ optoe->write_max = write_max; -+ -+ /* buffer (data + address at the beginning) */ -+ optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL); -+ if (!optoe->writebuf) { -+ err = -ENOMEM; -+ goto exit_kfree; -+ } -+ } else { -+ dev_warn(&client->dev, -+ "cannot write due to controller restrictions."); -+ } -+ -+ optoe->client[0] = client; -+ -+ /* SFF-8472 spec requires that the second I2C address be 0x51 */ -+ if (num_addresses == 2) { -+ optoe->client[1] = i2c_new_dummy(client->adapter, 0x51); -+ if (!optoe->client[1]) { -+ dev_err(&client->dev, "address 0x51 unavailable\n"); -+ err = -EADDRINUSE; -+ goto err_struct; -+ } -+ } -+ -+ /* create the sysfs eeprom file */ -+ err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin); -+ if (err) -+ goto err_struct; -+ -+ optoe->attr_group = optoe_attr_group; -+ -+ err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group); ++ /* Create eeprom file */ ++ err = sysfs_create_bin_file(kobj, eeprom); + if (err) { -+ dev_err(&client->dev, "failed to create sysfs attribute group.\n"); -+ goto err_struct; -+ } -+ -+#ifdef EEPROM_CLASS -+ optoe->eeprom_dev = eeprom_device_register(&client->dev, -+ chip.eeprom_data); -+ if (IS_ERR(optoe->eeprom_dev)) { -+ dev_err(&client->dev, "error registering eeprom device.\n"); -+ err = PTR_ERR(optoe->eeprom_dev); -+ goto err_sysfs_cleanup; -+ } -+#endif -+ -+ i2c_set_clientdata(client, optoe); -+ -+ dev_info(&client->dev, "%zu byte %s EEPROM, %s\n", -+ optoe->bin.size, client->name, -+ optoe->bin.write ? "read/write" : "read-only"); -+ -+ if (use_smbus == I2C_SMBUS_WORD_DATA || -+ use_smbus == I2C_SMBUS_BYTE_DATA) { -+ dev_notice(&client->dev, -+ "Falling back to %s reads, performance will suffer\n", -+ use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte"); ++ return err; + } + + return 0; -+ -+#ifdef EEPROM_CLASS -+err_sysfs_cleanup: -+ sysfs_remove_group(&client->dev.kobj, &optoe->attr_group); -+ sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin); -+#endif -+ -+err_struct: -+ if (num_addresses == 2) { -+ if (optoe->client[1]) -+ i2c_unregister_device(optoe->client[1]); -+ } -+ -+ kfree(optoe->writebuf); -+exit_kfree: -+ kfree(optoe); -+exit: -+ dev_dbg(&client->dev, "probe error %d\n", err); -+ -+ return err; +} + -+/*-------------------------------------------------------------------------*/ ++static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) ++{ ++ sysfs_remove_bin_file(kobj, eeprom); ++ return 0; ++} + -+static struct i2c_driver optoe_driver = { -+ .driver = { -+ .name = "optoe", -+ .owner = THIS_MODULE, -+ }, -+ .probe = optoe_probe, -+ .remove = optoe_remove, -+ .id_table = optoe_ids, ++static const struct attribute_group sfp_msa_group = { ++ .attrs = sfp_msa_attributes, +}; + -+static int __init optoe_init(void) ++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 ++} + -+ if (!io_limit) { -+ pr_err("optoe: io_limit must not be 0!\n"); -+ return -EINVAL; ++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; + } + -+ io_limit = rounddown_pow_of_two(io_limit); -+ return i2c_add_driver(&optoe_driver); -+} -+module_init(optoe_init); ++ msa = kzalloc(sizeof(struct sfp_msa_data), GFP_KERNEL); ++ if (!msa) { ++ status = -ENOMEM; ++ goto exit; ++ } + -+static void __exit optoe_exit(void) ++ /* 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) +{ -+ i2c_del_driver(&optoe_driver); -+} -+module_exit(optoe_exit); ++ int status; ++ struct sfp_ddm_data *ddm; + -+MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs"); -+MODULE_AUTHOR("DON BOLLINGER "); ++ 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; ++ } ++ ++ *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; ++} ++ ++/* Platform dependent +++ */ ++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 (client->addr != SFP_EEPROM_A0_I2C_ADDR && ++ client->addr != SFP_EEPROM_A2_I2C_ADDR ) { ++ return -ENODEV; ++ } ++ ++ if (dev_id->driver_data >= as4610_sfp1 && dev_id->driver_data <= as4610_sfp4) { ++ 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 if (dev_id->driver_data >= as4610_sfp5 && dev_id->driver_data <= as4610_sfp6) { ++ if (client->addr == SFP_EEPROM_A0_I2C_ADDR) { ++ data->driver_type = DRIVER_TYPE_QSFP; ++ return qsfp_probe(client, dev_id, &data->qsfp); ++ } ++ } ++ ++ return -ENODEV; ++} ++/* Platform dependent --- */ ++ ++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; ++} ++ ++/* Addresses scanned ++ */ ++static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; ++ ++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) ++{ ++ return i2c_add_driver(&sfp_driver); ++} ++ ++static void __exit sfp_exit(void) ++{ ++ i2c_del_driver(&sfp_driver); ++} ++ ++MODULE_AUTHOR("Brandon Chuang "); ++MODULE_DESCRIPTION("accton as4610_sfp driver"); +MODULE_LICENSE("GPL"); ++ ++late_initcall(sfp_init); ++module_exit(sfp_exit); ++ diff --git a/include/linux/accton_i2c_cpld.h b/include/linux/accton_i2c_cpld.h new file mode 100644 index 0000000..9b75abd --- /dev/null -+++ b/include/linux/accton_as4610_cpld.h ++++ b/include/linux/accton_i2c_cpld.h @@ -0,0 +1,76 @@ +/* -+ * A hwmon driver for the accton as4610 cpld ++ * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2016 Accton Technology Corporation. + * Brandon Chuang @@ -3908,8 +3874,8 @@ index 0000000..9b75abd + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + -+extern int as4610_54_cpld_read(unsigned short cpld_addr, u8 reg); -+extern int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); ++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 AS4610_CPLD_SLAVE_ADDR 0x30 +#define AS4610_CPLD_PID_OFFSET 0x01 /* Product ID offset */ @@ -3926,7 +3892,7 @@ index 0000000..9b75abd + +static inline int as4610_product_id(void) +{ -+ int pid = as4610_54_cpld_read(AS4610_CPLD_SLAVE_ADDR, AS4610_CPLD_PID_OFFSET); ++ int pid = accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, AS4610_CPLD_PID_OFFSET); + pid &= 0xF; + + if (pid < PID_AS4610_30T || pid > PID_AS4610_54T_B || pid == PID_RESERVED) { @@ -3963,3 +3929,4 @@ index 0000000..9b75abd + return nFan; +} + + diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-30/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-30/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts index 3226b312..0e7a33c0 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-30/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-30/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts @@ -139,8 +139,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <0>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "at,as4610_sfp1"; reg = <0x50>; label = "port49"; }; @@ -151,8 +151,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <1>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp2"; reg = <0x50>; label = "port50"; }; @@ -163,8 +163,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <2>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp3"; reg = <0x50>; label = "port51"; }; @@ -175,8 +175,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <3>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp4"; reg = <0x50>; label = "port52"; }; @@ -187,10 +187,9 @@ #address-cells = <1>; #size-cells = <0>; reg = <4>; - optoe@50 { - compatible = "optoe1"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp5"; reg = <0x50>; - label = "port53"; }; }; @@ -199,10 +198,9 @@ #address-cells = <1>; #size-cells = <0>; reg = <5>; - optoe@50 { - compatible = "optoe1"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp6"; reg = <0x50>; - label = "port54"; }; }; diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts index 3226b312..0e7a33c0 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/arm-accton-as4610-54-r0.dts @@ -139,8 +139,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <0>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "at,as4610_sfp1"; reg = <0x50>; label = "port49"; }; @@ -151,8 +151,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <1>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp2"; reg = <0x50>; label = "port50"; }; @@ -163,8 +163,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <2>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp3"; reg = <0x50>; label = "port51"; }; @@ -175,8 +175,8 @@ #address-cells = <1>; #size-cells = <0>; reg = <3>; - optoe@50 { - compatible = "optoe2"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp4"; reg = <0x50>; label = "port52"; }; @@ -187,10 +187,9 @@ #address-cells = <1>; #size-cells = <0>; reg = <4>; - optoe@50 { - compatible = "optoe1"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp5"; reg = <0x50>; - label = "port53"; }; }; @@ -199,10 +198,9 @@ #address-cells = <1>; #size-cells = <0>; reg = <5>; - optoe@50 { - compatible = "optoe1"; + sfp_eeprom@50 { + compatible = "accton,as4610_sfp6"; reg = <0x50>; - label = "port54"; }; }; diff --git a/packages/platforms/accton/armel/arm-accton-as4610/src/arm_accton_as4610/module/src/sfpi.c b/packages/platforms/accton/armel/arm-accton-as4610/src/arm_accton_as4610/module/src/sfpi.c index 41379f73..7199a31a 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/src/arm_accton_as4610/module/src/sfpi.c +++ b/packages/platforms/accton/armel/arm-accton-as4610/src/arm_accton_as4610/module/src/sfpi.c @@ -24,31 +24,50 @@ * ***********************************************************/ #include -#include #include "platform_lib.h" #include #include "arm_accton_as4610_log.h" -#define PORT_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom" -#define MODULE_PRESENT_FORMAT "/sys/bus/i2c/devices/0-0030/module_present_%d" -#define MODULE_RXLOS_FORMAT "/sys/bus/i2c/devices/0-0030/module_rx_los_%d" -#define MODULE_TXFAULT_FORMAT "/sys/bus/i2c/devices/0-0030/module_tx_fault_%d" -#define MODULE_PRESENT_ALL_ATTR_CPLD "/sys/bus/i2c/devices/0-0030/module_present_all" -#define MODULE_RXLOS_ALL_ATTR_CPLD "/sys/bus/i2c/devices/0-0030/module_rx_los_all" +#define MAX_SFP_PATH 64 +static char sfp_node_path[MAX_SFP_PATH] = {0}; +#define FRONT_PORT_MUX_INDEX(port) (port-46) -static int front_port_bus_index(int port) +static int +sfp_node_read_int(char *node_path, int *value, int data_len) { - return (platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0) ? - (port - 22) : /* PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0 */ - (port - 46) ; /* PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0 */ + int ret = 0; + char buf[8]; + *value = 0; + + ret = deviceNodeReadString(node_path, buf, sizeof(buf), data_len); + + if (ret == 0) { + *value = atoi(buf); + } + + return ret; } -static int front_port_to_cpld_port(int port) +static char* +sfp_get_port_path_addr(int port, int addr, char *node_name) { - return (platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0) ? - (port - 23) : /* PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0 */ - (port - 47) ; /* PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0 */ + int front_port_mux_id; + + if(platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0) + front_port_mux_id = port - 22; + else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/ + front_port_mux_id = port - 46; + + sprintf(sfp_node_path, "/sys/bus/i2c/devices/%d-00%d/%s", + front_port_mux_id, addr, node_name); + return sfp_node_path; +} + +static char* +sfp_get_port_path(int port, char *node_name) +{ + return sfp_get_port_path_addr(port, 50, node_name); } /************************************************************ @@ -96,9 +115,9 @@ onlp_sfpi_is_present(int port) * Return < 0 if error. */ int present; - int cpld_port = front_port_to_cpld_port(port); - - if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, cpld_port) < 0) { + char* path = sfp_get_port_path(port, "sfp_is_present"); + + if (sfp_node_read_int(path, &present, 0) != 0) { AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", port); return ONLP_STATUS_E_INTERNAL; } @@ -110,6 +129,7 @@ int onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst) { uint32_t byte; + char* path; FILE* fp; int port; @@ -120,18 +140,18 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst) else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/ port = 48; - /* Read present status of each port */ - fp = fopen(MODULE_PRESENT_ALL_ATTR_CPLD, "r"); + path = sfp_get_port_path(port, "sfp_is_present_all"); + fp = fopen(path, "r"); + if(fp == NULL) { - AIM_LOG_ERROR("Unable to open the module_present_all device file of CPLD."); + AIM_LOG_ERROR("Unable to open the sfp_is_present_all device file."); return ONLP_STATUS_E_INTERNAL; } - int count = fscanf(fp, "%x", &byte); fclose(fp); if(count != 1) { /* Likely a CPLD read timeout. */ - AIM_LOG_ERROR("Unable to read all fields the module_present_all device file of CPLD."); + AIM_LOG_ERROR("Unable to read all fields from the sfp_is_present_all device file."); return ONLP_STATUS_E_INTERNAL; } @@ -153,6 +173,7 @@ int onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) { uint32_t byte; + char* path; FILE* fp; int port; @@ -161,18 +182,16 @@ onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/ port = 48; - /* Read present status of each port */ - fp = fopen(MODULE_RXLOS_ALL_ATTR_CPLD, "r"); + path = sfp_get_port_path(port, "sfp_rx_los_all"); + fp = fopen(path, "r"); if(fp == NULL) { - AIM_LOG_ERROR("Unable to open the module_rx_los_all device file of CPLD."); + AIM_LOG_ERROR("Unable to open the sfp_rx_los_all device file."); return ONLP_STATUS_E_INTERNAL; } - int count = fscanf(fp, "%x", &byte); fclose(fp); if(count != 1) { - /* Likely a CPLD read timeout. */ - AIM_LOG_ERROR("Unable to read all fields the module_rx_los_all device file of CPLD."); + AIM_LOG_ERROR("Unable to read all fields from the sfp_rx_los_all device file."); return ONLP_STATUS_E_INTERNAL; } @@ -193,51 +212,32 @@ onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) int onlp_sfpi_eeprom_read(int port, uint8_t data[256]) { + char* path = sfp_get_port_path(port, "sfp_eeprom"); + /* * Read the SFP eeprom into data[] * * Return MISSING if SFP is missing. * Return OK if eeprom is read */ - int size = 0; memset(data, 0, 256); - if(onlp_file_read(data, 256, &size, PORT_EEPROM_FORMAT, front_port_bus_index(port)) != ONLP_STATUS_OK) { + if (deviceNodeReadBinary(path, (char*)data, 256, 256) != 0) { AIM_LOG_ERROR("Unable to read eeprom from port(%d)\r\n", port); return ONLP_STATUS_E_INTERNAL; } - if (size != 256) { - AIM_LOG_ERROR("Unable to read eeprom from port(%d), size is different!\r\n", port); - return ONLP_STATUS_E_INTERNAL; - } - return ONLP_STATUS_OK; } int onlp_sfpi_dom_read(int port, uint8_t data[256]) { - FILE* fp; - char file[64] = {0}; - - sprintf(file, PORT_EEPROM_FORMAT, front_port_bus_index(port)); - fp = fopen(file, "r"); - if(fp == NULL) { - AIM_LOG_ERROR("Unable to open the eeprom device file of port(%d)", port); - return ONLP_STATUS_E_INTERNAL; - } + char* path = sfp_get_port_path_addr(port, 51, "sfp_eeprom"); + memset(data, 0, 256); - if (fseek(fp, 256, SEEK_CUR) != 0) { - fclose(fp); - AIM_LOG_ERROR("Unable to set the file position indicator of port(%d)", port); - return ONLP_STATUS_E_INTERNAL; - } - - int ret = fread(data, 1, 256, fp); - fclose(fp); - if (ret != 256) { - AIM_LOG_ERROR("Unable to read the module_eeprom device file of port(%d)", port); + if (deviceNodeReadBinary(path, (char*)data, 256, 256) != 0) { + AIM_LOG_INFO("Unable to read eeprom from port(%d)\r\n", port); return ONLP_STATUS_E_INTERNAL; } @@ -247,24 +247,45 @@ onlp_sfpi_dom_read(int port, uint8_t data[256]) int onlp_sfpi_control_set(int port, onlp_sfp_control_t control, int value) { - return ONLP_STATUS_E_UNSUPPORTED; + int rv; + + switch(control) + { + case ONLP_SFP_CONTROL_TX_DISABLE: + { + char* path = sfp_get_port_path(port, "sfp_tx_disable"); + + if (deviceNodeWriteInt(path, value, 0) != 0) { + AIM_LOG_ERROR("Unable to set tx_disable status to port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + break; + } + + default: + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + return rv; } int onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) { int rv; - int cpld_port = front_port_to_cpld_port(port); - - if (cpld_port > 4) { - return ONLP_STATUS_E_UNSUPPORTED; - } + char* path = NULL; switch(control) { case ONLP_SFP_CONTROL_RX_LOS: { - if (onlp_file_read_int(value, MODULE_RXLOS_FORMAT, cpld_port) < 0) { + path = sfp_get_port_path(port, "sfp_rx_los"); + + if (sfp_node_read_int(path, value, 0) != 0) { AIM_LOG_ERROR("Unable to read rx_los status from port(%d)\r\n", port); rv = ONLP_STATUS_E_INTERNAL; } @@ -276,7 +297,9 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) case ONLP_SFP_CONTROL_TX_FAULT: { - if (onlp_file_read_int(value, MODULE_TXFAULT_FORMAT, cpld_port) < 0) { + path = sfp_get_port_path(port, "sfp_tx_fault"); + + if (sfp_node_read_int(path, value, 0) != 0) { AIM_LOG_ERROR("Unable to read tx_fault status from port(%d)\r\n", port); rv = ONLP_STATUS_E_INTERNAL; } @@ -286,6 +309,20 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) break; } + case ONLP_SFP_CONTROL_TX_DISABLE: + { + path = sfp_get_port_path(port, "sfp_tx_disable"); + + if (sfp_node_read_int(path, value, 0) != 0) { + AIM_LOG_ERROR("Unable to read tx_disabled status from port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + break; + } + default: rv = ONLP_STATUS_E_UNSUPPORTED; } @@ -293,9 +330,9 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) return rv; } + int onlp_sfpi_denit(void) { return ONLP_STATUS_OK; } -