diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/PKG.yml b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/PKG.yml index 0ddb1ebe..45fc38d0 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/PKG.yml +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/PKG.yml @@ -1 +1 @@ -!include $ONL_TEMPLATES/no-platform-modules.yml ARCH=armel VENDOR=accton BASENAME=arm-accton-as4610-54 +!include $ONL_TEMPLATES/platform-modules.yml ARCH=armel VENDOR=accton BASENAME=arm-accton-as4610-54 KERNELS="onl-kernel-4.14-lts-arm-iproc-all:armel" diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/.gitignore b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/.gitignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/Makefile b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/Makefile new file mode 100644 index 00000000..faed20dd --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/Makefile @@ -0,0 +1,7 @@ +KERNELS := onl-kernel-4.14-lts-arm-iproc-all:armel +KMODULES := $(wildcard *.c) +KINCLUDES := $(wildcard *.h) +VENDOR := accton +BASENAME := arm-accton-as4610-54 +ARCH := arm +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_fan.c b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_fan.c new file mode 100644 index 00000000..e84bbaf5 --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_fan.c @@ -0,0 +1,344 @@ +/* + * A hwmon driver for the Accton as4610 fan + * + * Copyright (C) 2016 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_i2c_cpld.h" + +#define DRVNAME "as4610_fan" + + +static struct as4610_fan_data *as4610_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +/* 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) */ + 0x2C, /* fan 2 speed(rpm) */ + 0x11, /* fan1-2 operating status */ +}; + +static struct as4610_fan_data *fan_data = NULL; + +/* Each client has this additional data */ +struct as4610_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */ +}; + +enum fan_id { + FAN1_ID, + FAN2_ID +}; + +enum sysfs_fan_attributes { + FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */ + FAN1_SPEED_RPM, + FAN2_SPEED_RPM, + FAN_FAULT, + FAN1_FAULT, + FAN2_FAULT +}; + +/* Define attributes + */ +#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT) +#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE) +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan##index##_duty_cycle_percentage.dev_attr.attr + +#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_SPEED_RPM) +#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_speed_rpm.dev_attr.attr + +/* fan fault attributes in this platform */ +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1); +DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2); +/* fan speed(rpm) attributes in this platform */ +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1); +DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2); +/* 1 fan duty cycle attribute in this platform */ +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(); + +static struct attribute *as4610_fan_attributes[] = { + /* fan related attributes */ + DECLARE_FAN_FAULT_ATTR(1), + DECLARE_FAN_FAULT_ATTR(2), + DECLARE_FAN_SPEED_RPM_ATTR(1), + DECLARE_FAN_SPEED_RPM_ATTR(2), + DECLARE_FAN_DUTY_CYCLE_ATTR(), + NULL +}; + +#define FAN_DUTY_CYCLE_REG_MASK 0xF +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100 + +static int as4610_fan_read_value(u8 reg) +{ + return accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, reg); +} + +static int as4610_fan_write_value(u8 reg, u8 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); +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + 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; +} + +static u8 is_fan_fault(struct as4610_fan_data *data, enum fan_id id) +{ + u8 mask = (id == FAN1_ID) ? 0x20 : 0x10; + + 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; + + error = kstrtoint(buf, 10, &value); + if (error) + return error; + + 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; +} + +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; + + 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; +} + +static const struct attribute_group as4610_fan_group = { + .attrs = as4610_fan_attributes, +}; + +static struct as4610_fan_data *as4610_fan_update_device(struct device *dev) +{ + mutex_lock(&fan_data->update_lock); + + 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; + + /* 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; + } + } + + fan_data->last_updated = jiffies; + fan_data->valid = 1; + } + + mutex_unlock(&fan_data->update_lock); + + return fan_data; +} + +static int as4610_fan_probe(struct platform_device *pdev) +{ + int status = -1; + + /* 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; + } + + dev_info(&pdev->dev, "accton_as4610_fan\n"); + + return 0; + +exit_remove: + sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group); +exit: + 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); + + return 0; +} + +static const struct i2c_device_id as4610_fan_id[] = { + { "as4610_fan", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as4610_fan_id); + +static struct platform_driver as4610_fan_driver = { + .probe = as4610_fan_probe, + .remove = as4610_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init as4610_fan_init(void) +{ + int ret; + + if (as4610_number_of_system_fan() == 0) { + return -ENODEV; + } + + 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; + } + + 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; + } + +exit: + return ret; +} + +static void __exit as4610_fan_exit(void) +{ + if (!fan_data) { + return; + } + + platform_device_unregister(fan_data->pdev); + platform_driver_unregister(&as4610_fan_driver); + kfree(fan_data); +} + +late_initcall(as4610_fan_init); +module_exit(as4610_fan_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as4610_fan driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_leds.c b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_leds.c new file mode 100644 index 00000000..cdc80779 --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_leds.c @@ -0,0 +1,674 @@ +/* + * A LED driver for the accton_as4610_led + * + * Copyright (C) 2016 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "accton_i2c_cpld.h" + +//extern void led_classdev_unregister(struct led_classdev *led_cdev); +//extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); + +#define DRVNAME "as4610_led" + +struct as4610_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + int led_map; + u8 reg_val[5]; /* Register value, 0 = (0x1A) Blinking function + 1 = (0x30) 7-seg 2 + 2 = (0x31) 7-seg 1 + 3 = (0x32) SYS/PRI/PSU1-2 LED + 4 = (0x33) STK1-2/Fan/PoE/Alarm LED */ +}; + +static struct as4610_led_data *ledctl = NULL; + +/* LED related data + */ +#define LED_7SEG_REG_MASK 0x0F +#define LED_7SEG_POINT_REG_MASK 0x10 + +#define LED_NORMAL_MASK 0x03 +#define LED_NORMAL_GREEN_VALUE 0x02 +#define LED_NORMAL_AMBER_VALUE 0x01 +#define LED_NORMAL_OFF_VALUE 0x00 + +#define LED_TYPE_SYS_REG_MASK 0xC0 +#define LED_MODE_SYS_BLINK_MASK 0x80 + +#define LED_TYPE_PRI_REG_MASK 0x30 +#define LED_MODE_PRI_BLINK_MASK 0x40 + +#define LED_TYPE_PSU1_REG_MASK 0x0C +#define LED_MODE_PSU1_BLINK_MASK 0x20 + +#define LED_TYPE_PSU2_REG_MASK 0x03 +#define LED_MODE_PSU2_BLINK_MASK 0x10 + +#define LED_TYPE_STK1_REG_MASK 0xC0 +#define LED_MODE_STK1_BLINK_MASK 0x08 + +#define LED_TYPE_STK2_REG_MASK 0x30 +#define LED_MODE_STK2_BLINK_MASK 0x04 + +#define LED_TYPE_FAN_REG_MASK 0x0C +#define LED_MODE_FAN_BLINK_MASK 0x02 + +#define LED_TYPE_POE_ALARM_REG_MASK 0x03 +#define LED_MODE_POE_ALARM_BLINK_MASK 0x01 + +static const u8 led_reg[] = { + 0x1A, /* Blinking function */ + 0x30, /* 7-seg 1 */ + 0x31, /* 7-seg 2 */ + 0x32, /* SYS/PRI/PSU1-2 LED */ + 0x33, /* STK1-2/Fan/PoE/Alarm LED */ +}; + +enum led_type { + LED_TYPE_SYS, + LED_TYPE_PRI, + LED_TYPE_PSU1, + LED_TYPE_PSU2, + LED_TYPE_STK1, + LED_TYPE_STK2, + LED_TYPE_7SEG_TENS, + LED_TYPE_7SEG_TENS_POINT, + LED_TYPE_7SEG_DIGITS, + LED_TYPE_7SEG_DIGITS_POINT, + LED_TYPE_FAN, + LED_TYPE_POE, + LED_TYPE_ALARM, + NUM_OF_LED +}; + +#define AS4610_COMMON_LED_MAP (BIT(LED_TYPE_SYS) | BIT(LED_TYPE_PRI) | BIT(LED_TYPE_PSU1) | \ + BIT(LED_TYPE_PSU2)| BIT(LED_TYPE_STK1)| BIT(LED_TYPE_STK2)) +#define AS4610_NPOE_LED_MAP (AS4610_COMMON_LED_MAP | BIT(LED_TYPE_7SEG_TENS) | \ + BIT(LED_TYPE_7SEG_TENS_POINT) | BIT(LED_TYPE_7SEG_DIGITS) | \ + BIT(LED_TYPE_7SEG_DIGITS_POINT)) +#define AS4610_POE_LED_MAP (AS4610_NPOE_LED_MAP | BIT(LED_TYPE_FAN) | BIT(LED_TYPE_POE)) +#define AS4610_54T_B_LED_MAP (AS4610_COMMON_LED_MAP | BIT(LED_TYPE_FAN) | BIT(LED_TYPE_ALARM)) + +static int as4610_ledmaps[] = { + [PID_AS4610_30T] = AS4610_NPOE_LED_MAP, + [PID_AS4610_30P] = AS4610_POE_LED_MAP, + [PID_AS4610_54T] = AS4610_NPOE_LED_MAP, + [PID_AS4610_54P] = AS4610_POE_LED_MAP, + [PID_AS4610_54T_B] = AS4610_54T_B_LED_MAP, + [PID_RESERVED] = 0, +}; + +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_GREEN, + LED_MODE_GREEN_BLINK, + LED_MODE_AMBER, + LED_MODE_AMBER_BLINK, + LED_MODE_RED, + LED_MODE_RED_BLINK, + LED_MODE_BLUE, + LED_MODE_BLUE_BLINK, + LED_MODE_AUTO, + LED_MODE_AUTO_BLINKING, + LED_MODE_UNKNOWN, + LED_MODE_SEVEN_SEGMENT_MAX = 9, +}; + +static int as4610_led_read_value(u8 reg) +{ + return accton_i2c_cpld_read(0x30, reg); +} + +static int as4610_led_write_value(u8 reg, u8 value) +{ + return accton_i2c_cpld_write(0x30, reg, value); +} + +static void as4610_led_update(void) +{ + mutex_lock(&ledctl->update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting as4610_led update\n"); + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = as4610_led_read_value(led_reg[i]); + + if (status < 0) { + ledctl->valid = 0; + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg[i], status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static enum led_brightness seven_segment_get(struct led_classdev *cdev, u8 reg_id) +{ + as4610_led_update(); + return (ledctl->reg_val[reg_id] & LED_7SEG_REG_MASK); +} + +static void seven_segment_set(struct led_classdev *cdev, enum led_brightness mode, u8 reg_id) +{ + if (mode > cdev->max_brightness) { + return; + } + + ledctl->reg_val[reg_id] &= 0xF0; + ledctl->reg_val[reg_id] |= mode; + as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]); +} + +static enum led_brightness seven_segment_digits_get(struct led_classdev *cdev) +{ + return seven_segment_get(cdev, 1); +} + +static void seven_segment_digits_set(struct led_classdev *cdev, enum led_brightness mode) +{ + seven_segment_set(cdev, mode, 1); +} + +static enum led_brightness seven_segment_tens_get(struct led_classdev *cdev) +{ + return seven_segment_get(cdev, 2); +} + +static void seven_segment_tens_set(struct led_classdev *cdev, enum led_brightness mode) +{ + seven_segment_set(cdev, mode, 2); +} + +static enum led_brightness seven_segment_point_get(struct led_classdev *cdev, u8 reg_id) +{ + as4610_led_update(); + return (ledctl->reg_val[reg_id] & LED_7SEG_POINT_REG_MASK) ? LED_MODE_GREEN : LED_MODE_OFF; +} + +static void seven_segment_point_set(struct led_classdev *cdev, + enum led_brightness mode, u8 reg_id) +{ + /* Validate brightness */ + if ((int)mode < LED_MODE_OFF || mode > cdev->max_brightness) { + return; + } + + if ((int)mode == (int)LED_MODE_OFF) { + ledctl->reg_val[reg_id] &= ~LED_7SEG_POINT_REG_MASK; + } + else { /* LED_MODE_GREEN */ + ledctl->reg_val[reg_id] |= LED_7SEG_POINT_REG_MASK; + } + + as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]); +} + +static enum led_brightness seven_segment_tens_point_get(struct led_classdev *cdev) +{ + return seven_segment_point_get(cdev, 2); +} + +static void seven_segment_tens_point_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + seven_segment_point_set(cdev, mode, 2); +} + +static enum led_brightness seven_segment_digits_point_get(struct led_classdev *cdev) +{ + return seven_segment_point_get(cdev, 1); +} + +static void seven_segment_digits_point_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + seven_segment_point_set(cdev, mode, 1); +} + +static u8 led_is_blinking_mode(enum led_brightness mode) +{ + return ((int)mode == (int)LED_MODE_GREEN_BLINK || + (int)mode == (int)LED_MODE_AMBER_BLINK || + (int)mode == (int)LED_MODE_AUTO_BLINKING); +} + +static enum led_brightness as4610_led_auto_get(u8 blink_mask) +{ + as4610_led_update(); + return (ledctl->reg_val[0] & blink_mask) ? LED_MODE_AUTO_BLINKING : LED_MODE_AUTO; +} + +static void as4610_led_auto_set(struct led_classdev *cdev, + enum led_brightness mode, u8 blink_mask) +{ + /* Validate brightness */ + if ((int)mode < (int)LED_MODE_AUTO || mode > cdev->max_brightness) { + return; + } + + /* Set blinking */ + if (led_is_blinking_mode(mode)) { + ledctl->reg_val[0] |= blink_mask; + } + else { + ledctl->reg_val[0] &= ~blink_mask; + } + as4610_led_write_value(led_reg[0], ledctl->reg_val[0]); +} + +static enum led_brightness as4610_led_psu1_get(struct led_classdev *cdev) +{ + return as4610_led_auto_get(LED_MODE_PSU1_BLINK_MASK); +} + +static void as4610_led_psu1_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_auto_set(cdev, mode, LED_MODE_PSU1_BLINK_MASK); +} + +static enum led_brightness as4610_led_psu2_get(struct led_classdev *cdev) +{ + return as4610_led_auto_get(LED_MODE_PSU2_BLINK_MASK); +} + +static void as4610_led_psu2_set(struct led_classdev *led_cdev, + enum led_brightness mode) +{ + as4610_led_auto_set(led_cdev, mode, LED_MODE_PSU2_BLINK_MASK); +} + +static enum led_brightness as4610_led_fan_get(struct led_classdev *cdev) +{ + return as4610_led_auto_get(LED_MODE_FAN_BLINK_MASK); +} + +static void as4610_led_fan_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_auto_set(cdev, mode, LED_MODE_FAN_BLINK_MASK); +} + +static u8 led_normal_light_mode_to_reg_val(enum led_brightness mode) +{ + if (led_is_blinking_mode(mode)) { + mode -= 1; /* convert blinking mode to non-blinking mode */ + } + + if ((int)mode == (int)LED_MODE_GREEN) { + return LED_NORMAL_GREEN_VALUE; + } + else if ((int)mode == (int)LED_MODE_AMBER) { + return LED_NORMAL_AMBER_VALUE; + } + + return LED_NORMAL_OFF_VALUE; +} + +static enum led_brightness led_normal_reg_val_to_light_mode(u8 reg_val) +{ + reg_val &= LED_NORMAL_MASK; + + if (reg_val & LED_NORMAL_GREEN_VALUE) { + return LED_MODE_GREEN; + } + else if (reg_val & LED_NORMAL_AMBER_VALUE) { + return LED_MODE_AMBER; + } + + return LED_MODE_OFF; +} + +static void as4610_led_normal_set(struct led_classdev *cdev, + enum led_brightness mode, u8 blink_mask, u8 reg_id, u8 reg_mask, u8 shift) +{ + /* Validate brightness */ + if (mode > cdev->max_brightness) { + return; + } + + /* Set blinking */ + if (led_is_blinking_mode(mode)) { + ledctl->reg_val[0] |= blink_mask; + } + else { + ledctl->reg_val[0] &= ~blink_mask; + } + as4610_led_write_value(led_reg[0], ledctl->reg_val[0]); + + /* Set color */ + ledctl->reg_val[reg_id] &= ~reg_mask; + ledctl->reg_val[reg_id] |= (led_normal_light_mode_to_reg_val(mode) << shift); + as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]); +} + +static enum led_brightness as4610_led_normal_get(u8 reg_id, u8 blink_mask, u8 shift) +{ + u8 blinking = 0; + enum led_brightness mode; + + as4610_led_update(); + + mode = led_normal_reg_val_to_light_mode(ledctl->reg_val[reg_id] >> shift); + if ((int)mode == (int)LED_MODE_OFF) { + return mode; + } + + /* Checking blinking */ + if (ledctl->reg_val[0] & blink_mask) { + blinking = 1; + } + + return blinking ? (mode+1) : mode; +} + +static void as4610_led_sys_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_normal_set(cdev, mode, LED_MODE_SYS_BLINK_MASK, + 3, LED_TYPE_SYS_REG_MASK, 6); +} + +static enum led_brightness as4610_led_sys_get(struct led_classdev *cdev) +{ + return as4610_led_normal_get(3, LED_MODE_SYS_BLINK_MASK, 6); +} + +static void as4610_led_pri_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_normal_set(cdev, mode, LED_MODE_PRI_BLINK_MASK, + 3, LED_TYPE_PRI_REG_MASK, 4); +} + +static enum led_brightness as4610_led_pri_get(struct led_classdev *cdev) +{ + return as4610_led_normal_get(3, LED_MODE_PRI_BLINK_MASK, 4); +} + +static void as4610_led_poe_alarm_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_normal_set(cdev, mode, LED_MODE_POE_ALARM_BLINK_MASK, + 4, LED_TYPE_POE_ALARM_REG_MASK, 0); +} + +static enum led_brightness as4610_led_poe_alarm_get(struct led_classdev *cdev) +{ + return as4610_led_normal_get(4, LED_MODE_POE_ALARM_BLINK_MASK, 0); +} + +static void as4610_led_stk1_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_normal_set(cdev, mode, LED_MODE_STK1_BLINK_MASK, + 4, LED_TYPE_STK1_REG_MASK, 6); +} + +static enum led_brightness as4610_led_stk1_get(struct led_classdev *cdev) +{ + return as4610_led_normal_get(4, LED_MODE_STK1_BLINK_MASK, 6); +} + +static void as4610_led_stk2_set(struct led_classdev *cdev, + enum led_brightness mode) +{ + as4610_led_normal_set(cdev, mode, LED_MODE_STK2_BLINK_MASK, + 4, LED_TYPE_STK2_REG_MASK, 4); +} + +static enum led_brightness as4610_led_stk2_get(struct led_classdev *cdev) +{ + return as4610_led_normal_get(4, LED_MODE_STK2_BLINK_MASK, 4); +} + +static struct led_classdev as4610_leds[] = { + [LED_TYPE_SYS] = { + .name = "as4610::sys", + .default_trigger = "unused", + .brightness_set = as4610_led_sys_set, + .brightness_get = as4610_led_sys_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, + [LED_TYPE_PRI] = { + .name = "as4610::pri", + .default_trigger = "unused", + .brightness_set = as4610_led_pri_set, + .brightness_get = as4610_led_pri_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, + [LED_TYPE_PSU1] = { + .name = "as4610::psu1", + .default_trigger = "unused", + .brightness_set = as4610_led_psu1_set, + .brightness_get = as4610_led_psu1_get, + .max_brightness = LED_MODE_AUTO_BLINKING, + }, + [LED_TYPE_PSU2] = { + .name = "as4610::psu2", + .default_trigger = "unused", + .brightness_set = as4610_led_psu2_set, + .brightness_get = as4610_led_psu2_get, + .max_brightness = LED_MODE_AUTO_BLINKING, + }, + [LED_TYPE_STK1] = { + .name = "as4610::stk1", + .default_trigger = "unused", + .brightness_set = as4610_led_stk1_set, + .brightness_get = as4610_led_stk1_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, + [LED_TYPE_STK2] = { + .name = "as4610::stk2", + .default_trigger = "unused", + .brightness_set = as4610_led_stk2_set, + .brightness_get = as4610_led_stk2_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, + [LED_TYPE_7SEG_TENS] = { + .name = "as4610::7seg_tens", + .default_trigger = "unused", + .brightness_set = seven_segment_tens_set, + .brightness_get = seven_segment_tens_get, + .max_brightness = LED_MODE_SEVEN_SEGMENT_MAX, + }, + [LED_TYPE_7SEG_TENS_POINT] = { + .name = "as4610::7seg_tens_point", + .default_trigger = "unused", + .brightness_set = seven_segment_tens_point_set, + .brightness_get = seven_segment_tens_point_get, + .max_brightness = LED_MODE_GREEN, + }, + [LED_TYPE_7SEG_DIGITS] = { + .name = "as4610::7seg_digits", + .default_trigger = "unused", + .brightness_set = seven_segment_digits_set, + .brightness_get = seven_segment_digits_get, + .max_brightness = LED_MODE_SEVEN_SEGMENT_MAX, + }, + [LED_TYPE_7SEG_DIGITS_POINT] = { + .name = "as4610::7seg_digits_point", + .default_trigger = "unused", + .brightness_set = seven_segment_digits_point_set, + .brightness_get = seven_segment_digits_point_get, + .max_brightness = LED_MODE_GREEN, + }, + [LED_TYPE_FAN] = { + .name = "as4610::fan", + .default_trigger = "unused", + .brightness_set = as4610_led_fan_set, + .brightness_get = as4610_led_fan_get, + .max_brightness = LED_MODE_AUTO_BLINKING, + }, + [LED_TYPE_POE] = { + .name = "as4610::poe", + .default_trigger = "unused", + .brightness_set = as4610_led_poe_alarm_set, + .brightness_get = as4610_led_poe_alarm_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, + [LED_TYPE_ALARM] = { + .name = "as4610::alarm", + .default_trigger = "unused", + .brightness_set = as4610_led_poe_alarm_set, + .brightness_get = as4610_led_poe_alarm_get, + .max_brightness = LED_MODE_AMBER_BLINK, + }, +}; + +static int as4610_led_probe(struct platform_device *pdev) +{ + int ret = 0, i; + + for (i = 0; i < NUM_OF_LED; i++) { + if (!(ledctl->led_map & BIT(i))) { + continue; + } + + ret = led_classdev_register(&pdev->dev, &as4610_leds[i]); + if (ret < 0) { + goto error; + } + } + + return 0; + +error: + for (i = i-1; i >= 0; i--) { + /* only unregister the LEDs that were successfully registered */ + if (!(ledctl->led_map & BIT(i))) { + continue; + } + + led_classdev_unregister(&as4610_leds[i]); + } + + return ret; +} + +static int as4610_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < NUM_OF_LED; i++) { + if (!(ledctl->led_map & BIT(i))) { + continue; + } + + led_classdev_unregister(&as4610_leds[i]); + } + + return 0; +} + +static struct platform_driver as4610_led_driver = { + .probe = as4610_led_probe, + .remove = as4610_led_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init as4610_led_init(void) +{ + int ret, pid; + + if (as4610_product_id() == PID_UNKNOWN) { + return -ENODEV; + } + + ret = platform_driver_register(&as4610_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct as4610_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&as4610_led_driver); + goto exit; + } + + pid = as4610_product_id(); + if (pid == PID_UNKNOWN) { + return -ENODEV; + } + + ledctl->led_map = as4610_ledmaps[pid]; + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&as4610_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit as4610_led_exit(void) +{ + if (!ledctl) { + return; + } + + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&as4610_led_driver); + kfree(ledctl); +} + +late_initcall(as4610_led_init); +module_exit(as4610_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as4610_led driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_psu.c b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_psu.c new file mode 100644 index 00000000..1f0d79dc --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_psu.c @@ -0,0 +1,286 @@ +/* + * An hwmon driver for accton as4610 Power Module + * + * Copyright (C) 2016 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf); +static int as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data,int data_len); +extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg); + +/* Addresses scanned + */ +static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END }; + +/* Each client has this additional data + */ +struct as4610_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 index; /* PSU index */ + u8 status; /* Status(present/power_good) register read from CPLD */ + char model_name[9]; /* Model name, read from eeprom */ +}; + +static struct as4610_psu_data *as4610_psu_update_device(struct device *dev); + +enum as4610_psu_sysfs_attributes { + PSU_PRESENT, + PSU_MODEL_NAME, + PSU_POWER_GOOD +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD); + +static struct attribute *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 +}; + +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; + + 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); +} + +static ssize_t show_model_name(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct as4610_psu_data *data = as4610_psu_update_device(dev); + + return sprintf(buf, "%s\n", data->model_name); +} + +static const struct attribute_group as4610_psu_group = { + .attrs = as4610_psu_attributes, +}; + +static int as4610_psu_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct as4610_psu_data *data; + int status; + + 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; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + data->index = dev_id->driver_data; + mutex_init(&data->update_lock); + + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &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; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as4610_psu_group); +exit_free: + kfree(data); +exit: + + return status; +} + +static int as4610_psu_remove(struct i2c_client *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); + + return 0; +} + +enum psu_index +{ + as4610_psu1, + as4610_psu2 +}; + +static const struct i2c_device_id as4610_psu_id[] = { + { "as4610_psu1", as4610_psu1 }, + { "as4610_psu2", as4610_psu2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, as4610_psu_id); + +static struct i2c_driver as4610_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "as4610_psu", + }, + .probe = as4610_psu_probe, + .remove = as4610_psu_remove, + .id_table = as4610_psu_id, + .address_list = normal_i2c, +}; + +static int as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data, + int count) +{ + int status = 0; + + while (count) { + status = i2c_smbus_read_byte_data(client, command); + if (unlikely(status < 0)) { + break; + } + + *data = (u8)status; + data += 1; + command += 1; + count -= 1; + } + + 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); + + mutex_lock(&data->update_lock); + + 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"); + + /* 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; + } + + /* 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; + + 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'; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +static int __init as4610_psu_init(void) +{ + return i2c_add_driver(&as4610_psu_driver); +} + +static void __exit as4610_psu_exit(void) +{ + i2c_del_driver(&as4610_psu_driver); +} + +module_init(as4610_psu_init); +module_exit(as4610_psu_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("as4610_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_sfp.c b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_sfp.c new file mode 100644 index 00000000..39c17ecf --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_as4610_sfp.c @@ -0,0 +1,1269 @@ +/* + * SFP driver for accton as4610 sfp + * + * Copyright (C) Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , DENX Software Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "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 + +#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 */ + +#define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) +#define SFP_EEPROM_A2_I2C_ADDR (0xA2 >> 1) + +#define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 +#define SFF8024_DEVICE_ID_SFP 0x3 +#define SFF8024_DEVICE_ID_QSFP 0xC +#define SFF8024_DEVICE_ID_QSFP_PLUS 0xD +#define SFF8024_DEVICE_ID_QSFP28 0x11 + +#define SFF8472_DIAG_MON_TYPE_ADDR 92 +#define SFF8472_DIAG_MON_TYPE_DDM_MASK 0x40 +#define SFF8472_10G_ETH_COMPLIANCE_ADDR 0x3 +#define SFF8472_10G_BASE_MASK 0xF0 + +#define SFF8436_RX_LOS_ADDR 3 +#define SFF8436_TX_FAULT_ADDR 4 +#define SFF8436_TX_DISABLE_ADDR 86 + +static ssize_t 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 +}; + +/* 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); + +/* 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 +}; + +/* 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 +}; + +/* 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 --- */ + +/* + * list of valid port types + * note OOM_PORT_TYPE_NOT_PRESENT to indicate no + * module is present in this port + */ +typedef enum oom_driver_port_type_e { + OOM_DRIVER_PORT_TYPE_INVALID, + OOM_DRIVER_PORT_TYPE_NOT_PRESENT, + OOM_DRIVER_PORT_TYPE_SFP, + OOM_DRIVER_PORT_TYPE_SFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP, + OOM_DRIVER_PORT_TYPE_QSFP_PLUS, + OOM_DRIVER_PORT_TYPE_QSFP28 +} oom_driver_port_type_t; + +enum driver_type_e { + DRIVER_TYPE_SFP_MSA, + DRIVER_TYPE_SFP_DDM, + DRIVER_TYPE_QSFP +}; + +/* Each client has this additional data + */ +struct eeprom_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + struct bin_attribute bin; /* eeprom data */ +}; + +struct sfp_msa_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u64 status[6]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss + 3 => device id + 4 => 10G Ethernet Compliance Codes + to distinguish SFP or SFP+ + 5 => DIAGNOSTIC MONITORING TYPE */ + struct eeprom_data eeprom; +}; + +struct sfp_ddm_data { + struct eeprom_data eeprom; +}; + +struct qsfp_data { + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 status[3]; /* bit0:port0, bit1:port1 and so on */ + /* index 0 => tx_fail + 1 => tx_disable + 2 => rx_loss */ + + u8 device_id; + struct eeprom_data eeprom; +}; + +struct sfp_port_data { + struct mutex update_lock; + enum driver_type_e driver_type; + int port; /* CPLD port index */ + oom_driver_port_type_t port_type; + u64 present; /* present status, bit0:port0, bit1:port1 and so on */ + + struct sfp_msa_data *msa; + struct sfp_ddm_data *ddm; + struct qsfp_data *qsfp; + + struct i2c_client *client; +}; + +static ssize_t show_port_number(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sfp_port_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "sfp %d\n", data->port); +} + +/* Platform dependent +++ */ +static struct sfp_port_data *sfp_update_present(struct i2c_client *client) +{ + struct sfp_port_data *data = i2c_get_clientdata(client); + int i = 0; + int status = -1; + u8 regs[] = {0x2, 0x3, 0x21}; + + DEBUG_PRINT("Starting sfp present status update"); + mutex_lock(&data->update_lock); + + /* Read present status of port 49~54 */ + data->present = 0; + + 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 TX_FAULT1: + case TX_FAULT2: + case TX_FAULT3: + case TX_FAULT4: + val = !!(data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)); + break; + 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: + 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; + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_write(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; +} + + +static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + 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)); + + 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_write(data, buf, off, count); +} + +static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ +#if USE_I2C_BLOCK_READ + int status, retry = I2C_RW_RETRY_COUNT; + + if (data_len > I2C_SMBUS_BLOCK_MAX) { + data_len = I2C_SMBUS_BLOCK_MAX; + } + + while (retry) { + status = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + goto abort; + } + if (unlikely(status != data_len)) { + status = -EIO; + goto abort; + } + + //result = data_len; + +abort: + return status; +#else + int status, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, command); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + if (unlikely(status < 0)) { + dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, 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(&data->update_lock); + + while (count) { + ssize_t status; + + status = sfp_eeprom_read(data->client, off, buf, count); + if (status <= 0) { + if (retval == 0) { + retval = status; + } + break; + } + + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&data->update_lock); + return retval; + +} + +static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + 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)); + + 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 int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +{ + int err; + + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IWUSR | S_IRUGO; + eeprom->read = sfp_bin_read; + eeprom->write = sfp_bin_write; + eeprom->size = EEPROM_SIZE; + + /* Create eeprom file */ + err = sysfs_create_bin_file(kobj, eeprom); + if (err) { + return err; + } + + return 0; +} + +static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); + return 0; +} + +static const struct attribute_group sfp_msa_group = { + .attrs = sfp_msa_attributes, +}; + +static int sfp_i2c_check_functionality(struct i2c_client *client) +{ +#if USE_I2C_BLOCK_READ + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); +#else + return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); +#endif +} + +static int sfp_msa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct sfp_msa_data **data) +{ + int status; + struct sfp_msa_data *msa; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + msa = kzalloc(sizeof(struct sfp_msa_data), GFP_KERNEL); + if (!msa) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &sfp_msa_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &msa->eeprom.bin); + if (status) { + goto exit_remove; + } + + *data = msa; + dev_info(&client->dev, "sfp msa '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &sfp_msa_group); +exit_free: + kfree(msa); +exit: + + return status; +} + +static const struct attribute_group sfp_ddm_group = { + .attrs = sfp_ddm_attributes, +}; + +static int sfp_ddm_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct sfp_ddm_data **data) +{ + int status; + struct sfp_ddm_data *ddm; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + ddm = kzalloc(sizeof(struct sfp_ddm_data), GFP_KERNEL); + if (!ddm) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &sfp_ddm_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &ddm->eeprom.bin); + if (status) { + goto exit_remove; + } + + *data = ddm; + dev_info(&client->dev, "sfp ddm '%s'\n", client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &sfp_ddm_group); +exit_free: + kfree(ddm); +exit: + + return status; +} + +static const struct attribute_group qsfp_group = { + .attrs = qsfp_attributes, +}; + +static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, + struct qsfp_data **data) +{ + int status; + struct qsfp_data *qsfp; + + if (!sfp_i2c_check_functionality(client)) { + status = -EIO; + goto exit; + } + + qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); + if (!qsfp) { + status = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &qsfp_group); + if (status) { + goto exit_free; + } + + /* init eeprom */ + status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); + if (status) { + goto exit_remove; + } + + *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/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.c b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.c new file mode 100644 index 00000000..a7140def --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.c @@ -0,0 +1,284 @@ +/* + * 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 "accton_i2c_cpld.h" + +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/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.h b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.h new file mode 100644 index 00000000..9b75abd9 --- /dev/null +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/modules/builds/accton_i2c_cpld.h @@ -0,0 +1,76 @@ +/* + * A hwmon driver for the accton_i2c_cpld + * + * Copyright (C) 2016 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +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 */ + +enum as4610_product_id_e { + PID_AS4610_30T, + PID_AS4610_30P, + PID_AS4610_54T, + PID_AS4610_54P, + PID_RESERVED, + PID_AS4610_54T_B, + PID_UNKNOWN +}; + +static inline int as4610_product_id(void) +{ + 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) { + return PID_UNKNOWN; + } + + return pid; +} + +static inline int as4610_is_poe_system(void) +{ + int pid = as4610_product_id(); + return (pid == PID_AS4610_30P || pid == PID_AS4610_54P); +} + +static inline int as4610_number_of_system_fan(void) +{ + int nFan = 0; + int pid = as4610_product_id(); + + switch (pid) { + case PID_AS4610_30P: + case PID_AS4610_54P: + nFan = 1; + break; + case PID_AS4610_54T_B: + nFan = 2; + break; + default: + nFan = 0; + break; + } + + return nFan; +} + diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/.gitignore b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/.gitignore index 7ac7a04f..f9450b8b 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/.gitignore +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/.gitignore @@ -1,2 +1,3 @@ *.dtb dts +include diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/Makefile b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/Makefile index 2956bc6e..85642cb1 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/Makefile +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/builds/dtb/Makefile @@ -1,10 +1,15 @@ +INCLUDES=include . +KERNEL := onl-kernel-4.14-lts-arm-iproc-all:armel +DTS_LIST := arm-accton-as4610.dts +VPATH := ../../../../../src include $(ONL)/make/dtbs.mk # # The 4610 DTS relies on the common arm devices tree includes. These are linked here from the kernel package. # setup:: - onlpm --link-dir onl-kernel-3.2-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-3.2-lts-arm-iproc-all/mbuilds/arch/arm/boot/dts dts + onlpm --link-dir onl-kernel-4.14-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-4.14-lts-arm-iproc-all/mbuilds/arch/arm/boot/dts dts + onlpm --link-dir onl-kernel-4.14-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-4.14-lts-arm-iproc-all/mbuilds/include include -clean:: - rm -rf fsl +setup-clean:: + #rm -rf dts include diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml index 542fac0c..b7c8d4ba 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/lib/arm-accton-as4610-54-r0.yml @@ -2,16 +2,16 @@ ###################################################################### # -# platform-config for AS4610 +# platform-config for AS4610-54 # ###################################################################### arm-accton-as4610-54-r0: flat_image_tree: kernel: - <<: *arm-iproc-kernel + <<: *arm-iproc-4-14-kernel dtb: - =: arm-accton-as4610-54-r0.dtb + =: arm-accton-as4610.dtb package: onl-platform-build-arm-accton-as4610-54-r0:armel itb: diff --git a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/python/arm_accton_as4610_54_r0/__init__.py b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/python/arm_accton_as4610_54_r0/__init__.py index a4cf2289..0991ab48 100644 --- a/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/python/arm_accton_as4610_54_r0/__init__.py +++ b/packages/platforms/accton/armel/arm-accton-as4610/arm-accton-as4610-54/platform-config/r0/src/python/arm_accton_as4610_54_r0/__init__.py @@ -6,3 +6,30 @@ class OnlPlatform_arm_accton_as4610_54_r0(OnlPlatformAccton, PLATFORM='arm-accton-as4610-54-r0' MODEL="AS4610-54" SYS_OBJECT_ID=".4610.54" + + def baseconfig(self): + self.insmod("accton_i2c_cpld") + self.new_i2c_device("as4610_54_cpld", 0x30, 0) + self.insmod("accton_as4610_sfp") + self.insmod("accton_as4610_psu") + self.insmod("accton_as4610_fan") + self.insmod("ym2651y") + self.new_i2c_devices( + [ + ("pca9548", 0x70, 1), + ("as4610_sfp1", 0x50, 2), + ("as4610_sfp2", 0x50, 3), + ("as4610_sfp3", 0x50, 4), + ("as4610_sfp4", 0x50, 5), + ("as4610_sfp5", 0x50, 6), + ("as4610_sfp6", 0x50, 7), + ("as4610_psu1", 0x50, 8), + ("as4610_psu2", 0x51, 8), + ("ym1921", 0x58, 8), + ("ym1921", 0x59, 8), + ("lm77", 0x48, 9), +# ("ds1307", 0x68, 9), + ("24c04", 0x50, 9), + ] + ) + return True