diff --git a/packages/base/any/kernels/6.12-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/6.12-lts/configs/x86_64-all/x86_64-all.config index dad49288b..489761704 100644 --- a/packages/base/any/kernels/6.12-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/6.12-lts/configs/x86_64-all/x86_64-all.config @@ -1392,7 +1392,7 @@ CONFIG_HOTPLUG_PCI=y # # PCI controller drivers # -# CONFIG_VMD is not set +CONFIG_VMD=y # # Cadence-based PCIe controllers diff --git a/packages/platforms/accton/x86-64/as1817-64o/Makefile b/packages/platforms/accton/x86-64/as1817-64o/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/Makefile b/packages/platforms/accton/x86-64/as1817-64o/modules/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/PKG.yml b/packages/platforms/accton/x86-64/as1817-64o/modules/PKG.yml new file mode 100644 index 000000000..151889c7b --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules.yml VENDOR=accton BASENAME=x86-64-accton-as1817-64o ARCH=amd64 KERNELS="onl-kernel-6.12-lts-x86-64-all:amd64" diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/.gitignore b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/.gitignore new file mode 100644 index 000000000..a65b41774 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/Makefile b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/Makefile new file mode 100644 index 000000000..a54d15b38 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/Makefile @@ -0,0 +1,6 @@ +KERNELS := onl-kernel-6.12-lts-x86-64-all:amd64 +KMODULES := src +VENDOR := accton +BASENAME := x86-64-accton-as1817-64o +ARCH := x86_64 +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/Makefile b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/Makefile new file mode 100644 index 000000000..2e4062c2c --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/Makefile @@ -0,0 +1,7 @@ +obj-m += x86-64-accton-as1817-64o-i2c-ocores.o +obj-m += x86-64-accton-as1817-64o-fpga.o +obj-m += x86-64-accton-as1817-64o-sys.o +obj-m += x86-64-accton-as1817-64o-thermal.o +obj-m += x86-64-accton-as1817-64o-fan.o +obj-m += x86-64-accton-as1817-64o-psu.o +obj-m += x86-64-accton-as1817-64o-leds.o diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fan.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fan.c new file mode 100644 index 000000000..cfdc6d443 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fan.c @@ -0,0 +1,455 @@ +/* + * A fan driver for accton as1817_64o via IPMI + * + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as1817_64o_fan" +#define ACCTON_IPMI_NETFN 0x34 +#define IPMI_FAN_READ_CMD 0x14 + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 + +#define NUM_OF_FAN_MODULE 8 +#define MAX_FAN_SPEED_FRONT 20500 +#define MAX_FAN_SPEED_REAR 21800 + +enum fan_data_index { + FAN_PRESENT, + FAN_PWM, + FAN_SPEED0, + FAN_SPEED1, + FAN_DATA_COUNT +}; + +/* IPMI response: 8 front rotors * 4 bytes + 8 rear rotors * 4 bytes + 2 bytes fan dir */ +#define FAN_RESP_SIZE (NUM_OF_FAN_MODULE * 2 * FAN_DATA_COUNT + 2) + +enum fan_sysfs_attrs { + FAN_ATTR_PRESENT, + FAN_ATTR_PWM, + FAN_ATTR_DIR, + FAN_ATTR_FRONT_INPUT, + FAN_ATTR_REAR_INPUT, + FAN_ATTR_FRONT_FAULT, + FAN_ATTR_REAR_FAULT, + FAN_ATTR_FRONT_MAX_SPEED, + FAN_ATTR_REAR_MAX_SPEED, + FAN_ATTR_FRONT_PERCENTAGE, + FAN_ATTR_REAR_PERCENTAGE, +}; + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static ssize_t show_fan(struct device *dev, struct device_attribute *da, char *buf); +static int as1817_64o_fan_probe(struct platform_device *pdev); +static void as1817_64o_fan_remove(struct platform_device *pdev); + +struct ipmi_data { + struct completion read_complete; + struct ipmi_addr address; + struct ipmi_user *user; + int interface; + struct kernel_ipmi_msg tx_message; + long tx_msgid; + void *rx_msg_data; + unsigned short rx_msg_len; + unsigned char rx_result; + int rx_recv_type; + struct ipmi_user_hndl ipmi_hndlrs; +}; + +struct as1817_64o_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; + unsigned long last_updated; + unsigned char ipmi_resp[FAN_RESP_SIZE]; + struct ipmi_data ipmi; +}; + +static struct as1817_64o_fan_data *data = NULL; + +static struct platform_driver as1817_64o_fan_driver = { + .probe = as1817_64o_fan_probe, + .remove = as1817_64o_fan_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +/* Each fan module index encodes: bits[7:4]=module(0-3), bits[3:0]=attr */ +#define FAN_MOD_ATTR(mod, attr) (((mod) << 4) | (attr)) +#define FAN_MOD_ID(index) ((index) >> 4) +#define FAN_MOD_ATTR_ID(index) ((index) & 0x0f) + +#define DECLARE_FAN_MODULE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_PRESENT)); \ + static SENSOR_DEVICE_ATTR(fan##index##_pwm, S_IRUGO, \ + show_fan, NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_PWM)); \ + static SENSOR_DEVICE_ATTR(fan##index##_dir, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_DIR)); \ + static SENSOR_DEVICE_ATTR(fan##index##_front_input, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_FRONT_INPUT)); \ + static SENSOR_DEVICE_ATTR(fan##index##_rear_input, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_REAR_INPUT)); \ + static SENSOR_DEVICE_ATTR(fan##index##_front_fault, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_FRONT_FAULT)); \ + static SENSOR_DEVICE_ATTR(fan##index##_rear_fault, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_REAR_FAULT)); \ + static SENSOR_DEVICE_ATTR(fan##index##_front_max_speed, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_FRONT_MAX_SPEED)); \ + static SENSOR_DEVICE_ATTR(fan##index##_rear_max_speed, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_REAR_MAX_SPEED)); \ + static SENSOR_DEVICE_ATTR(fan##index##_front_percentage, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_FRONT_PERCENTAGE)); \ + static SENSOR_DEVICE_ATTR(fan##index##_rear_percentage, S_IRUGO, show_fan, \ + NULL, FAN_MOD_ATTR((index)-1, FAN_ATTR_REAR_PERCENTAGE)) + +#define DECLARE_FAN_MODULE_SYSFS(index) \ + &sensor_dev_attr_fan##index##_present.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_pwm.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_dir.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_front_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_input.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_front_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_fault.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_front_max_speed.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_max_speed.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_front_percentage.dev_attr.attr, \ + &sensor_dev_attr_fan##index##_rear_percentage.dev_attr.attr + +DECLARE_FAN_MODULE_ATTR(1); +DECLARE_FAN_MODULE_ATTR(2); +DECLARE_FAN_MODULE_ATTR(3); +DECLARE_FAN_MODULE_ATTR(4); +DECLARE_FAN_MODULE_ATTR(5); +DECLARE_FAN_MODULE_ATTR(6); +DECLARE_FAN_MODULE_ATTR(7); +DECLARE_FAN_MODULE_ATTR(8); + +static struct attribute *as1817_64o_fan_attrs[] = { + DECLARE_FAN_MODULE_SYSFS(1), + DECLARE_FAN_MODULE_SYSFS(2), + DECLARE_FAN_MODULE_SYSFS(3), + DECLARE_FAN_MODULE_SYSFS(4), + DECLARE_FAN_MODULE_SYSFS(5), + DECLARE_FAN_MODULE_SYSFS(6), + DECLARE_FAN_MODULE_SYSFS(7), + DECLARE_FAN_MODULE_SYSFS(8), + NULL +}; +ATTRIBUTE_GROUPS(as1817_64o_fan); + +/* IPMI functions */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface, struct device *dev) +{ + int err; + + init_completion(&ipmi->read_complete); + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + dev_err(dev, "Unable to register user with IPMI interface %d\n", iface); + return -EACCES; + } + return 0; +} + +static int _ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err; + + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_data; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_data; + ipmi->rx_msg_len = rx_len; + + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) + return err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + return err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + return -ETIMEDOUT; + + return 0; +} + +static int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int status = 0, retry = 0; + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + status = _ipmi_send_message(ipmi, cmd, tx_data, tx_len, + rx_data, rx_len); + if (unlikely(status != 0)) + continue; + if (unlikely(ipmi->rx_result != 0)) + continue; + break; + } + return status; +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + if (msg->msgid != ipmi->tx_msgid) { + ipmi_free_recv_msg(msg); + return; + } + + ipmi->rx_recv_type = msg->recv_type; + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +static void as1817_64o_fan_update(void) +{ + int status; + + if (time_before(jiffies, data->last_updated + HZ * 5) && data->valid) + return; + + data->valid = 0; + status = ipmi_send_message(&data->ipmi, IPMI_FAN_READ_CMD, NULL, 0, + data->ipmi_resp, sizeof(data->ipmi_resp)); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->last_updated = jiffies; + data->valid = 1; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int mid = FAN_MOD_ID(attr->index); + int aid = FAN_MOD_ATTR_ID(attr->index); + int front_idx, rear_idx, value = 0, dir; + int error = 0; + + mutex_lock(&data->update_lock); + as1817_64o_fan_update(); + if (!data->valid) { + error = -EIO; + goto exit; + } + + /* Front fan data at offset mid*4, rear at (mid+4)*4 */ + front_idx = mid * FAN_DATA_COUNT; + rear_idx = (mid + NUM_OF_FAN_MODULE) * FAN_DATA_COUNT; + + switch (aid) { + case FAN_ATTR_PRESENT: + value = !!data->ipmi_resp[front_idx + FAN_PRESENT]; + break; + case FAN_ATTR_PWM: + value = data->ipmi_resp[front_idx + FAN_PWM]; + break; + case FAN_ATTR_DIR: + dir = data->ipmi_resp[NUM_OF_FAN_MODULE * 2 * FAN_DATA_COUNT] | + (data->ipmi_resp[NUM_OF_FAN_MODULE * 2 * FAN_DATA_COUNT + 1] << 8); + mutex_unlock(&data->update_lock); + return sprintf(buf, "%s\n", (dir & BIT(mid)) ? "B2F" : "F2B"); + case FAN_ATTR_FRONT_INPUT: + value = (int)data->ipmi_resp[front_idx + FAN_SPEED0] | + (int)data->ipmi_resp[front_idx + FAN_SPEED1] << 8; + break; + case FAN_ATTR_REAR_INPUT: + value = (int)data->ipmi_resp[rear_idx + FAN_SPEED0] | + (int)data->ipmi_resp[rear_idx + FAN_SPEED1] << 8; + break; + case FAN_ATTR_FRONT_FAULT: + if (!data->ipmi_resp[front_idx + FAN_PRESENT]) { + value = 1; + } else { + int rpm = (int)data->ipmi_resp[front_idx + FAN_SPEED0] | + (int)data->ipmi_resp[front_idx + FAN_SPEED1] << 8; + value = (rpm == 0) ? 1 : 0; + } + break; + case FAN_ATTR_REAR_FAULT: + if (!data->ipmi_resp[front_idx + FAN_PRESENT]) { + value = 1; + } else { + int rpm = (int)data->ipmi_resp[rear_idx + FAN_SPEED0] | + (int)data->ipmi_resp[rear_idx + FAN_SPEED1] << 8; + value = (rpm == 0) ? 1 : 0; + } + break; + case FAN_ATTR_FRONT_MAX_SPEED: + value = MAX_FAN_SPEED_FRONT; + break; + case FAN_ATTR_REAR_MAX_SPEED: + value = MAX_FAN_SPEED_REAR; + break; + case FAN_ATTR_FRONT_PERCENTAGE: + value = (int)data->ipmi_resp[front_idx + FAN_SPEED0] | + (int)data->ipmi_resp[front_idx + FAN_SPEED1] << 8; + value = DIV_ROUND_CLOSEST(value * 100, MAX_FAN_SPEED_FRONT); + if (value > 100) + value = 100; + break; + case FAN_ATTR_REAR_PERCENTAGE: + value = (int)data->ipmi_resp[rear_idx + FAN_SPEED0] | + (int)data->ipmi_resp[rear_idx + FAN_SPEED1] << 8; + value = DIV_ROUND_CLOSEST(value * 100, MAX_FAN_SPEED_REAR); + if (value > 100) + value = 100; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", value); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static int as1817_64o_fan_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + + hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + NULL, as1817_64o_fan_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + data->hwmon_dev = hwmon_dev; + dev_info(&pdev->dev, "device created\n"); + return 0; +} + +static void as1817_64o_fan_remove(struct platform_device *pdev) +{ + if (data->hwmon_dev) { + hwmon_device_unregister(data->hwmon_dev); + data->hwmon_dev = NULL; + } +} + +static int __init as1817_64o_fan_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as1817_64o_fan_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as1817_64o_fan_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) + goto ipmi_err; + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as1817_64o_fan_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as1817_64o_fan_exit(void) +{ + platform_device_unregister(data->pdev); + platform_driver_unregister(&as1817_64o_fan_driver); + ipmi_destroy_user(data->ipmi.user); + kfree(data); +} + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("as1817_64o_fan driver"); +MODULE_LICENSE("GPL"); + +module_init(as1817_64o_fan_init); +module_exit(as1817_64o_fan_exit); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fpga.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fpga.c new file mode 100644 index 000000000..5d5006fe7 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-fpga.c @@ -0,0 +1,1139 @@ +/* + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * This module supports the accton fpga via pcie that read/write reg + * mechanism to get OSFP/SFP status ...etc. + * This includes the: + * Accton as1817_64o FPGA + * + * Copyright (C) 2017 Finisar Corp. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include + +/*********************************************** + * variable define + * *********************************************/ +#define DRVNAME "as1817_64o_fpga" +#define OCORES_I2C_DRVNAME "ocores-i2c" + +#define PORT_NUM (64 + 2) /* 64 OSFPs/QSFPDDs + 2 SFP28s */ + +/* + * PCIE BAR0 address + */ +#define BAR0_NUM 0 +#define REGION_LEN 0xFF +#define FPGA_PCI_VENDOR_ID 0x10ee +#define FPGA_PCI_DEVICE_ID 0x7021 + +#define FPGA_PCIE_START_OFFSET 0x0000 +#define FPGA_MAJOR_VER_REG 0x01 +#define FPGA_MINOR_VER_REG 0x02 +#define SPI_BUSY_MASK_CPLD1 0x01 +#define SPI_BUSY_MASK_CPLD2 0x02 + +#define FPGA_PCIE_START_OFFSET 0x0000 +/* CPLD 1 */ +#define CPLD1_PCIE_START_OFFSET 0x2000 +#define CPLD1_MAJOR_VER_REG (CPLD1_PCIE_START_OFFSET + 0x00) +#define CPLD1_MINOR_VER_REG (CPLD1_PCIE_START_OFFSET + 0x01) +#define SFP_TXFAULT_REG (CPLD1_PCIE_START_OFFSET + 0x06) +#define SFP_TXDIS_REG (CPLD1_PCIE_START_OFFSET + 0x07) +#define SFP_RXLOSS_REG (CPLD1_PCIE_START_OFFSET + 0x08) +#define SFP_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x09) +#define XCVR_P7_P0_LPMODE_REG (CPLD1_PCIE_START_OFFSET + 0x70) +#define XCVR_P15_P8_LPMODE_REG (CPLD1_PCIE_START_OFFSET + 0x71) +#define XCVR_P23_P16_LPMODE_REG (CPLD1_PCIE_START_OFFSET + 0x72) +#define XCVR_P31_P24_LPMODE_REG (CPLD1_PCIE_START_OFFSET + 0x73) +#define XCVR_P7_P0_RESET_REG (CPLD1_PCIE_START_OFFSET + 0x78) +#define XCVR_P15_P8_RESET_REG (CPLD1_PCIE_START_OFFSET + 0x79) +#define XCVR_P23_P16_RESET_REG (CPLD1_PCIE_START_OFFSET + 0x7A) +#define XCVR_P31_P24_RESET_REG (CPLD1_PCIE_START_OFFSET + 0x7B) +#define XCVR_P7_P0_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x88) +#define XCVR_P15_P8_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x89) +#define XCVR_P23_P16_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x8A) +#define XCVR_P31_P24_PRESENT_REG (CPLD1_PCIE_START_OFFSET + 0x8B) +#define XCVR_P7_P0_PGOOD_REG (CPLD1_PCIE_START_OFFSET + 0x90) +#define XCVR_P15_P8_PGOOD_REG (CPLD1_PCIE_START_OFFSET + 0x91) +#define XCVR_P23_P16_PGOOD_REG (CPLD1_PCIE_START_OFFSET + 0x92) +#define XCVR_P31_P24_PGOOD_REG (CPLD1_PCIE_START_OFFSET + 0x93) +#define XCVR_P7_P0_INTR_REG (CPLD1_PCIE_START_OFFSET + 0x80) +#define XCVR_P15_P8_INTR_REG (CPLD1_PCIE_START_OFFSET + 0x81) +#define XCVR_P23_P16_INTR_REG (CPLD1_PCIE_START_OFFSET + 0x82) +#define XCVR_P31_P24_INTR_REG (CPLD1_PCIE_START_OFFSET + 0x83) + +/* CPLD 2 */ +#define CPLD2_PCIE_START_OFFSET 0x3000 +#define CPLD2_MAJOR_VER_REG (CPLD2_PCIE_START_OFFSET + 0x00) +#define CPLD2_MINOR_VER_REG (CPLD2_PCIE_START_OFFSET + 0x01) +#define XCVR_P39_P32_LPMODE_REG (CPLD2_PCIE_START_OFFSET + 0x70) +#define XCVR_P47_P40_LPMODE_REG (CPLD2_PCIE_START_OFFSET + 0x71) +#define XCVR_P55_P48_LPMODE_REG (CPLD2_PCIE_START_OFFSET + 0x72) +#define XCVR_P63_P56_LPMODE_REG (CPLD2_PCIE_START_OFFSET + 0x73) +#define XCVR_P39_P32_RESET_REG (CPLD2_PCIE_START_OFFSET + 0x78) +#define XCVR_P47_P40_RESET_REG (CPLD2_PCIE_START_OFFSET + 0x79) +#define XCVR_P55_P48_RESET_REG (CPLD2_PCIE_START_OFFSET + 0x7A) +#define XCVR_P63_P56_RESET_REG (CPLD2_PCIE_START_OFFSET + 0x7B) +#define XCVR_P39_P32_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x88) +#define XCVR_P47_P40_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x89) +#define XCVR_P55_P48_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x8A) +#define XCVR_P63_P56_PRESENT_REG (CPLD2_PCIE_START_OFFSET + 0x8B) +#define XCVR_P39_P32_PGOOD_REG (CPLD2_PCIE_START_OFFSET + 0x90) +#define XCVR_P47_P40_PGOOD_REG (CPLD2_PCIE_START_OFFSET + 0x91) +#define XCVR_P55_P48_PGOOD_REG (CPLD2_PCIE_START_OFFSET + 0x92) +#define XCVR_P63_P56_PGOOD_REG (CPLD2_PCIE_START_OFFSET + 0x93) +#define XCVR_P39_P32_INTR_REG (CPLD2_PCIE_START_OFFSET + 0x80) +#define XCVR_P47_P40_INTR_REG (CPLD2_PCIE_START_OFFSET + 0x81) +#define XCVR_P55_P48_INTR_REG (CPLD2_PCIE_START_OFFSET + 0x82) +#define XCVR_P63_P56_INTR_REG (CPLD2_PCIE_START_OFFSET + 0x83) + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_LPMODE_ATTR_ID(index) MODULE_LPMODE_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index +#define TRANSCEIVER_PGOOD_ATTR_ID(index) MODULE_PGOOD_##index +#define TRANSCEIVER_INTR_ATTR_ID(index) MODULE_INTR_##index +#define TRANSCEIVER_TX_DISABLE_ATTR_ID(index) MODULE_TX_DISABLE_##index +#define TRANSCEIVER_TX_FAULT_ATTR_ID(index) MODULE_TX_FAULT_##index +#define TRANSCEIVER_RX_LOS_ATTR_ID(index) MODULE_RX_LOS_##index + +/*********************************************** + * macro define + * *********************************************/ +#define pcie_err(fmt, args...) \ + printk(KERN_ERR "["DRVNAME"]: " fmt " ", ##args) + +#define pcie_info(fmt, args...) \ + printk(KERN_INFO "["DRVNAME"]: " fmt " ", ##args) + + +/*********************************************** + * structure & variable declare + * *********************************************/ +typedef struct pci_fpga_device_s { + void __iomem *data_base_addr0; + void __iomem *data_base_addr1; + void __iomem *data_base_addr2; + resource_size_t data_region1; + resource_size_t data_region2; + struct pci_dev *pci_dev; + struct platform_device *fpga_i2c[PORT_NUM]; +} pci_fpga_device_t; + +/*fpga port status*/ +struct as1817_64o_fpga_data { + struct mutex lock; + unsigned long last_updated; /* In jiffies */ + pci_fpga_device_t pci_fpga_dev; +}; + +static struct platform_device *pdev = NULL; + +/*********************************************** + * enum define + * *********************************************/ +enum fpga_sysfs_attributes { + /* 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_PRESENT_ATTR_ID(7), + TRANSCEIVER_PRESENT_ATTR_ID(8), + TRANSCEIVER_PRESENT_ATTR_ID(9), + TRANSCEIVER_PRESENT_ATTR_ID(10), + TRANSCEIVER_PRESENT_ATTR_ID(11), + TRANSCEIVER_PRESENT_ATTR_ID(12), + TRANSCEIVER_PRESENT_ATTR_ID(13), + TRANSCEIVER_PRESENT_ATTR_ID(14), + TRANSCEIVER_PRESENT_ATTR_ID(15), + TRANSCEIVER_PRESENT_ATTR_ID(16), + TRANSCEIVER_PRESENT_ATTR_ID(17), + TRANSCEIVER_PRESENT_ATTR_ID(18), + TRANSCEIVER_PRESENT_ATTR_ID(19), + TRANSCEIVER_PRESENT_ATTR_ID(20), + TRANSCEIVER_PRESENT_ATTR_ID(21), + TRANSCEIVER_PRESENT_ATTR_ID(22), + TRANSCEIVER_PRESENT_ATTR_ID(23), + TRANSCEIVER_PRESENT_ATTR_ID(24), + TRANSCEIVER_PRESENT_ATTR_ID(25), + TRANSCEIVER_PRESENT_ATTR_ID(26), + TRANSCEIVER_PRESENT_ATTR_ID(27), + TRANSCEIVER_PRESENT_ATTR_ID(28), + TRANSCEIVER_PRESENT_ATTR_ID(29), + TRANSCEIVER_PRESENT_ATTR_ID(30), + TRANSCEIVER_PRESENT_ATTR_ID(31), + TRANSCEIVER_PRESENT_ATTR_ID(32), + TRANSCEIVER_PRESENT_ATTR_ID(33), + TRANSCEIVER_PRESENT_ATTR_ID(34), + TRANSCEIVER_PRESENT_ATTR_ID(35), + TRANSCEIVER_PRESENT_ATTR_ID(36), + TRANSCEIVER_PRESENT_ATTR_ID(37), + TRANSCEIVER_PRESENT_ATTR_ID(38), + TRANSCEIVER_PRESENT_ATTR_ID(39), + TRANSCEIVER_PRESENT_ATTR_ID(40), + TRANSCEIVER_PRESENT_ATTR_ID(41), + TRANSCEIVER_PRESENT_ATTR_ID(42), + TRANSCEIVER_PRESENT_ATTR_ID(43), + TRANSCEIVER_PRESENT_ATTR_ID(44), + TRANSCEIVER_PRESENT_ATTR_ID(45), + TRANSCEIVER_PRESENT_ATTR_ID(46), + TRANSCEIVER_PRESENT_ATTR_ID(47), + TRANSCEIVER_PRESENT_ATTR_ID(48), + TRANSCEIVER_PRESENT_ATTR_ID(49), + TRANSCEIVER_PRESENT_ATTR_ID(50), + TRANSCEIVER_PRESENT_ATTR_ID(51), + TRANSCEIVER_PRESENT_ATTR_ID(52), + TRANSCEIVER_PRESENT_ATTR_ID(53), + TRANSCEIVER_PRESENT_ATTR_ID(54), + TRANSCEIVER_PRESENT_ATTR_ID(55), + TRANSCEIVER_PRESENT_ATTR_ID(56), + TRANSCEIVER_PRESENT_ATTR_ID(57), + TRANSCEIVER_PRESENT_ATTR_ID(58), + TRANSCEIVER_PRESENT_ATTR_ID(59), + TRANSCEIVER_PRESENT_ATTR_ID(60), + TRANSCEIVER_PRESENT_ATTR_ID(61), + TRANSCEIVER_PRESENT_ATTR_ID(62), + TRANSCEIVER_PRESENT_ATTR_ID(63), + TRANSCEIVER_PRESENT_ATTR_ID(64), + TRANSCEIVER_PRESENT_ATTR_ID(65), + TRANSCEIVER_PRESENT_ATTR_ID(66), + /*Reset*/ + TRANSCEIVER_RESET_ATTR_ID(1), + TRANSCEIVER_RESET_ATTR_ID(2), + TRANSCEIVER_RESET_ATTR_ID(3), + TRANSCEIVER_RESET_ATTR_ID(4), + TRANSCEIVER_RESET_ATTR_ID(5), + TRANSCEIVER_RESET_ATTR_ID(6), + TRANSCEIVER_RESET_ATTR_ID(7), + TRANSCEIVER_RESET_ATTR_ID(8), + TRANSCEIVER_RESET_ATTR_ID(9), + TRANSCEIVER_RESET_ATTR_ID(10), + TRANSCEIVER_RESET_ATTR_ID(11), + TRANSCEIVER_RESET_ATTR_ID(12), + TRANSCEIVER_RESET_ATTR_ID(13), + TRANSCEIVER_RESET_ATTR_ID(14), + TRANSCEIVER_RESET_ATTR_ID(15), + TRANSCEIVER_RESET_ATTR_ID(16), + TRANSCEIVER_RESET_ATTR_ID(17), + TRANSCEIVER_RESET_ATTR_ID(18), + TRANSCEIVER_RESET_ATTR_ID(19), + TRANSCEIVER_RESET_ATTR_ID(20), + TRANSCEIVER_RESET_ATTR_ID(21), + TRANSCEIVER_RESET_ATTR_ID(22), + TRANSCEIVER_RESET_ATTR_ID(23), + TRANSCEIVER_RESET_ATTR_ID(24), + TRANSCEIVER_RESET_ATTR_ID(25), + TRANSCEIVER_RESET_ATTR_ID(26), + TRANSCEIVER_RESET_ATTR_ID(27), + TRANSCEIVER_RESET_ATTR_ID(28), + TRANSCEIVER_RESET_ATTR_ID(29), + TRANSCEIVER_RESET_ATTR_ID(30), + TRANSCEIVER_RESET_ATTR_ID(31), + TRANSCEIVER_RESET_ATTR_ID(32), + TRANSCEIVER_RESET_ATTR_ID(33), + TRANSCEIVER_RESET_ATTR_ID(34), + TRANSCEIVER_RESET_ATTR_ID(35), + TRANSCEIVER_RESET_ATTR_ID(36), + TRANSCEIVER_RESET_ATTR_ID(37), + TRANSCEIVER_RESET_ATTR_ID(38), + TRANSCEIVER_RESET_ATTR_ID(39), + TRANSCEIVER_RESET_ATTR_ID(40), + TRANSCEIVER_RESET_ATTR_ID(41), + TRANSCEIVER_RESET_ATTR_ID(42), + TRANSCEIVER_RESET_ATTR_ID(43), + TRANSCEIVER_RESET_ATTR_ID(44), + TRANSCEIVER_RESET_ATTR_ID(45), + TRANSCEIVER_RESET_ATTR_ID(46), + TRANSCEIVER_RESET_ATTR_ID(47), + TRANSCEIVER_RESET_ATTR_ID(48), + TRANSCEIVER_RESET_ATTR_ID(49), + TRANSCEIVER_RESET_ATTR_ID(50), + TRANSCEIVER_RESET_ATTR_ID(51), + TRANSCEIVER_RESET_ATTR_ID(52), + TRANSCEIVER_RESET_ATTR_ID(53), + TRANSCEIVER_RESET_ATTR_ID(54), + TRANSCEIVER_RESET_ATTR_ID(55), + TRANSCEIVER_RESET_ATTR_ID(56), + TRANSCEIVER_RESET_ATTR_ID(57), + TRANSCEIVER_RESET_ATTR_ID(58), + TRANSCEIVER_RESET_ATTR_ID(59), + TRANSCEIVER_RESET_ATTR_ID(60), + TRANSCEIVER_RESET_ATTR_ID(61), + TRANSCEIVER_RESET_ATTR_ID(62), + TRANSCEIVER_RESET_ATTR_ID(63), + TRANSCEIVER_RESET_ATTR_ID(64), + TRANSCEIVER_LPMODE_ATTR_ID(1), + TRANSCEIVER_LPMODE_ATTR_ID(2), + TRANSCEIVER_LPMODE_ATTR_ID(3), + TRANSCEIVER_LPMODE_ATTR_ID(4), + TRANSCEIVER_LPMODE_ATTR_ID(5), + TRANSCEIVER_LPMODE_ATTR_ID(6), + TRANSCEIVER_LPMODE_ATTR_ID(7), + TRANSCEIVER_LPMODE_ATTR_ID(8), + TRANSCEIVER_LPMODE_ATTR_ID(9), + TRANSCEIVER_LPMODE_ATTR_ID(10), + TRANSCEIVER_LPMODE_ATTR_ID(11), + TRANSCEIVER_LPMODE_ATTR_ID(12), + TRANSCEIVER_LPMODE_ATTR_ID(13), + TRANSCEIVER_LPMODE_ATTR_ID(14), + TRANSCEIVER_LPMODE_ATTR_ID(15), + TRANSCEIVER_LPMODE_ATTR_ID(16), + TRANSCEIVER_LPMODE_ATTR_ID(17), + TRANSCEIVER_LPMODE_ATTR_ID(18), + TRANSCEIVER_LPMODE_ATTR_ID(19), + TRANSCEIVER_LPMODE_ATTR_ID(20), + TRANSCEIVER_LPMODE_ATTR_ID(21), + TRANSCEIVER_LPMODE_ATTR_ID(22), + TRANSCEIVER_LPMODE_ATTR_ID(23), + TRANSCEIVER_LPMODE_ATTR_ID(24), + TRANSCEIVER_LPMODE_ATTR_ID(25), + TRANSCEIVER_LPMODE_ATTR_ID(26), + TRANSCEIVER_LPMODE_ATTR_ID(27), + TRANSCEIVER_LPMODE_ATTR_ID(28), + TRANSCEIVER_LPMODE_ATTR_ID(29), + TRANSCEIVER_LPMODE_ATTR_ID(30), + TRANSCEIVER_LPMODE_ATTR_ID(31), + TRANSCEIVER_LPMODE_ATTR_ID(32), + TRANSCEIVER_LPMODE_ATTR_ID(33), + TRANSCEIVER_LPMODE_ATTR_ID(34), + TRANSCEIVER_LPMODE_ATTR_ID(35), + TRANSCEIVER_LPMODE_ATTR_ID(36), + TRANSCEIVER_LPMODE_ATTR_ID(37), + TRANSCEIVER_LPMODE_ATTR_ID(38), + TRANSCEIVER_LPMODE_ATTR_ID(39), + TRANSCEIVER_LPMODE_ATTR_ID(40), + TRANSCEIVER_LPMODE_ATTR_ID(41), + TRANSCEIVER_LPMODE_ATTR_ID(42), + TRANSCEIVER_LPMODE_ATTR_ID(43), + TRANSCEIVER_LPMODE_ATTR_ID(44), + TRANSCEIVER_LPMODE_ATTR_ID(45), + TRANSCEIVER_LPMODE_ATTR_ID(46), + TRANSCEIVER_LPMODE_ATTR_ID(47), + TRANSCEIVER_LPMODE_ATTR_ID(48), + TRANSCEIVER_LPMODE_ATTR_ID(49), + TRANSCEIVER_LPMODE_ATTR_ID(50), + TRANSCEIVER_LPMODE_ATTR_ID(51), + TRANSCEIVER_LPMODE_ATTR_ID(52), + TRANSCEIVER_LPMODE_ATTR_ID(53), + TRANSCEIVER_LPMODE_ATTR_ID(54), + TRANSCEIVER_LPMODE_ATTR_ID(55), + TRANSCEIVER_LPMODE_ATTR_ID(56), + TRANSCEIVER_LPMODE_ATTR_ID(57), + TRANSCEIVER_LPMODE_ATTR_ID(58), + TRANSCEIVER_LPMODE_ATTR_ID(59), + TRANSCEIVER_LPMODE_ATTR_ID(60), + TRANSCEIVER_LPMODE_ATTR_ID(61), + TRANSCEIVER_LPMODE_ATTR_ID(62), + TRANSCEIVER_LPMODE_ATTR_ID(63), + TRANSCEIVER_LPMODE_ATTR_ID(64), + TRANSCEIVER_PGOOD_ATTR_ID(1), + TRANSCEIVER_PGOOD_ATTR_ID(2), + TRANSCEIVER_PGOOD_ATTR_ID(3), + TRANSCEIVER_PGOOD_ATTR_ID(4), + TRANSCEIVER_PGOOD_ATTR_ID(5), + TRANSCEIVER_PGOOD_ATTR_ID(6), + TRANSCEIVER_PGOOD_ATTR_ID(7), + TRANSCEIVER_PGOOD_ATTR_ID(8), + TRANSCEIVER_PGOOD_ATTR_ID(9), + TRANSCEIVER_PGOOD_ATTR_ID(10), + TRANSCEIVER_PGOOD_ATTR_ID(11), + TRANSCEIVER_PGOOD_ATTR_ID(12), + TRANSCEIVER_PGOOD_ATTR_ID(13), + TRANSCEIVER_PGOOD_ATTR_ID(14), + TRANSCEIVER_PGOOD_ATTR_ID(15), + TRANSCEIVER_PGOOD_ATTR_ID(16), + TRANSCEIVER_PGOOD_ATTR_ID(17), + TRANSCEIVER_PGOOD_ATTR_ID(18), + TRANSCEIVER_PGOOD_ATTR_ID(19), + TRANSCEIVER_PGOOD_ATTR_ID(20), + TRANSCEIVER_PGOOD_ATTR_ID(21), + TRANSCEIVER_PGOOD_ATTR_ID(22), + TRANSCEIVER_PGOOD_ATTR_ID(23), + TRANSCEIVER_PGOOD_ATTR_ID(24), + TRANSCEIVER_PGOOD_ATTR_ID(25), + TRANSCEIVER_PGOOD_ATTR_ID(26), + TRANSCEIVER_PGOOD_ATTR_ID(27), + TRANSCEIVER_PGOOD_ATTR_ID(28), + TRANSCEIVER_PGOOD_ATTR_ID(29), + TRANSCEIVER_PGOOD_ATTR_ID(30), + TRANSCEIVER_PGOOD_ATTR_ID(31), + TRANSCEIVER_PGOOD_ATTR_ID(32), + TRANSCEIVER_PGOOD_ATTR_ID(33), + TRANSCEIVER_PGOOD_ATTR_ID(34), + TRANSCEIVER_PGOOD_ATTR_ID(35), + TRANSCEIVER_PGOOD_ATTR_ID(36), + TRANSCEIVER_PGOOD_ATTR_ID(37), + TRANSCEIVER_PGOOD_ATTR_ID(38), + TRANSCEIVER_PGOOD_ATTR_ID(39), + TRANSCEIVER_PGOOD_ATTR_ID(40), + TRANSCEIVER_PGOOD_ATTR_ID(41), + TRANSCEIVER_PGOOD_ATTR_ID(42), + TRANSCEIVER_PGOOD_ATTR_ID(43), + TRANSCEIVER_PGOOD_ATTR_ID(44), + TRANSCEIVER_PGOOD_ATTR_ID(45), + TRANSCEIVER_PGOOD_ATTR_ID(46), + TRANSCEIVER_PGOOD_ATTR_ID(47), + TRANSCEIVER_PGOOD_ATTR_ID(48), + TRANSCEIVER_PGOOD_ATTR_ID(49), + TRANSCEIVER_PGOOD_ATTR_ID(50), + TRANSCEIVER_PGOOD_ATTR_ID(51), + TRANSCEIVER_PGOOD_ATTR_ID(52), + TRANSCEIVER_PGOOD_ATTR_ID(53), + TRANSCEIVER_PGOOD_ATTR_ID(54), + TRANSCEIVER_PGOOD_ATTR_ID(55), + TRANSCEIVER_PGOOD_ATTR_ID(56), + TRANSCEIVER_PGOOD_ATTR_ID(57), + TRANSCEIVER_PGOOD_ATTR_ID(58), + TRANSCEIVER_PGOOD_ATTR_ID(59), + TRANSCEIVER_PGOOD_ATTR_ID(60), + TRANSCEIVER_PGOOD_ATTR_ID(61), + TRANSCEIVER_PGOOD_ATTR_ID(62), + TRANSCEIVER_PGOOD_ATTR_ID(63), + TRANSCEIVER_PGOOD_ATTR_ID(64), + TRANSCEIVER_INTR_ATTR_ID(1), + TRANSCEIVER_INTR_ATTR_ID(2), + TRANSCEIVER_INTR_ATTR_ID(3), + TRANSCEIVER_INTR_ATTR_ID(4), + TRANSCEIVER_INTR_ATTR_ID(5), + TRANSCEIVER_INTR_ATTR_ID(6), + TRANSCEIVER_INTR_ATTR_ID(7), + TRANSCEIVER_INTR_ATTR_ID(8), + TRANSCEIVER_INTR_ATTR_ID(9), + TRANSCEIVER_INTR_ATTR_ID(10), + TRANSCEIVER_INTR_ATTR_ID(11), + TRANSCEIVER_INTR_ATTR_ID(12), + TRANSCEIVER_INTR_ATTR_ID(13), + TRANSCEIVER_INTR_ATTR_ID(14), + TRANSCEIVER_INTR_ATTR_ID(15), + TRANSCEIVER_INTR_ATTR_ID(16), + TRANSCEIVER_INTR_ATTR_ID(17), + TRANSCEIVER_INTR_ATTR_ID(18), + TRANSCEIVER_INTR_ATTR_ID(19), + TRANSCEIVER_INTR_ATTR_ID(20), + TRANSCEIVER_INTR_ATTR_ID(21), + TRANSCEIVER_INTR_ATTR_ID(22), + TRANSCEIVER_INTR_ATTR_ID(23), + TRANSCEIVER_INTR_ATTR_ID(24), + TRANSCEIVER_INTR_ATTR_ID(25), + TRANSCEIVER_INTR_ATTR_ID(26), + TRANSCEIVER_INTR_ATTR_ID(27), + TRANSCEIVER_INTR_ATTR_ID(28), + TRANSCEIVER_INTR_ATTR_ID(29), + TRANSCEIVER_INTR_ATTR_ID(30), + TRANSCEIVER_INTR_ATTR_ID(31), + TRANSCEIVER_INTR_ATTR_ID(32), + TRANSCEIVER_INTR_ATTR_ID(33), + TRANSCEIVER_INTR_ATTR_ID(34), + TRANSCEIVER_INTR_ATTR_ID(35), + TRANSCEIVER_INTR_ATTR_ID(36), + TRANSCEIVER_INTR_ATTR_ID(37), + TRANSCEIVER_INTR_ATTR_ID(38), + TRANSCEIVER_INTR_ATTR_ID(39), + TRANSCEIVER_INTR_ATTR_ID(40), + TRANSCEIVER_INTR_ATTR_ID(41), + TRANSCEIVER_INTR_ATTR_ID(42), + TRANSCEIVER_INTR_ATTR_ID(43), + TRANSCEIVER_INTR_ATTR_ID(44), + TRANSCEIVER_INTR_ATTR_ID(45), + TRANSCEIVER_INTR_ATTR_ID(46), + TRANSCEIVER_INTR_ATTR_ID(47), + TRANSCEIVER_INTR_ATTR_ID(48), + TRANSCEIVER_INTR_ATTR_ID(49), + TRANSCEIVER_INTR_ATTR_ID(50), + TRANSCEIVER_INTR_ATTR_ID(51), + TRANSCEIVER_INTR_ATTR_ID(52), + TRANSCEIVER_INTR_ATTR_ID(53), + TRANSCEIVER_INTR_ATTR_ID(54), + TRANSCEIVER_INTR_ATTR_ID(55), + TRANSCEIVER_INTR_ATTR_ID(56), + TRANSCEIVER_INTR_ATTR_ID(57), + TRANSCEIVER_INTR_ATTR_ID(58), + TRANSCEIVER_INTR_ATTR_ID(59), + TRANSCEIVER_INTR_ATTR_ID(60), + TRANSCEIVER_INTR_ATTR_ID(61), + TRANSCEIVER_INTR_ATTR_ID(62), + TRANSCEIVER_INTR_ATTR_ID(63), + TRANSCEIVER_INTR_ATTR_ID(64), + TRANSCEIVER_TX_DISABLE_ATTR_ID(65), + TRANSCEIVER_TX_DISABLE_ATTR_ID(66), + TRANSCEIVER_TX_FAULT_ATTR_ID(65), + TRANSCEIVER_TX_FAULT_ATTR_ID(66), + TRANSCEIVER_RX_LOS_ATTR_ID(65), + TRANSCEIVER_RX_LOS_ATTR_ID(66), + /* MODULE_RESET_ALL, */ +}; + + +/*********************************************** + * function declare + * *********************************************/ +static ssize_t status_read(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t status_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, status_read, NULL, MODULE_PRESENT_##index); \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_RESET_##index); \ + static SENSOR_DEVICE_ATTR(module_lp_mode_##index, S_IRUGO|S_IWUSR, status_read, status_write, MODULE_LPMODE_##index); \ + static SENSOR_DEVICE_ATTR(module_power_good_##index, S_IRUGO, status_read, NULL, MODULE_PGOOD_##index); \ + static SENSOR_DEVICE_ATTR(module_interrupt_##index, S_IRUGO, status_read, NULL, MODULE_INTR_##index) +#define DECLARE_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_present_##index.dev_attr.attr, \ + &sensor_dev_attr_module_reset_##index.dev_attr.attr, \ + &sensor_dev_attr_module_lp_mode_##index.dev_attr.attr, \ + &sensor_dev_attr_module_power_good_##index.dev_attr.attr, \ + &sensor_dev_attr_module_interrupt_##index.dev_attr.attr + +/* transceiver attributes */ +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(49); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(50); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(51); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(52); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(53); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(54); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(55); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(56); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(57); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(58); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(59); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(60); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(61); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(62); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(63); +DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(64); +static SENSOR_DEVICE_ATTR(module_present_65, S_IRUGO, status_read, NULL, MODULE_PRESENT_65); +static SENSOR_DEVICE_ATTR(module_present_66, S_IRUGO, status_read, NULL, MODULE_PRESENT_66); +//static SENSOR_DEVICE_ATTR(module_reset_all, S_IWUSR, NULL, status_write, MODULE_RESET_ALL); +static SENSOR_DEVICE_ATTR(module_tx_disable_65, S_IRUGO|S_IWUSR, status_read, + status_write, MODULE_TX_DISABLE_65); +static SENSOR_DEVICE_ATTR(module_tx_disable_66, S_IRUGO|S_IWUSR, status_read, + status_write, MODULE_TX_DISABLE_66); +static SENSOR_DEVICE_ATTR(module_tx_fault_65, S_IRUGO, status_read, NULL, MODULE_TX_FAULT_65); +static SENSOR_DEVICE_ATTR(module_tx_fault_66, S_IRUGO, status_read, NULL, MODULE_TX_FAULT_66); +static SENSOR_DEVICE_ATTR(module_rx_los_65, S_IRUGO, status_read, NULL, MODULE_RX_LOS_65); +static SENSOR_DEVICE_ATTR(module_rx_los_66, S_IRUGO, status_read, NULL, MODULE_RX_LOS_66); + + + +static struct attribute *fpga_transceiver_attributes[] = { + DECLARE_TRANSCEIVER_ATTR(1), + DECLARE_TRANSCEIVER_ATTR(2), + DECLARE_TRANSCEIVER_ATTR(3), + DECLARE_TRANSCEIVER_ATTR(4), + DECLARE_TRANSCEIVER_ATTR(5), + DECLARE_TRANSCEIVER_ATTR(6), + DECLARE_TRANSCEIVER_ATTR(7), + DECLARE_TRANSCEIVER_ATTR(8), + DECLARE_TRANSCEIVER_ATTR(9), + DECLARE_TRANSCEIVER_ATTR(10), + DECLARE_TRANSCEIVER_ATTR(11), + DECLARE_TRANSCEIVER_ATTR(12), + DECLARE_TRANSCEIVER_ATTR(13), + DECLARE_TRANSCEIVER_ATTR(14), + DECLARE_TRANSCEIVER_ATTR(15), + DECLARE_TRANSCEIVER_ATTR(16), + DECLARE_TRANSCEIVER_ATTR(17), + DECLARE_TRANSCEIVER_ATTR(18), + DECLARE_TRANSCEIVER_ATTR(19), + DECLARE_TRANSCEIVER_ATTR(20), + DECLARE_TRANSCEIVER_ATTR(21), + DECLARE_TRANSCEIVER_ATTR(22), + DECLARE_TRANSCEIVER_ATTR(23), + DECLARE_TRANSCEIVER_ATTR(24), + DECLARE_TRANSCEIVER_ATTR(25), + DECLARE_TRANSCEIVER_ATTR(26), + DECLARE_TRANSCEIVER_ATTR(27), + DECLARE_TRANSCEIVER_ATTR(28), + DECLARE_TRANSCEIVER_ATTR(29), + DECLARE_TRANSCEIVER_ATTR(30), + DECLARE_TRANSCEIVER_ATTR(31), + DECLARE_TRANSCEIVER_ATTR(32), + DECLARE_TRANSCEIVER_ATTR(33), + DECLARE_TRANSCEIVER_ATTR(34), + DECLARE_TRANSCEIVER_ATTR(35), + DECLARE_TRANSCEIVER_ATTR(36), + DECLARE_TRANSCEIVER_ATTR(37), + DECLARE_TRANSCEIVER_ATTR(38), + DECLARE_TRANSCEIVER_ATTR(39), + DECLARE_TRANSCEIVER_ATTR(40), + DECLARE_TRANSCEIVER_ATTR(41), + DECLARE_TRANSCEIVER_ATTR(42), + DECLARE_TRANSCEIVER_ATTR(43), + DECLARE_TRANSCEIVER_ATTR(44), + DECLARE_TRANSCEIVER_ATTR(45), + DECLARE_TRANSCEIVER_ATTR(46), + DECLARE_TRANSCEIVER_ATTR(47), + DECLARE_TRANSCEIVER_ATTR(48), + DECLARE_TRANSCEIVER_ATTR(49), + DECLARE_TRANSCEIVER_ATTR(50), + DECLARE_TRANSCEIVER_ATTR(51), + DECLARE_TRANSCEIVER_ATTR(52), + DECLARE_TRANSCEIVER_ATTR(53), + DECLARE_TRANSCEIVER_ATTR(54), + DECLARE_TRANSCEIVER_ATTR(55), + DECLARE_TRANSCEIVER_ATTR(56), + DECLARE_TRANSCEIVER_ATTR(57), + DECLARE_TRANSCEIVER_ATTR(58), + DECLARE_TRANSCEIVER_ATTR(59), + DECLARE_TRANSCEIVER_ATTR(60), + DECLARE_TRANSCEIVER_ATTR(61), + DECLARE_TRANSCEIVER_ATTR(62), + DECLARE_TRANSCEIVER_ATTR(63), + DECLARE_TRANSCEIVER_ATTR(64), + &sensor_dev_attr_module_present_65.dev_attr.attr, + &sensor_dev_attr_module_present_66.dev_attr.attr, + /* &sensor_dev_attr_module_reset_all.dev_attr.attr,*/ + &sensor_dev_attr_module_tx_disable_65.dev_attr.attr, + &sensor_dev_attr_module_tx_disable_66.dev_attr.attr, + &sensor_dev_attr_module_tx_fault_65.dev_attr.attr, + &sensor_dev_attr_module_tx_fault_66.dev_attr.attr, + &sensor_dev_attr_module_rx_los_65.dev_attr.attr, + &sensor_dev_attr_module_rx_los_66.dev_attr.attr, + NULL +}; + +static const struct attribute_group fpga_port_stat_group = { + .attrs = fpga_transceiver_attributes, +}; + +struct attribute_mapping { + u16 attr_base; + u16 reg; + u8 revert; +}; + +// Define an array of attribute mappings +static struct attribute_mapping attribute_mappings[] = { + [MODULE_PRESENT_1 ... MODULE_PRESENT_8] = {MODULE_PRESENT_1, XCVR_P7_P0_PRESENT_REG, 1}, + [MODULE_PRESENT_9 ... MODULE_PRESENT_16] = {MODULE_PRESENT_9, XCVR_P15_P8_PRESENT_REG, 1}, + [MODULE_PRESENT_17 ... MODULE_PRESENT_24] = {MODULE_PRESENT_17, XCVR_P23_P16_PRESENT_REG, 1}, + [MODULE_PRESENT_25 ... MODULE_PRESENT_32] = {MODULE_PRESENT_25, XCVR_P31_P24_PRESENT_REG, 1}, + [MODULE_PRESENT_33 ... MODULE_PRESENT_40] = {MODULE_PRESENT_33, XCVR_P39_P32_PRESENT_REG, 1}, + [MODULE_PRESENT_41 ... MODULE_PRESENT_48] = {MODULE_PRESENT_41, XCVR_P47_P40_PRESENT_REG, 1}, + [MODULE_PRESENT_49 ... MODULE_PRESENT_56] = {MODULE_PRESENT_49, XCVR_P55_P48_PRESENT_REG, 1}, + [MODULE_PRESENT_57 ... MODULE_PRESENT_64] = {MODULE_PRESENT_57, XCVR_P63_P56_PRESENT_REG, 1}, + [MODULE_PRESENT_65 ... MODULE_PRESENT_66] = {MODULE_PRESENT_65, SFP_PRESENT_REG, 1}, + + [MODULE_LPMODE_1 ... MODULE_LPMODE_8] = {MODULE_LPMODE_1, XCVR_P7_P0_LPMODE_REG, 1}, + [MODULE_LPMODE_9 ... MODULE_LPMODE_16] = {MODULE_LPMODE_9, XCVR_P15_P8_LPMODE_REG, 1}, + [MODULE_LPMODE_17 ... MODULE_LPMODE_24] = {MODULE_LPMODE_17, XCVR_P23_P16_LPMODE_REG, 1}, + [MODULE_LPMODE_25 ... MODULE_LPMODE_32] = {MODULE_LPMODE_25, XCVR_P31_P24_LPMODE_REG, 1}, + [MODULE_LPMODE_33 ... MODULE_LPMODE_40] = {MODULE_LPMODE_33, XCVR_P39_P32_LPMODE_REG, 1}, + [MODULE_LPMODE_41 ... MODULE_LPMODE_48] = {MODULE_LPMODE_41, XCVR_P47_P40_LPMODE_REG, 1}, + [MODULE_LPMODE_49 ... MODULE_LPMODE_56] = {MODULE_LPMODE_49, XCVR_P55_P48_LPMODE_REG, 1}, + [MODULE_LPMODE_57 ... MODULE_LPMODE_64] = {MODULE_LPMODE_57, XCVR_P63_P56_LPMODE_REG, 1}, + + [MODULE_RESET_1 ... MODULE_RESET_8] = {MODULE_RESET_1, XCVR_P7_P0_RESET_REG, 1}, + [MODULE_RESET_9 ... MODULE_RESET_16] = {MODULE_RESET_9, XCVR_P15_P8_RESET_REG, 1}, + [MODULE_RESET_17 ... MODULE_RESET_24] = {MODULE_RESET_17, XCVR_P23_P16_RESET_REG, 1}, + [MODULE_RESET_25 ... MODULE_RESET_32] = {MODULE_RESET_25, XCVR_P31_P24_RESET_REG, 1}, + [MODULE_RESET_33 ... MODULE_RESET_40] = {MODULE_RESET_33, XCVR_P39_P32_RESET_REG, 1}, + [MODULE_RESET_41 ... MODULE_RESET_48] = {MODULE_RESET_41, XCVR_P47_P40_RESET_REG, 1}, + [MODULE_RESET_49 ... MODULE_RESET_56] = {MODULE_RESET_49, XCVR_P55_P48_RESET_REG, 1}, + [MODULE_RESET_57 ... MODULE_RESET_64] = {MODULE_RESET_57, XCVR_P63_P56_RESET_REG, 1}, + + [MODULE_PGOOD_1 ... MODULE_PGOOD_8] = {MODULE_PGOOD_1, XCVR_P7_P0_PGOOD_REG, 0}, + [MODULE_PGOOD_9 ... MODULE_PGOOD_16] = {MODULE_PGOOD_9, XCVR_P15_P8_PGOOD_REG, 0}, + [MODULE_PGOOD_17 ... MODULE_PGOOD_24] = {MODULE_PGOOD_17, XCVR_P23_P16_PGOOD_REG, 0}, + [MODULE_PGOOD_25 ... MODULE_PGOOD_32] = {MODULE_PGOOD_25, XCVR_P31_P24_PGOOD_REG, 0}, + [MODULE_PGOOD_33 ... MODULE_PGOOD_40] = {MODULE_PGOOD_33, XCVR_P39_P32_PGOOD_REG, 0}, + [MODULE_PGOOD_41 ... MODULE_PGOOD_48] = {MODULE_PGOOD_41, XCVR_P47_P40_PGOOD_REG, 0}, + [MODULE_PGOOD_49 ... MODULE_PGOOD_56] = {MODULE_PGOOD_49, XCVR_P55_P48_PGOOD_REG, 0}, + [MODULE_PGOOD_57 ... MODULE_PGOOD_64] = {MODULE_PGOOD_57, XCVR_P63_P56_PGOOD_REG, 0}, + + [MODULE_INTR_1 ... MODULE_INTR_8] = {MODULE_INTR_1, XCVR_P7_P0_INTR_REG, 0}, + [MODULE_INTR_9 ... MODULE_INTR_16] = {MODULE_INTR_9, XCVR_P15_P8_INTR_REG, 0}, + [MODULE_INTR_17 ... MODULE_INTR_24] = {MODULE_INTR_17, XCVR_P23_P16_INTR_REG, 0}, + [MODULE_INTR_25 ... MODULE_INTR_32] = {MODULE_INTR_25, XCVR_P31_P24_INTR_REG, 0}, + [MODULE_INTR_33 ... MODULE_INTR_40] = {MODULE_INTR_33, XCVR_P39_P32_INTR_REG, 0}, + [MODULE_INTR_41 ... MODULE_INTR_48] = {MODULE_INTR_41, XCVR_P47_P40_INTR_REG, 0}, + [MODULE_INTR_49 ... MODULE_INTR_56] = {MODULE_INTR_49, XCVR_P55_P48_INTR_REG, 0}, + [MODULE_INTR_57 ... MODULE_INTR_64] = {MODULE_INTR_57, XCVR_P63_P56_INTR_REG, 0}, + + [MODULE_TX_DISABLE_65 ... MODULE_TX_DISABLE_66] ={MODULE_TX_DISABLE_65, SFP_TXDIS_REG, 0}, + [MODULE_TX_FAULT_65 ... MODULE_TX_FAULT_66] = {MODULE_TX_FAULT_65, SFP_TXFAULT_REG, 0}, + [MODULE_RX_LOS_65 ... MODULE_RX_LOS_66] = {MODULE_RX_LOS_65, SFP_RXLOSS_REG, 0}, +}; + +static inline unsigned int fpga_read(const void __iomem *addr) +{ + return ioread8(addr); +} + +static inline void fpga_write(void __iomem *addr, u8 val) +{ + iowrite8(val, addr); +} + + +static ssize_t status_read(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as1817_64o_fpga_data *fpga_ctl = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + u16 reg; + u8 reg_val; + u8 bits_shift; + + switch(attr->index) + { + case MODULE_PRESENT_1 ... MODULE_RX_LOS_66: + reg = attribute_mappings[attr->index].reg; + mutex_lock(&fpga_ctl->lock); + if ((reg & 0xF000) == CPLD1_PCIE_START_OFFSET) { + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr1 + reg); + } else if ((reg & 0xF000) == CPLD2_PCIE_START_OFFSET) { + reg_val = fpga_read(fpga_ctl->pci_fpga_dev.data_base_addr2 + reg); + } + mutex_unlock(&fpga_ctl->lock); + + bits_shift = attr->index - attribute_mappings[attr->index].attr_base; + reg_val = (reg_val >> bits_shift) & 0x01; + if (attribute_mappings[attr->index].revert) { + reg_val = !reg_val; + } + + ret = sprintf(buf, "%u\n", reg_val); + break; + default: + break; + } + + return ret; +} + +static ssize_t status_write(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as1817_64o_fpga_data *fpga_ctl = dev_get_drvdata(dev); + void __iomem *addr; + int status; + u16 reg; + u8 input; + u8 reg_val, bit_mask, should_set_bit; + + status = kstrtou8(buf, 10, &input); + if (status) { + return status; + } + + reg = attribute_mappings[attr->index].reg; + if ((reg & 0xF000) == CPLD1_PCIE_START_OFFSET) { + addr = fpga_ctl->pci_fpga_dev.data_base_addr1; + } else if ((reg & 0xF000) == CPLD2_PCIE_START_OFFSET) { + addr = fpga_ctl->pci_fpga_dev.data_base_addr2; + } else { + return -EINVAL; + } + bit_mask = 0x01 << (attr->index - attribute_mappings[attr->index].attr_base); + should_set_bit = attribute_mappings[attr->index].revert ? !input : input; + + mutex_lock(&fpga_ctl->lock); + reg_val = fpga_read(addr + reg); + if (should_set_bit) { + reg_val |= bit_mask; + } else { + reg_val &= ~bit_mask; + } + fpga_write(addr + reg, reg_val); + mutex_unlock(&fpga_ctl->lock); + + return count; +} + +struct _port_data { + u16 offset; + u16 mask; /* SPI Busy mask : 0x01 --> CPLD1, 0x02 --> CPLD2 */ +}; +/* ============PCIe Bar Offset to I2C Master Mapping============== */ +static const struct _port_data port[PORT_NUM]= { + /* CPLD1: OSFP Port 0-31 (0x2100-0x24F0) */ + {0x2100, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port0 */ + {0x2120, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port1 */ + {0x2140, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port2 */ + {0x2160, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port3 */ + {0x2180, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port4 */ + {0x21A0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port5 */ + {0x21C0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port6 */ + {0x21E0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port7 */ + {0x2200, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port8 */ + {0x2220, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port9 */ + {0x2240, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port10 */ + {0x2260, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port11 */ + {0x2280, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port12 */ + {0x22A0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port13 */ + {0x22C0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port14 */ + {0x22E0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port15 */ + {0x2300, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port16 */ + {0x2320, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port17 */ + {0x2340, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port18 */ + {0x2360, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port19 */ + {0x2380, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port20 */ + {0x23A0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port21 */ + {0x23C0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port22 */ + {0x23E0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port23 */ + {0x2400, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port24 */ + {0x2420, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port25 */ + {0x2440, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port26 */ + {0x2460, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port27 */ + {0x2480, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port28 */ + {0x24A0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port29 */ + {0x24C0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port30 */ + {0x24E0, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master OSFP Port31 */ + /* CPLD2: OSFP Port 32-63 (0x3100-0x34F0) */ + {0x3100, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port32 */ + {0x3120, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port33 */ + {0x3140, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port34 */ + {0x3160, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port35 */ + {0x3180, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port36 */ + {0x31A0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port37 */ + {0x31C0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port38 */ + {0x31E0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port39 */ + {0x3200, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port40 */ + {0x3220, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port41 */ + {0x3240, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port42 */ + {0x3260, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port43 */ + {0x3280, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port44 */ + {0x32A0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port45 */ + {0x32C0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port46 */ + {0x32E0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port47 */ + {0x3300, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port48 */ + {0x3320, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port49 */ + {0x3340, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port50 */ + {0x3360, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port51 */ + {0x3380, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port52 */ + {0x33A0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port53 */ + {0x33C0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port54 */ + {0x33E0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port55 */ + {0x3400, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port56 */ + {0x3420, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port57 */ + {0x3440, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port58 */ + {0x3460, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port59 */ + {0x3480, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port60 */ + {0x34A0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port61 */ + {0x34C0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port62 */ + {0x34E0, SPI_BUSY_MASK_CPLD2},/* CPLD2 I2C Master OSFP Port63 */ + /* CPLD1: SFP28 Port 64-65 (0x2500-0x2520) */ + {0x2500, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master SFP28 Port64 */ + {0x2520, SPI_BUSY_MASK_CPLD1},/* CPLD1 I2C Master SFP28 Port65 */ +}; + +static struct ocores_i2c_platform_data as1817_64o_platform_data = { + .reg_io_width = 1, + .reg_shift = 2, + /* + * PRER_L and PRER_H are calculated based on clock_khz and bus_khz + * in i2c-ocores.c:ocores_init. + */ +#if 0 + /* SCL 400KHZ in FPGA spec. => PRER_L = 0x0B, PRER_H = 0x00 */ + .clock_khz = 24000, + .bus_khz = 400, +#else + /* SCL 100KHZ in FPGA spec. => PRER_L = 0x2F, PRER_H = 0x00 */ + .clock_khz = 24000, + .bus_khz = 100, +#endif +}; + +static struct platform_device *ocore_i2c_device_add(unsigned int id, unsigned long bar_base, + unsigned int offset) +{ + struct resource res = DEFINE_RES_MEM(bar_base + offset, 0x20); + struct platform_device *pdev; + int err; + + pdev = platform_device_alloc(OCORES_I2C_DRVNAME, id); + if (!pdev) { + err = -ENOMEM; + pcie_err("Port%u device allocation failed (%d)\n", (id & 0xFF), err); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + pcie_err("Port%u device resource addition failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + err = platform_device_add_data(pdev, &as1817_64o_platform_data, + sizeof(struct ocores_i2c_platform_data)); + if (err) { + pcie_err("Port%u platform data allocation failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + pcie_err("Port%u device addition failed (%d)\n", (id & 0xFF), err); + goto exit_device_put; + } + + return pdev; + +exit_device_put: + platform_device_put(pdev); +exit: + return ERR_PTR(err); +} + +static int as1817_64o_pcie_fpga_stat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct as1817_64o_fpga_data *fpga_ctl; + struct pci_dev *pcidev; + struct resource *ret; + int i; + int status = 0, err = 0; + unsigned long bar_base; + + fpga_ctl = devm_kzalloc(dev, sizeof(struct as1817_64o_fpga_data), GFP_KERNEL); + if (!fpga_ctl) { + return -ENOMEM; + } + platform_set_drvdata(pdev, fpga_ctl); + + pcidev = pci_get_device(FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID, NULL); + if (!pcidev) { + dev_err(dev, "Cannot found PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + return -ENODEV; + } + fpga_ctl->pci_fpga_dev.pci_dev = pcidev; + + err = pci_enable_device(pcidev); + if (err != 0) { + dev_err(dev, "Cannot enable PCI device(%x:%x)\n", + FPGA_PCI_VENDOR_ID, FPGA_PCI_DEVICE_ID); + status = -ENODEV; + goto exit_pci_disable; + } + /* enable PCI bus-mastering */ + pci_set_master(pcidev); + + /* + * Detect platform for changing the setting behavior of LP mode. + */ + fpga_ctl->pci_fpga_dev.data_base_addr0 = pci_iomap(pcidev, BAR0_NUM, 0); + if (fpga_ctl->pci_fpga_dev.data_base_addr0 == NULL) { + dev_err(dev, "Failed to map BAR0\n"); + status = -EIO; + goto exit_pci_disable; + } + + mutex_init(&fpga_ctl->lock); + + /* + * TH6 design: CPLD1 and CPLD2 registers are all within BAR0. + */ + fpga_ctl->pci_fpga_dev.data_base_addr1 = fpga_ctl->pci_fpga_dev.data_base_addr0; + fpga_ctl->pci_fpga_dev.data_region1 = pci_resource_start(pcidev, BAR0_NUM) + CPLD1_PCIE_START_OFFSET; + ret = request_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN, DRVNAME"_cpld1"); + if (ret == NULL) { + dev_err(dev, "[%s] cannot request region\n", DRVNAME"_cpld1"); + status = -EIO; + goto exit_pci_iounmap0; + } + dev_info(dev, "(CPLD1 resource: Start=0x%lx, Length=0x%x)", + (unsigned long)fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); + + fpga_ctl->pci_fpga_dev.data_base_addr2 = fpga_ctl->pci_fpga_dev.data_base_addr0; + fpga_ctl->pci_fpga_dev.data_region2 = pci_resource_start(pcidev, BAR0_NUM) + CPLD2_PCIE_START_OFFSET; + ret = request_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN, DRVNAME"_cpld2"); + if (ret == NULL) { + dev_err(dev, "[%s] cannot request region\n", DRVNAME"_cpld2"); + status = -EIO; + goto exit_pci_release1; + } + dev_info(dev, "(CPLD2 resource: Start=0x%lx, Length=0x%x)", + (unsigned long)fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); + + /* Create I2C ocore devices first, then create the FPGA sysfs. + * To prevent the application from accessing an ocore device + * that has not been fully created due to the port status + * being present. + */ + + /* + * Create ocore_i2c device for OSFP EEPROM + */ + bar_base = pci_resource_start(pcidev, BAR0_NUM); + for (i = 0; i < PORT_NUM; i++) { + fpga_ctl->pci_fpga_dev.fpga_i2c[i] = + ocore_i2c_device_add((i | (port[i].mask << 8)), bar_base, port[i].offset); + if (IS_ERR(fpga_ctl->pci_fpga_dev.fpga_i2c[i])) { + status = PTR_ERR(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + dev_err(dev, "rc:%d, unload Port%u[0x%ux] device\n", + status, i, port[i].offset); + goto exit_ocores_device; + } + } + + status = sysfs_create_group(&pdev->dev.kobj, &fpga_port_stat_group); + if (status) { + goto exit_ocores_device; + } + + return 0; + +exit_ocores_device: + while (i > 0) { + i--; + platform_device_unregister(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + } + release_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); +exit_pci_release1: + release_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); +exit_pci_iounmap0: + pci_iounmap(fpga_ctl->pci_fpga_dev.pci_dev, fpga_ctl->pci_fpga_dev.data_base_addr0); +exit_pci_disable: + pci_disable_device(fpga_ctl->pci_fpga_dev.pci_dev); + pci_dev_put(fpga_ctl->pci_fpga_dev.pci_dev); + + return status; +} + +static void as1817_64o_pcie_fpga_stat_remove(struct platform_device *pdev) +{ + struct as1817_64o_fpga_data *fpga_ctl = platform_get_drvdata(pdev); + + if (pci_is_enabled(fpga_ctl->pci_fpga_dev.pci_dev)) { + int i; + + sysfs_remove_group(&pdev->dev.kobj, &fpga_port_stat_group); + /* Unregister ocore_i2c device */ + for (i = 0; i < PORT_NUM; i++) { + platform_device_unregister(fpga_ctl->pci_fpga_dev.fpga_i2c[i]); + } + release_mem_region(fpga_ctl->pci_fpga_dev.data_region1, REGION_LEN); + release_mem_region(fpga_ctl->pci_fpga_dev.data_region2, REGION_LEN); + pci_iounmap(fpga_ctl->pci_fpga_dev.pci_dev, fpga_ctl->pci_fpga_dev.data_base_addr0); + pci_disable_device(fpga_ctl->pci_fpga_dev.pci_dev); + pci_dev_put(fpga_ctl->pci_fpga_dev.pci_dev); + } +} + +static struct platform_driver pcie_fpga_port_stat_driver = { + .probe = as1817_64o_pcie_fpga_stat_probe, + .remove = as1817_64o_pcie_fpga_stat_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, +}; + +static int __init as1817_64o_pcie_fpga_init(void) +{ + int status = 0; + + /* + * Create FPGA platform driver and device + */ + status = platform_driver_register(&pcie_fpga_port_stat_driver); + if (status < 0) { + return status; + } + + pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + status = PTR_ERR(pdev); + goto exit_pci; + } + + return status; + +exit_pci: + platform_driver_unregister(&pcie_fpga_port_stat_driver); + + return status; +} + +static void __exit as1817_64o_pcie_fpga_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&pcie_fpga_port_stat_driver); +} + + +module_init(as1817_64o_pcie_fpga_init); +module_exit(as1817_64o_pcie_fpga_exit); + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("AS1817-64O FPGA via PCIE"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-i2c-ocores.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-i2c-ocores.c new file mode 100644 index 000000000..bd4357c76 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-i2c-ocores.c @@ -0,0 +1,1011 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller + * (https://opencores.org/project/i2c/overview) + * + * Peter Korsgaard + * + * Support for the GRLIB port of the controller by + * Andreas Larsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 'process_lock' exists because ocores_process() and ocores_process_timeout() + * can't run in parallel. + */ +struct ocores_i2c { + void __iomem *base; + int iobase; + u32 reg_shift; + u32 reg_io_width; + unsigned long flags; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; /* see STATE_ */ + spinlock_t process_lock; + struct clk *clk; + int ip_clock_khz; + int bus_clock_khz; + void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value); + u8 (*getreg)(struct ocores_i2c *i2c, int reg); +}; + +/* registers */ +#define OCI2C_PRELOW 0 +#define OCI2C_PREHIGH 1 +#define OCI2C_CONTROL 2 +#define OCI2C_DATA 3 +#define OCI2C_CMD 4 /* write only */ +#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */ + +#define OCI2C_CTRL_IEN 0x40 +#define OCI2C_CTRL_EN 0x80 + +#define OCI2C_CMD_START 0x91 +#define OCI2C_CMD_STOP 0x41 +#define OCI2C_CMD_READ 0x21 +#define OCI2C_CMD_WRITE 0x11 +#define OCI2C_CMD_READ_ACK 0x21 +#define OCI2C_CMD_READ_NACK 0x29 +#define OCI2C_CMD_IACK 0x01 + +#define OCI2C_STAT_IF 0x01 +#define OCI2C_STAT_TIP 0x02 +#define OCI2C_STAT_ARBLOST 0x20 +#define OCI2C_STAT_BUSY 0x40 +#define OCI2C_STAT_NACK 0x80 + +#define STATE_DONE 0 +#define STATE_START 1 +#define STATE_WRITE 2 +#define STATE_READ 3 +#define STATE_ERROR 4 + +#define TYPE_OCORES 0 +#define TYPE_GRLIB 1 +#define TYPE_SIFIVE_REV0 2 + +#define OCORES_FLAG_BROKEN_IRQ BIT(1) /* Broken IRQ for FU540-C000 SoC */ + +static unsigned int timeout = 1; +module_param(timeout , uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Timeout for ocores_poll_wait, in unit of milliseconds."); + +static unsigned int debug = 1; +module_param(debug , uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug, "Enable or disable debug message. 1 -> enable, 0 -> disable"); + +static void oc_setreg_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite8(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32be(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32be(value, i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_8(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16(struct ocores_i2c *i2c, int reg) +{ + return ioread16(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32(struct ocores_i2c *i2c, int reg) +{ + return ioread32(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16be(struct ocores_i2c *i2c, int reg) +{ + return ioread16be(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg) +{ + return ioread32be(i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value) +{ + outb(value, i2c->iobase + reg); +} + +static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg) +{ + return inb(i2c->iobase + reg); +} + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + i2c->setreg(i2c, reg, value); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + return i2c->getreg(i2c, reg); +} + +static void ocores_process(struct ocores_i2c *i2c, u8 stat) +{ + struct i2c_msg *msg = i2c->msg; + unsigned long flags; + struct device *dev = i2c->adap.dev.parent; + + /* + * If we spin here is because we are in timeout, so we are going + * to be in STATE_ERROR. See ocores_process_timeout() + */ + spin_lock_irqsave(&i2c->process_lock, flags); + if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { + /* stop has been sent */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + wake_up(&i2c->wait); + goto out; + } + + /* error? */ + if (stat & OCI2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + if (debug) { + dev_warn(dev, "I2C %s arbitration lost", i2c->adap.name); + } + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { + i2c->state = + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & OCI2C_STAT_NACK) { + i2c->state = STATE_ERROR; + if (debug) { + dev_warn(dev, "I2C %s, no ACK from slave 0x%02x", + i2c->adap.name, msg->addr); + } + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + } else { + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + } + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { /* end? */ + /* send start? */ + if (!(msg->flags & I2C_M_NOSTART)) { + u8 addr = i2c_8bit_addr_from_msg(msg); + + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, addr); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + goto out; + } + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } else { + i2c->state = STATE_DONE; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + goto out; + } + } + + if (i2c->state == STATE_READ) { + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); + } else { + oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); + } + +out: + spin_unlock_irqrestore(&i2c->process_lock, flags); + +} + +static irqreturn_t ocores_isr(int irq, void *dev_id) +{ + struct ocores_i2c *i2c = dev_id; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); + + if (i2c->flags & OCORES_FLAG_BROKEN_IRQ) { + if ((stat & OCI2C_STAT_IF) && !(stat & OCI2C_STAT_BUSY)) + return IRQ_NONE; + } else if (!(stat & OCI2C_STAT_IF)) { + return IRQ_NONE; + } + ocores_process(i2c, stat); + + return IRQ_HANDLED; +} + +/** + * Process timeout event + * @i2c: ocores I2C device instance + */ +static void ocores_process_timeout(struct ocores_i2c *i2c) +{ + unsigned long flags; + + spin_lock_irqsave(&i2c->process_lock, flags); + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + spin_unlock_irqrestore(&i2c->process_lock, flags); +} + +/** + * Wait until something change in a given register + * @i2c: ocores I2C device instance + * @reg: register to query + * @mask: bitmask to apply on register value + * @val: expected result + * @timeout: timeout in jiffies + * + * Timeout is necessary to avoid to stay here forever when the chip + * does not answer correctly. + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_wait(struct ocores_i2c *i2c, + int reg, u8 mask, u8 val, + const unsigned long timeout) +{ + unsigned long j; + + j = jiffies + timeout; + while (1) { + u8 status = oc_getreg(i2c, reg); + + if ((status & mask) == val) + break; + + if (time_after(jiffies, j)) + return -ETIMEDOUT; + } + return 0; +} + +/** + * Wait until is possible to process some data + * @i2c: ocores I2C device instance + * + * Used when the device is in polling mode (interrupts disabled). + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_poll_wait(struct ocores_i2c *i2c) +{ + u8 mask; + int err; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* transfer is over */ + mask = OCI2C_STAT_BUSY; + } else { + /* on going transfer */ + mask = OCI2C_STAT_TIP; + /* + * We wait for the data to be transferred (8bit), + * then we start polling on the ACK/NACK bit + */ + udelay((8 * 1000) / i2c->bus_clock_khz); + } + + /* + * once we are here we expect to get the expected result immediately + * so if after 1ms we timeout then something is broken. + */ + err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(timeout)); + if (err) { + if (debug) { + dev_warn(i2c->adap.dev.parent, + "%s: STATUS timeout, bit 0x%x did not clear in %ums(msecs_to_jiffies(%u)=%lu)\n", + __func__, mask, timeout, timeout, msecs_to_jiffies(timeout)); + } + } + return err; +} + +/** + * It handles an IRQ-less transfer + * @i2c: ocores I2C device instance + * + * Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same + * (only that IRQ are not produced). This means that we can re-use entirely + * ocores_isr(), we just add our polling code around it. + * + * It can run in atomic context + * + * Return: 0 on success, -ETIMEDOUT on timeout + */ +static int ocores_process_polling(struct ocores_i2c *i2c) +{ + irqreturn_t ret; + int err; + + while (1) { + err = ocores_poll_wait(i2c); + if (err) { + break; /* timeout */ + } + + ret = ocores_isr(-1, i2c); + if (ret == IRQ_NONE) + break; /* all messages have been transferred */ + else { + if (i2c->flags & OCORES_FLAG_BROKEN_IRQ) + if (i2c->state == STATE_DONE) + break; + } + } + + return err; +} + +static int ocores_xfer_core(struct ocores_i2c *i2c, + struct i2c_msg *msgs, int num, + bool polling) +{ + int ret = 0; + u8 ctrl; + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + if (polling) + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN); + else + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN); + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + + if (polling) { + ret = ocores_process_polling(i2c); + } else { + if (wait_event_timeout(i2c->wait, + (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ) == 0) + ret = -ETIMEDOUT; + } + if (ret) { + ocores_process_timeout(i2c); + return ret; + } + return (i2c->state == STATE_DONE) ? num : -EIO; +} + +static int ocores_xfer_polling(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true); +} + +static int ocores_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false); +} + +static int ocores_init(struct device *dev, struct ocores_i2c *i2c) +{ + int prescale; + int diff; + u8 ctrl; + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1; + prescale = clamp(prescale, 0, 0xffff); + + diff = i2c->ip_clock_khz / (5 * (prescale + 1)) - i2c->bus_clock_khz; + if (abs(diff) > i2c->bus_clock_khz / 10) { + dev_err(dev, + "Unsupported clock settings: core: %d KHz, bus: %d KHz\n", + i2c->ip_clock_khz, i2c->bus_clock_khz); + return -EINVAL; + } + + dev_info(dev, "OCI2C_PRELOW=0x%02x OCI2C_PREHIGH=0x%02x\n", + prescale & 0xff, prescale >> 8); + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); + oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8); + + /* Init the device */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN); + + return 0; +} + +/** + * ocores_bus_clock_to_prescale - Convert runtime bus clock to prescale bytes + */ +static int ocores_bus_clock_to_prescale(unsigned int bus_clock_khz, + u8 *prelow, u8 *prehigh) +{ + switch (bus_clock_khz) { + case 100: + *prelow = 0x2f; + *prehigh = 0x00; + break; + case 400: + *prelow = 0x0b; + *prehigh = 0x00; + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int ocores_prescale_to_ip_clock(unsigned int bus_clock_khz, + u8 prelow, u8 prehigh) +{ + unsigned int prescale; + + prescale = prelow | (((unsigned int)prehigh) << 8); + + return bus_clock_khz * 5 * (prescale + 1); +} + +static ssize_t bus_clock_khz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + + if (!i2c) + return -ENODEV; + + return sprintf(buf, "%d\n", i2c->bus_clock_khz); +} + +static int ocores_set_bus_clock_khz(struct device *dev, + struct ocores_i2c *i2c, + unsigned int bus_clock_khz) +{ + unsigned long flags; + unsigned int ip_clock_khz; + u8 prelow; + u8 prehigh; + u8 status; + u8 ctrl; + int ret; + + ret = ocores_bus_clock_to_prescale(bus_clock_khz, &prelow, &prehigh); + if (ret) { + dev_err(dev, "Unsupported I2C bus clock: %u KHz\n", + bus_clock_khz); + return ret; + } + + ip_clock_khz = ocores_prescale_to_ip_clock(bus_clock_khz, + prelow, prehigh); + + i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER); + + ret = ocores_wait(i2c, OCI2C_STATUS, + OCI2C_STAT_BUSY | OCI2C_STAT_TIP, 0, + msecs_to_jiffies(timeout)); + if (ret) { + dev_warn(dev, + "Controller is busy, force re-init while changing I2C bus clock\n"); + } + + spin_lock_irqsave(&i2c->process_lock, flags); + + if (i2c->state != STATE_DONE && i2c->state != STATE_ERROR) { + dev_warn(dev, + "Force I2C state from %d to error while changing bus clock\n", + i2c->state); + i2c->state = STATE_ERROR; + wake_up(&i2c->wait); + } + + status = oc_getreg(i2c, OCI2C_STATUS); + if (status & (OCI2C_STAT_BUSY | OCI2C_STAT_TIP)) { + dev_warn(dev, + "Controller status is 0x%02x before forced bus clock update\n", + status); + } + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + oc_setreg(i2c, OCI2C_CONTROL, + ctrl & ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN)); + + oc_setreg(i2c, OCI2C_PRELOW, prelow); + oc_setreg(i2c, OCI2C_PREHIGH, prehigh); + + i2c->bus_clock_khz = bus_clock_khz; + i2c->ip_clock_khz = ip_clock_khz; + + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + if (ctrl & OCI2C_CTRL_EN) + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + + spin_unlock_irqrestore(&i2c->process_lock, flags); + i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER); + + dev_info(dev, + "Set I2C bus clock to %u KHz, ip clock to %u KHz, OCI2C_PRELOW=0x%02x OCI2C_PREHIGH=0x%02x\n", + bus_clock_khz, ip_clock_khz, prelow, prehigh); + + return 0; +} + +static ssize_t bus_clock_khz_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + unsigned int bus_clock_khz; + int ret; + + if (!i2c) + return -ENODEV; + + ret = kstrtouint(buf, 0, &bus_clock_khz); + if (ret) + return ret; + + ret = ocores_set_bus_clock_khz(dev, i2c, bus_clock_khz); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(bus_clock_khz); + +static struct attribute *ocores_i2c_attrs[] = { + &dev_attr_bus_clock_khz.attr, + NULL +}; + +static const struct attribute_group ocores_i2c_attr_group = { + .attrs = ocores_i2c_attrs, +}; + +static u32 ocores_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ocores_algorithm = { + .master_xfer = ocores_xfer, + .master_xfer_atomic = ocores_xfer_polling, + .functionality = ocores_func, +}; + +static const struct i2c_adapter ocores_adapter = { + .owner = THIS_MODULE, + .name = "i2c-ocores", + .class = I2C_CLASS_DEPRECATED, + .algo = &ocores_algorithm, +}; + +static const struct of_device_id ocores_i2c_match[] = { + { + .compatible = "opencores,i2c-ocores", + .data = (void *)TYPE_OCORES, + }, + { + .compatible = "aeroflexgaisler,i2cmst", + .data = (void *)TYPE_GRLIB, + }, + { + .compatible = "sifive,fu540-c000-i2c", + .data = (void *)TYPE_SIFIVE_REV0, + }, + { + .compatible = "sifive,i2c0", + .data = (void *)TYPE_SIFIVE_REV0, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ocores_i2c_match); + +#ifdef CONFIG_OF +/* + * Read and write functions for the GRLIB port of the controller. Registers are + * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one + * register. The subsequent registers have their offsets decreased accordingly. + */ +static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) +{ + u32 rd; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + rd = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PREHIGH) + return (u8)(rd >> 8); + else + return (u8)rd; +} + +static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) +{ + u32 curr, wr; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { + curr = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PRELOW) + wr = (curr & 0xff00) | value; + else + wr = (((u32)value) << 8) | (curr & 0xff); + } else { + wr = value; + } + iowrite32be(wr, i2c->base + (rreg << i2c->reg_shift)); +} + +static int ocores_i2c_of_probe(struct platform_device *pdev, + struct ocores_i2c *i2c) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + u32 val; + u32 clock_frequency; + bool clock_frequency_present; + + if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) { + /* no 'reg-shift', check for deprecated 'regstep' */ + if (!of_property_read_u32(np, "regstep", &val)) { + if (!is_power_of_2(val)) { + dev_err(&pdev->dev, "invalid regstep %d\n", + val); + return -EINVAL; + } + i2c->reg_shift = ilog2(val); + dev_warn(&pdev->dev, + "regstep property deprecated, use reg-shift\n"); + } + } + + clock_frequency_present = !of_property_read_u32(np, "clock-frequency", + &clock_frequency); + i2c->bus_clock_khz = 100; + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + + if (!IS_ERR(i2c->clk)) { + int ret = clk_prepare_enable(i2c->clk); + + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed: %d\n", ret); + return ret; + } + i2c->ip_clock_khz = clk_get_rate(i2c->clk) / 1000; + if (clock_frequency_present) + i2c->bus_clock_khz = clock_frequency / 1000; + } + + if (i2c->ip_clock_khz == 0) { + if (of_property_read_u32(np, "opencores,ip-clock-frequency", + &val)) { + if (!clock_frequency_present) { + dev_err(&pdev->dev, + "Missing required parameter 'opencores,ip-clock-frequency'\n"); + clk_disable_unprepare(i2c->clk); + return -ENODEV; + } + i2c->ip_clock_khz = clock_frequency / 1000; + dev_warn(&pdev->dev, + "Deprecated usage of the 'clock-frequency' property, please update to 'opencores,ip-clock-frequency'\n"); + } else { + i2c->ip_clock_khz = val / 1000; + if (clock_frequency_present) + i2c->bus_clock_khz = clock_frequency / 1000; + } + } + + of_property_read_u32(pdev->dev.of_node, "reg-io-width", + &i2c->reg_io_width); + + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (long)match->data == TYPE_GRLIB) { + dev_dbg(&pdev->dev, "GRLIB variant of i2c-ocores\n"); + i2c->setreg = oc_setreg_grlib; + i2c->getreg = oc_getreg_grlib; + } + + return 0; +} +#else +#define ocores_i2c_of_probe(pdev, i2c) -ENODEV +#endif + +static int ocores_i2c_probe(struct platform_device *pdev) +{ + struct ocores_i2c *i2c; + struct ocores_i2c_platform_data *pdata; + const struct of_device_id *match; + struct resource *res; + int irq; + int ret; + int i; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + spin_lock_init(&i2c->process_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + i2c->base = devm_ioremap_resource(&pdev->dev, res); + dev_info(&pdev->dev, "Resouce start:0x%llx, end:0x%llx", res->start, res->end); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + } else { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + i2c->iobase = res->start; + if (!devm_request_region(&pdev->dev, res->start, + resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Can't get I/O resource.\n"); + return -EBUSY; + } + i2c->setreg = oc_setreg_io_8; + i2c->getreg = oc_getreg_io_8; + } + + pdata = dev_get_platdata(&pdev->dev); + if (pdata) { + i2c->reg_shift = pdata->reg_shift; + i2c->reg_io_width = pdata->reg_io_width; + i2c->ip_clock_khz = pdata->clock_khz; + dev_info(&pdev->dev, "Write %d KHz, ioWidth:%d, shift:%d", i2c->ip_clock_khz, pdata->reg_io_width ,pdata->reg_shift); + if (pdata->bus_khz) + i2c->bus_clock_khz = pdata->bus_khz; + else + i2c->bus_clock_khz = 100; + } else { + ret = ocores_i2c_of_probe(pdev, i2c); + if (ret) + return ret; + } + + if (i2c->reg_io_width == 0) + i2c->reg_io_width = 1; /* Set to default value */ + + if (!i2c->setreg || !i2c->getreg) { + bool be = pdata ? pdata->big_endian : + of_device_is_big_endian(pdev->dev.of_node); + + switch (i2c->reg_io_width) { + case 1: + i2c->setreg = oc_setreg_8; + i2c->getreg = oc_getreg_8; + break; + + case 2: + i2c->setreg = be ? oc_setreg_16be : oc_setreg_16; + i2c->getreg = be ? oc_getreg_16be : oc_getreg_16; + break; + + case 4: + i2c->setreg = be ? oc_setreg_32be : oc_setreg_32; + i2c->getreg = be ? oc_getreg_32be : oc_getreg_32; + break; + + default: + dev_err(&pdev->dev, "Unsupported I/O width (%d)\n", + i2c->reg_io_width); + ret = -EINVAL; + goto err_clk; + } + } + + init_waitqueue_head(&i2c->wait); + + irq = platform_get_irq_optional(pdev, 0); + if (irq == -ENXIO) { + ocores_algorithm.master_xfer = ocores_xfer_polling; + + /* + * Set in OCORES_FLAG_BROKEN_IRQ to enable workaround for + * FU540-C000 SoC in polling mode. + */ + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (long)match->data == TYPE_SIFIVE_REV0) + i2c->flags |= OCORES_FLAG_BROKEN_IRQ; + } else { + if (irq < 0) + return irq; + } + + if (ocores_algorithm.master_xfer != ocores_xfer_polling) { + ret = devm_request_any_context_irq(&pdev->dev, irq, + ocores_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_clk; + } + } + ret = ocores_init(&pdev->dev, i2c); + if (ret) { + goto err_clk; + } + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = ocores_adapter; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + goto err_clk; + } + + ret = sysfs_create_group(&i2c->adap.dev.kobj, + &ocores_i2c_attr_group); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs group: %d\n", ret); + goto err_del_adapter; + } + + /* add in known devices to the bus */ + if (pdata) { + for (i = 0; i < pdata->num_devices; i++){ + i2c_new_client_device(&i2c->adap, pdata->devices + i); + } + } + + return 0; + +err_del_adapter: + i2c_del_adapter(&i2c->adap); +err_clk: + clk_disable_unprepare(i2c->clk); + return ret; +} + +static void ocores_i2c_remove(struct platform_device *pdev) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + u8 ctrl; + + sysfs_remove_group(&i2c->adap.dev.kobj, &ocores_i2c_attr_group); + + ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* disable i2c logic */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + + if (!IS_ERR(i2c->clk)) + clk_disable_unprepare(i2c->clk); +} + +#ifdef CONFIG_PM_SLEEP +static int ocores_i2c_suspend(struct device *dev) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN); + oc_setreg(i2c, OCI2C_CONTROL, ctrl); + + if (!IS_ERR(i2c->clk)) + clk_disable_unprepare(i2c->clk); + return 0; +} + +static int ocores_i2c_resume(struct device *dev) +{ + struct ocores_i2c *i2c = dev_get_drvdata(dev); + + if (!IS_ERR(i2c->clk)) { + unsigned long rate; + int ret = clk_prepare_enable(i2c->clk); + + if (ret) { + dev_err(dev, + "clk_prepare_enable failed: %d\n", ret); + return ret; + } + rate = clk_get_rate(i2c->clk) / 1000; + if (rate) + i2c->ip_clock_khz = rate; + } + return ocores_init(dev, i2c); +} + +static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume); +#define OCORES_I2C_PM (&ocores_i2c_pm) +#else +#define OCORES_I2C_PM NULL +#endif + +static struct platform_driver ocores_i2c_driver = { + .probe = ocores_i2c_probe, + .remove = ocores_i2c_remove, + .driver = { + .name = "ocores-i2c", + .of_match_table = ocores_i2c_match, + .pm = OCORES_I2C_PM, + }, +}; + +module_platform_driver(ocores_i2c_driver); + +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ocores-i2c"); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-leds.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-leds.c new file mode 100644 index 000000000..05ca07b6d --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-leds.c @@ -0,0 +1,380 @@ +/* + * A LED driver for the accton as1817_64o via IPMI + * + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as1817_64o_led" +#define ACCTON_IPMI_NETFN 0x34 +#define IPMI_LED_READ_CMD 0x1A +#define IPMI_LED_WRITE_CMD 0x1B + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 + +#define NUM_OF_LED 5 + +/* IPMI response color codes from BMC (0x1A get) */ +enum ipmi_led_color { + IPMI_LED_OFF = 0x00, + IPMI_LED_RED = 0x02, + IPMI_LED_GREEN = 0x04, + IPMI_LED_GREEN_BLINK = 0x05, + IPMI_LED_YELLOW = 0x06, + IPMI_LED_BLUE = 0x08, + IPMI_LED_BLUE_BLINK = 0x09, +}; + +/* Userspace LED mode values (sysfs) */ +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_RED = 10, + LED_MODE_RED_BLINKING = 11, + LED_MODE_YELLOW = 14, + LED_MODE_GREEN = 16, + LED_MODE_GREEN_BLINKING = 17, + LED_MODE_BLUE = 18, + LED_MODE_BLUE_BLINKING = 19, + LED_MODE_UNKNOWN = 99, +}; + +/* IPMI set (0x1B) color codes */ +enum ipmi_led_set_color { + IPMI_SET_LED_OFF = 0x00, + IPMI_SET_LED_RED = 0x02, + IPMI_SET_LED_GREEN = 0x04, + IPMI_SET_LED_GREEN_BLINK = 0x05, + IPMI_SET_LED_AMBER = 0x06, + IPMI_SET_LED_BLUE = 0x08, + IPMI_SET_LED_BLUE_BLINK = 0x09, +}; + +enum led_sysfs_attrs { + LED_LOC, + LED_DIAG, + LED_ALARM, + LED_FAN, + LED_PSU, +}; + +struct ipmi_data { + struct completion read_complete; + struct ipmi_addr address; + struct ipmi_user *user; + int interface; + struct kernel_ipmi_msg tx_message; + long tx_msgid; + void *rx_msg_data; + unsigned short rx_msg_len; + unsigned char rx_result; + int rx_recv_type; + struct ipmi_user_hndl ipmi_hndlrs; +}; + +struct as1817_64o_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; + unsigned long last_updated; + unsigned char ipmi_resp[NUM_OF_LED]; + struct ipmi_data ipmi; +}; + +static struct as1817_64o_led_data *data = NULL; + +static ssize_t show_led(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static int as1817_64o_led_probe(struct platform_device *pdev); +static void as1817_64o_led_remove(struct platform_device *pdev); + +static struct platform_driver as1817_64o_led_driver = { + .probe = as1817_64o_led_probe, + .remove = as1817_64o_led_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static SENSOR_DEVICE_ATTR(led_loc, S_IWUSR | S_IRUGO, show_led, set_led, LED_LOC); +static SENSOR_DEVICE_ATTR(led_diag, S_IRUGO, show_led, NULL, LED_DIAG); +static SENSOR_DEVICE_ATTR(led_alarm, S_IRUGO, show_led, NULL, LED_ALARM); +static SENSOR_DEVICE_ATTR(led_fan, S_IRUGO, show_led, NULL, LED_FAN); +static SENSOR_DEVICE_ATTR(led_psu, S_IRUGO, show_led, NULL, LED_PSU); + +static struct attribute *as1817_64o_led_attrs[] = { + &sensor_dev_attr_led_loc.dev_attr.attr, + &sensor_dev_attr_led_diag.dev_attr.attr, + &sensor_dev_attr_led_alarm.dev_attr.attr, + &sensor_dev_attr_led_fan.dev_attr.attr, + &sensor_dev_attr_led_psu.dev_attr.attr, + NULL +}; + +static const struct attribute_group as1817_64o_led_group = { + .attrs = as1817_64o_led_attrs, +}; + +/* IPMI functions */ +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + if (msg->msgid != ipmi->tx_msgid) { + ipmi_free_recv_msg(msg); + return; + } + + ipmi->rx_recv_type = msg->recv_type; + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +static int init_ipmi_data(struct ipmi_data *ipmi, int iface) +{ + int err; + + init_completion(&ipmi->read_complete); + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + pr_err("Unable to register user with IPMI interface %d\n", iface); + return -EACCES; + } + return 0; +} + +static int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err, retry; + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_data; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_data; + ipmi->rx_msg_len = rx_len; + + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) + return err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, + ipmi->tx_msgid, &ipmi->tx_message, + ipmi, 0, 0, 0); + if (err) + continue; + + err = wait_for_completion_timeout(&ipmi->read_complete, + IPMI_TIMEOUT); + if (!err) { + err = -ETIMEDOUT; + continue; + } + + if (ipmi->rx_result != 0) + continue; + + return 0; + } + return err ? err : -EIO; +} + +static void as1817_64o_led_update(void) +{ + int status; + + if (time_before(jiffies, data->last_updated + HZ * 5) && data->valid) + return; + + data->valid = 0; + status = ipmi_send_message(&data->ipmi, IPMI_LED_READ_CMD, + NULL, 0, + data->ipmi_resp, NUM_OF_LED); + if (unlikely(status != 0)) + return; + + data->last_updated = jiffies; + data->valid = 1; +} + +static int ipmi_color_to_led_mode(unsigned char ipmi_color) +{ + switch (ipmi_color) { + case IPMI_LED_OFF: return LED_MODE_OFF; + case IPMI_LED_RED: return LED_MODE_RED; + case IPMI_LED_GREEN: return LED_MODE_GREEN; + case IPMI_LED_BLUE_BLINK: return LED_MODE_BLUE_BLINKING; + default: return LED_MODE_UNKNOWN; + } +} + +static int led_mode_to_ipmi_color(int mode) +{ + switch (mode) { + case LED_MODE_OFF: return IPMI_SET_LED_OFF; + case LED_MODE_RED: return IPMI_SET_LED_RED; + case LED_MODE_GREEN: return IPMI_SET_LED_GREEN; + case LED_MODE_BLUE_BLINKING: return IPMI_SET_LED_BLUE_BLINK; + default: return -EINVAL; + } +} + +static ssize_t show_led(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + mutex_lock(&data->update_lock); + as1817_64o_led_update(); + + if (!data->valid) { + mutex_unlock(&data->update_lock); + return -EIO; + } + + value = ipmi_color_to_led_mode(data->ipmi_resp[attr->index]); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t set_led(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long mode; + int status, ipmi_color; + unsigned char tx_data[2]; + + status = kstrtol(buf, 10, &mode); + if (status) + return status; + + ipmi_color = led_mode_to_ipmi_color(mode); + if (ipmi_color < 0) + return -EINVAL; + + /* LED type is 1-based in IPMI: 1=LOC, 2=DIAG, 3=ALARM, 4=FAN, 5=PSU1, 6=PSU2 */ + tx_data[0] = attr->index + 1; + tx_data[1] = (unsigned char)ipmi_color; + + mutex_lock(&data->update_lock); + status = ipmi_send_message(&data->ipmi, IPMI_LED_WRITE_CMD, + tx_data, 2, NULL, 0); + if (status) { + mutex_unlock(&data->update_lock); + return -EIO; + } + + data->valid = 0; /* force re-read on next show */ + mutex_unlock(&data->update_lock); + + return count; +} + +static int as1817_64o_led_probe(struct platform_device *pdev) +{ + return sysfs_create_group(&pdev->dev.kobj, &as1817_64o_led_group); +} + +static void as1817_64o_led_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &as1817_64o_led_group); +} + +static int __init as1817_64o_led_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as1817_64o_led_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as1817_64o_led_driver); + if (ret) + goto err_free; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto err_driver; + } + + ret = init_ipmi_data(&data->ipmi, 0); + if (ret) + goto err_device; + + return 0; + +err_device: + platform_device_unregister(data->pdev); +err_driver: + platform_driver_unregister(&as1817_64o_led_driver); +err_free: + kfree(data); + return ret; +} + +static void __exit as1817_64o_led_exit(void) +{ + platform_device_unregister(data->pdev); + platform_driver_unregister(&as1817_64o_led_driver); + ipmi_destroy_user(data->ipmi.user); + kfree(data); +} + +module_init(as1817_64o_led_init); +module_exit(as1817_64o_led_exit); + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("as1817_64o_led driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-psu.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-psu.c new file mode 100644 index 000000000..970fb4132 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-psu.c @@ -0,0 +1,766 @@ +/* + * A PSU driver for accton as1817_64o via IPMI + * + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as1817_64o_psu" +#define ACCTON_IPMI_NETFN 0x34 +#define IPMI_PSU_READ_CMD 0x16 +#define IPMI_PSU_MODEL_NAME_CMD 0x10 +#define IPMI_PSU_SERIAL_NUM_CMD 0x11 +#define IPMI_PSU_FAN_DIR_CMD 0x13 +#define IPMI_PSU_INFO_CMD 0x20 + +#define PSU_INFO_SIZE 46 + +enum psu_info_offset { + PSU_INFO_FANDIR = 0, + PSU_INFO_TYPE = 1, + PSU_INFO_TEMP1_HCRIT = 2, + PSU_INFO_TEMP1_LCRIT = 4, + PSU_INFO_TEMP2_HCRIT = 6, + PSU_INFO_TEMP2_LCRIT = 8, + PSU_INFO_TEMP3_HCRIT = 10, + PSU_INFO_TEMP3_LCRIT = 12, + PSU_INFO_VIN_HCRIT = 14, + PSU_INFO_VIN_HWARN = 17, + PSU_INFO_VIN_LWARN = 20, + PSU_INFO_VIN_LCRIT = 23, + PSU_INFO_VOUT_HCRIT = 26, + PSU_INFO_VOUT_LCRIT = 29, + PSU_INFO_IIN_HCRIT = 32, + PSU_INFO_IOUT_HCRIT = 35, + PSU_INFO_PIN_HCRIT = 38, + PSU_INFO_POUT_HCRIT = 42, +}; + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 +#define IPMI_MODEL_SERIAL_LEN 32 +#define IPMI_FAN_DIR_LEN 3 + +#define NUM_OF_PSU 4 + +enum psu_data_index { + PSU_PRESENT = 0, + PSU_TEMP_FAULT, + PSU_POWER_GOOD_CPLD, + PSU_POWER_GOOD_PMBUS, + PSU_OVER_VOLTAGE, + PSU_OVER_CURRENT, + PSU_POWER_ON, + PSU_VIN0, PSU_VIN1, PSU_VIN2, + PSU_VOUT0, PSU_VOUT1, PSU_VOUT2, + PSU_IIN0, PSU_IIN1, PSU_IIN2, + PSU_IOUT0, PSU_IOUT1, PSU_IOUT2, + PSU_PIN0, PSU_PIN1, PSU_PIN2, PSU_PIN3, + PSU_POUT0, PSU_POUT1, PSU_POUT2, PSU_POUT3, + PSU_TEMP1_0, PSU_TEMP1_1, + PSU_TEMP2_0, PSU_TEMP2_1, + PSU_TEMP3_0, PSU_TEMP3_1, + PSU_FAN0, PSU_FAN1, + PSU_VOUT_MODE, + PSU_STATUS_COUNT +}; + +enum psu_sysfs_attrs { + PSU_ATTR_PRESENT, + PSU_ATTR_POWER_GOOD, + PSU_ATTR_VIN, + PSU_ATTR_VOUT, + PSU_ATTR_IIN, + PSU_ATTR_IOUT, + PSU_ATTR_PIN, + PSU_ATTR_POUT, + PSU_ATTR_MODEL, + PSU_ATTR_SERIAL, + PSU_ATTR_TEMP1_INPUT, + PSU_ATTR_TEMP2_INPUT, + PSU_ATTR_TEMP3_INPUT, + PSU_ATTR_FAN_INPUT, + PSU_ATTR_FAN_MAX_SPEED, + PSU_ATTR_FAN_PERCENTAGE, + PSU_ATTR_FAN_DIR, + PSU_ATTR_TYPE, + PSU_ATTR_TEMP1_HIGH_CRIT, + PSU_ATTR_TEMP1_LOW_CRIT, + PSU_ATTR_TEMP2_HIGH_CRIT, + PSU_ATTR_TEMP2_LOW_CRIT, + PSU_ATTR_TEMP3_HIGH_CRIT, + PSU_ATTR_TEMP3_LOW_CRIT, + PSU_ATTR_VIN_HIGH_CRIT, + PSU_ATTR_VIN_LOW_CRIT, + PSU_ATTR_VIN_HIGH_WARN, + PSU_ATTR_VIN_LOW_WARN, + PSU_ATTR_VOUT_HIGH_CRIT, + PSU_ATTR_VOUT_LOW_CRIT, + PSU_ATTR_IIN_HIGH_CRIT, + PSU_ATTR_IOUT_HIGH_CRIT, +}; + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static ssize_t show_psu(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf); +static int as1817_64o_psu_probe(struct platform_device *pdev); +static void as1817_64o_psu_remove(struct platform_device *pdev); + +struct ipmi_data { + struct completion read_complete; + struct ipmi_addr address; + struct ipmi_user *user; + int interface; + struct kernel_ipmi_msg tx_message; + long tx_msgid; + void *rx_msg_data; + unsigned short rx_msg_len; + unsigned char rx_result; + int rx_recv_type; + struct ipmi_user_hndl ipmi_hndlrs; +}; + +struct ipmi_psu_resp_data { + unsigned char status[PSU_STATUS_COUNT]; + unsigned char info[PSU_INFO_SIZE]; + char model[IPMI_MODEL_SERIAL_LEN + 1]; + char serial[IPMI_MODEL_SERIAL_LEN + 1]; + char fandir[IPMI_FAN_DIR_LEN + 1]; +}; + +struct as1817_64o_psu_data { + struct platform_device *pdev[NUM_OF_PSU]; + struct device *hwmon_dev[NUM_OF_PSU]; + struct mutex update_lock; + char valid[NUM_OF_PSU]; + unsigned long last_updated[NUM_OF_PSU]; + struct ipmi_data ipmi; + struct ipmi_psu_resp_data ipmi_resp[NUM_OF_PSU]; + unsigned char ipmi_tx_data[2]; +}; + +static struct as1817_64o_psu_data *data = NULL; + +static struct platform_driver as1817_64o_psu_driver = { + .probe = as1817_64o_psu_probe, + .remove = as1817_64o_psu_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +#define DECLARE_PSU_SENSOR_DEVICE_ATTR() \ + static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_PRESENT); \ + static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_POWER_GOOD); \ + static SENSOR_DEVICE_ATTR(psu_vin, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VIN); \ + static SENSOR_DEVICE_ATTR(psu_vout, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VOUT); \ + static SENSOR_DEVICE_ATTR(psu_iin, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_IIN); \ + static SENSOR_DEVICE_ATTR(psu_iout, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_IOUT); \ + static SENSOR_DEVICE_ATTR(psu_pin, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_PIN); \ + static SENSOR_DEVICE_ATTR(psu_pout, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_POUT); \ + static SENSOR_DEVICE_ATTR(psu_model, S_IRUGO, show_string, \ + NULL, PSU_ATTR_MODEL); \ + static SENSOR_DEVICE_ATTR(psu_serial, S_IRUGO, show_string, \ + NULL, PSU_ATTR_SERIAL); \ + static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP1_INPUT); \ + static SENSOR_DEVICE_ATTR(psu_temp2_input, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP2_INPUT); \ + static SENSOR_DEVICE_ATTR(psu_temp3_input, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP3_INPUT); \ + static SENSOR_DEVICE_ATTR(psu_fan_input, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_FAN_INPUT); \ + static SENSOR_DEVICE_ATTR(psu_fan_max_speed, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_FAN_MAX_SPEED); \ + static SENSOR_DEVICE_ATTR(psu_fan_percentage, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_FAN_PERCENTAGE); \ + static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_string, \ + NULL, PSU_ATTR_FAN_DIR); \ + static SENSOR_DEVICE_ATTR(psu_type, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TYPE); \ + static SENSOR_DEVICE_ATTR(psu_temp1_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP1_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_temp1_low_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP1_LOW_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_temp2_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP2_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_temp2_low_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP2_LOW_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_temp3_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP3_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_temp3_low_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_TEMP3_LOW_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_vin_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VIN_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_vin_low_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VIN_LOW_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_vin_high_warn, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VIN_HIGH_WARN); \ + static SENSOR_DEVICE_ATTR(psu_vin_low_warn, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VIN_LOW_WARN); \ + static SENSOR_DEVICE_ATTR(psu_vout_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VOUT_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_vout_low_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_VOUT_LOW_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_iin_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_IIN_HIGH_CRIT); \ + static SENSOR_DEVICE_ATTR(psu_iout_high_crit, S_IRUGO, show_psu, \ + NULL, PSU_ATTR_IOUT_HIGH_CRIT) + +DECLARE_PSU_SENSOR_DEVICE_ATTR(); + +static struct attribute *as1817_64o_psu_attrs[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_vin.dev_attr.attr, + &sensor_dev_attr_psu_vout.dev_attr.attr, + &sensor_dev_attr_psu_iin.dev_attr.attr, + &sensor_dev_attr_psu_iout.dev_attr.attr, + &sensor_dev_attr_psu_pin.dev_attr.attr, + &sensor_dev_attr_psu_pout.dev_attr.attr, + &sensor_dev_attr_psu_model.dev_attr.attr, + &sensor_dev_attr_psu_serial.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_temp2_input.dev_attr.attr, + &sensor_dev_attr_psu_temp3_input.dev_attr.attr, + &sensor_dev_attr_psu_fan_input.dev_attr.attr, + &sensor_dev_attr_psu_fan_max_speed.dev_attr.attr, + &sensor_dev_attr_psu_fan_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_type.dev_attr.attr, + &sensor_dev_attr_psu_temp1_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_temp1_low_crit.dev_attr.attr, + &sensor_dev_attr_psu_temp2_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_temp2_low_crit.dev_attr.attr, + &sensor_dev_attr_psu_temp3_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_temp3_low_crit.dev_attr.attr, + &sensor_dev_attr_psu_vin_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_vin_low_crit.dev_attr.attr, + &sensor_dev_attr_psu_vin_high_warn.dev_attr.attr, + &sensor_dev_attr_psu_vin_low_warn.dev_attr.attr, + &sensor_dev_attr_psu_vout_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_vout_low_crit.dev_attr.attr, + &sensor_dev_attr_psu_iin_high_crit.dev_attr.attr, + &sensor_dev_attr_psu_iout_high_crit.dev_attr.attr, + NULL +}; + +static struct attribute_group as1817_64o_psu_group = { .attrs = as1817_64o_psu_attrs }; + +static const struct attribute_group *as1817_64o_psu_groups[] = { + &as1817_64o_psu_group, NULL +}; + +/* IPMI functions */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface) +{ + int err; + + init_completion(&ipmi->read_complete); + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + pr_err("Unable to register user with IPMI interface %d\n", iface); + return -EACCES; + } + return 0; +} + +static int _ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err; + + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_data; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_data; + ipmi->rx_msg_len = rx_len; + + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) + return err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + return err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + return -ETIMEDOUT; + + return 0; +} + +static int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int status = 0, retry = 0; + + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + status = _ipmi_send_message(ipmi, cmd, tx_data, tx_len, + rx_data, rx_len); + if (unlikely(status != 0)) + continue; + if (unlikely(ipmi->rx_result != 0)) + continue; + break; + } + return status; +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + if (msg->msgid != ipmi->tx_msgid) { + ipmi_free_recv_msg(msg); + return; + } + + ipmi->rx_recv_type = msg->recv_type; + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +static void as1817_64o_psu_update(unsigned char pid) +{ + int status; + + if (time_before(jiffies, data->last_updated[pid] + HZ * 5) && data->valid[pid]) + return; + + data->valid[pid] = 0; + data->ipmi_resp[pid].status[PSU_VOUT_MODE] = 0xff; + + data->ipmi_tx_data[0] = pid + 1; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp[pid].status, + sizeof(data->ipmi_resp[pid].status)); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->ipmi_tx_data[1] = IPMI_PSU_MODEL_NAME_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].model, + sizeof(data->ipmi_resp[pid].model) - 1); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->ipmi_tx_data[1] = IPMI_PSU_SERIAL_NUM_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].serial, + sizeof(data->ipmi_resp[pid].serial) - 1); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->ipmi_tx_data[1] = IPMI_PSU_FAN_DIR_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].fandir, + sizeof(data->ipmi_resp[pid].fandir) - 1); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->ipmi_tx_data[1] = IPMI_PSU_INFO_CMD; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_READ_CMD, + data->ipmi_tx_data, 2, + data->ipmi_resp[pid].info, + sizeof(data->ipmi_resp[pid].info)); + if (unlikely(status != 0 || data->ipmi.rx_result != 0)) + return; + + data->last_updated[pid] = jiffies; + data->valid[pid] = 1; +} + +#define VALIDATE_PRESENT_RETURN(pid) \ +do { \ + if (data->ipmi_resp[pid].status[PSU_PRESENT] != 1) { \ + mutex_unlock(&data->update_lock); \ + return -ENXIO; \ + } \ +} while (0) + +static ssize_t show_psu(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct platform_device *pdev = dev_get_drvdata(dev); + unsigned char pid = pdev->id; + int value = 0; + int error = 0; + + mutex_lock(&data->update_lock); + as1817_64o_psu_update(pid); + if (!data->valid[pid]) { + error = -EIO; + goto exit; + } + + switch (attr->index) { + case PSU_ATTR_PRESENT: + value = (data->ipmi_resp[pid].status[PSU_PRESENT] == 1) ? 1 : 0; + break; + case PSU_ATTR_POWER_GOOD: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].status[PSU_POWER_GOOD_PMBUS]; + break; + case PSU_ATTR_VIN: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_VIN0] | + (u32)data->ipmi_resp[pid].status[PSU_VIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_VIN2] << 16; + break; + case PSU_ATTR_VOUT: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_VOUT0] | + (u32)data->ipmi_resp[pid].status[PSU_VOUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_VOUT2] << 16; + break; + case PSU_ATTR_IIN: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_IIN0] | + (u32)data->ipmi_resp[pid].status[PSU_IIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_IIN2] << 16; + break; + case PSU_ATTR_IOUT: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_IOUT0] | + (u32)data->ipmi_resp[pid].status[PSU_IOUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_IOUT2] << 16; + break; + case PSU_ATTR_PIN: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_PIN0] | + (u32)data->ipmi_resp[pid].status[PSU_PIN1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_PIN2] << 16 | + (u32)data->ipmi_resp[pid].status[PSU_PIN3] << 24; + value /= 1000; /* microWatt to milliWatt */ + break; + case PSU_ATTR_POUT: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_POUT0] | + (u32)data->ipmi_resp[pid].status[PSU_POUT1] << 8 | + (u32)data->ipmi_resp[pid].status[PSU_POUT2] << 16 | + (u32)data->ipmi_resp[pid].status[PSU_POUT3] << 24; + value /= 1000; /* microWatt to milliWatt */ + break; + case PSU_ATTR_TEMP1_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP1_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP1_1] << 8); + value *= 1000; /* to millidegree */ + break; + case PSU_ATTR_TEMP2_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP2_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP2_1] << 8); + value *= 1000; + break; + case PSU_ATTR_TEMP3_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (s16)((u16)data->ipmi_resp[pid].status[PSU_TEMP3_0] | + (u16)data->ipmi_resp[pid].status[PSU_TEMP3_1] << 8); + value *= 1000; + break; + case PSU_ATTR_FAN_INPUT: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_FAN0] | + (u32)data->ipmi_resp[pid].status[PSU_FAN1] << 8; + break; + case PSU_ATTR_FAN_MAX_SPEED: + value = 35000; + break; + case PSU_ATTR_FAN_PERCENTAGE: + VALIDATE_PRESENT_RETURN(pid); + value = (u32)data->ipmi_resp[pid].status[PSU_FAN0] | + (u32)data->ipmi_resp[pid].status[PSU_FAN1] << 8; + value = DIV_ROUND_CLOSEST(value * 100, 35000); + if (value > 100) + value = 100; + break; + case PSU_ATTR_TYPE: + VALIDATE_PRESENT_RETURN(pid); + mutex_unlock(&data->update_lock); + return sprintf(buf, "%s\n", + data->ipmi_resp[pid].info[PSU_INFO_TYPE] ? "AC" : "DC"); + case PSU_ATTR_TEMP1_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP1_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP1_HCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_TEMP1_LOW_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP1_LCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP1_LCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_TEMP2_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP2_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP2_HCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_TEMP2_LOW_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP2_LCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP2_LCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_TEMP3_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP3_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP3_HCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_TEMP3_LOW_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = (data->ipmi_resp[pid].info[PSU_INFO_TEMP3_LCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_TEMP3_LCRIT + 1] << 8) * 1000; + break; + case PSU_ATTR_VIN_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VIN_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_VIN_HCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VIN_HCRIT + 2] << 16; + break; + case PSU_ATTR_VIN_LOW_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VIN_LCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_VIN_LCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VIN_LCRIT + 2] << 16; + break; + case PSU_ATTR_VIN_HIGH_WARN: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VIN_HWARN] | + data->ipmi_resp[pid].info[PSU_INFO_VIN_HWARN + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VIN_HWARN + 2] << 16; + break; + case PSU_ATTR_VIN_LOW_WARN: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VIN_LWARN] | + data->ipmi_resp[pid].info[PSU_INFO_VIN_LWARN + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VIN_LWARN + 2] << 16; + break; + case PSU_ATTR_VOUT_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VOUT_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_VOUT_HCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VOUT_HCRIT + 2] << 16; + break; + case PSU_ATTR_VOUT_LOW_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_VOUT_LCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_VOUT_LCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_VOUT_LCRIT + 2] << 16; + break; + case PSU_ATTR_IIN_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_IIN_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_IIN_HCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_IIN_HCRIT + 2] << 16; + break; + case PSU_ATTR_IOUT_HIGH_CRIT: + VALIDATE_PRESENT_RETURN(pid); + value = data->ipmi_resp[pid].info[PSU_INFO_IOUT_HCRIT] | + data->ipmi_resp[pid].info[PSU_INFO_IOUT_HCRIT + 1] << 8 | + data->ipmi_resp[pid].info[PSU_INFO_IOUT_HCRIT + 2] << 16; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", value); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static ssize_t show_string(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct platform_device *pdev = dev_get_drvdata(dev); + unsigned char pid = pdev->id; + char *str = NULL; + int error = 0; + + mutex_lock(&data->update_lock); + as1817_64o_psu_update(pid); + if (!data->valid[pid]) { + error = -EIO; + goto exit; + } + + VALIDATE_PRESENT_RETURN(pid); + + switch (attr->index) { + case PSU_ATTR_MODEL: + str = data->ipmi_resp[pid].model; + break; + case PSU_ATTR_SERIAL: + str = data->ipmi_resp[pid].serial; + break; + case PSU_ATTR_FAN_DIR: + str = data->ipmi_resp[pid].fandir; + break; + default: + error = -EINVAL; + goto exit; + } + + mutex_unlock(&data->update_lock); + return sprintf(buf, "%s\n", str); + +exit: + mutex_unlock(&data->update_lock); + return error; +} + +static int as1817_64o_psu_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + + hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + pdev, as1817_64o_psu_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + data->hwmon_dev[pdev->id] = hwmon_dev; + dev_info(&pdev->dev, "PSU%d device created\n", pdev->id + 1); + return 0; +} + +static void as1817_64o_psu_remove(struct platform_device *pdev) +{ + if (data->hwmon_dev[pdev->id]) { + hwmon_device_unregister(data->hwmon_dev[pdev->id]); + data->hwmon_dev[pdev->id] = NULL; + } +} + +static int __init as1817_64o_psu_init(void) +{ + int ret, i; + + data = kzalloc(sizeof(struct as1817_64o_psu_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as1817_64o_psu_driver); + if (ret < 0) + goto dri_reg_err; + + ret = init_ipmi_data(&data->ipmi, 0); + if (ret) + goto ipmi_err; + + for (i = 0; i < NUM_OF_PSU; i++) { + data->pdev[i] = platform_device_register_simple(DRVNAME, i, NULL, 0); + if (IS_ERR(data->pdev[i])) { + ret = PTR_ERR(data->pdev[i]); + goto dev_reg_err; + } + } + + return 0; + +dev_reg_err: + while (--i >= 0) + platform_device_unregister(data->pdev[i]); + ipmi_destroy_user(data->ipmi.user); +ipmi_err: + platform_driver_unregister(&as1817_64o_psu_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as1817_64o_psu_exit(void) +{ + int i; + + for (i = 0; i < NUM_OF_PSU; i++) + platform_device_unregister(data->pdev[i]); + platform_driver_unregister(&as1817_64o_psu_driver); + ipmi_destroy_user(data->ipmi.user); + kfree(data); +} + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("as1817_64o_psu driver"); +MODULE_LICENSE("GPL"); + +module_init(as1817_64o_psu_init); +module_exit(as1817_64o_psu_exit); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-sys.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-sys.c new file mode 100644 index 000000000..73fb216a5 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-sys.c @@ -0,0 +1,408 @@ +/* + * A sys driver for accton as1817_64o System EEPROM + * + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as1817_64o_sys" +#define ACCTON_IPMI_NETFN 0x34 + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 +#define IPMI_READ_MAX_LEN 128 + +#define EEPROM_NAME "eeprom" +#define EEPROM_SIZE 256 + +#define IPMI_SYSEEPROM_READ_CMD 0x18 +#define IPMI_CPLD_VERSION_CMD 0x20 + +enum cpld_version_id { + CPU_CPLD_VERSION, + FPGA_VERSION, + FAN_CPLD_VERSION, + DCSCM_CPLD_VERSION, + SYS_CPLD_VERSION, + CPLD1_VERSION, + CPLD2_VERSION, + NUM_CPLD_VERSIONS, +}; + +static const unsigned char cpld_addr[] = { + [CPU_CPLD_VERSION] = 0x21, + [FPGA_VERSION] = 0x60, + [FAN_CPLD_VERSION] = 0x33, + [DCSCM_CPLD_VERSION] = 0x06, + [SYS_CPLD_VERSION] = 0x61, + [CPLD1_VERSION] = 0x64, + [CPLD2_VERSION] = 0x65, +}; + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static int as1817_64o_sys_probe(struct platform_device *pdev); +static void as1817_64o_sys_remove(struct platform_device *pdev); + +struct ipmi_data { + struct completion read_complete; + struct ipmi_addr address; + struct ipmi_user *user; + int interface; + + struct kernel_ipmi_msg tx_message; + long tx_msgid; + + void *rx_msg_data; + unsigned short rx_msg_len; + unsigned char rx_result; + int rx_recv_type; + + struct ipmi_user_hndl ipmi_hndlrs; +}; + +struct as1817_64o_sys_data { + struct platform_device *pdev; + struct mutex update_lock; + struct ipmi_data ipmi; + unsigned char ipmi_resp_eeprom[EEPROM_SIZE]; + unsigned char ipmi_tx_data[2]; + struct bin_attribute eeprom; +}; + +static struct as1817_64o_sys_data *data = NULL; + +static struct platform_driver as1817_64o_sys_driver = { + .probe = as1817_64o_sys_probe, + .remove = as1817_64o_sys_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +/* IPMI interface functions */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface, struct device *dev) +{ + int err; + + init_completion(&ipmi->read_complete); + + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + dev_err(dev, "Unable to register user with IPMI interface %d\n", + ipmi->interface); + return -EACCES; + } + + return 0; +} + +static int _ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err; + + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_data; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_data; + ipmi->rx_msg_len = rx_len; + + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) + return err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + return err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + return -ETIMEDOUT; + + return 0; +} + +static int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int status = 0, retry = 0; + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + status = _ipmi_send_message(ipmi, cmd, tx_data, tx_len, + rx_data, rx_len); + if (unlikely(status != 0)) + continue; + if (unlikely(ipmi->rx_result != 0)) + continue; + break; + } + + return status; +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + if (msg->msgid != ipmi->tx_msgid) { + ipmi_free_recv_msg(msg); + return; + } + + ipmi->rx_recv_type = msg->recv_type; + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +/* EEPROM read via IPMI OEM command 0x18 */ +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned char resp[2] = {0}; + unsigned char tx_data; + int status; + + mutex_lock(&data->update_lock); + tx_data = cpld_addr[attr->index]; + status = ipmi_send_message(&data->ipmi, IPMI_CPLD_VERSION_CMD, + &tx_data, 1, resp, sizeof(resp)); + mutex_unlock(&data->update_lock); + + if (status != 0 || data->ipmi.rx_result != 0) + return -EIO; + + return sprintf(buf, "%x.%x\n", resp[0], resp[1]); +} + +static SENSOR_DEVICE_ATTR(cpu_cpld_version, S_IRUGO, show_version, NULL, CPU_CPLD_VERSION); +static SENSOR_DEVICE_ATTR(fpga_version, S_IRUGO, show_version, NULL, FPGA_VERSION); +static SENSOR_DEVICE_ATTR(fan_cpld_version, S_IRUGO, show_version, NULL, FAN_CPLD_VERSION); +static SENSOR_DEVICE_ATTR(dcscm_cpld_version, S_IRUGO, show_version, NULL, DCSCM_CPLD_VERSION); +static SENSOR_DEVICE_ATTR(sys_cpld_version, S_IRUGO, show_version, NULL, SYS_CPLD_VERSION); +static SENSOR_DEVICE_ATTR(cpld1_version, S_IRUGO, show_version, NULL, CPLD1_VERSION); +static SENSOR_DEVICE_ATTR(cpld2_version, S_IRUGO, show_version, NULL, CPLD2_VERSION); + +static struct attribute *as1817_64o_sys_attrs[] = { + &sensor_dev_attr_cpu_cpld_version.dev_attr.attr, + &sensor_dev_attr_fpga_version.dev_attr.attr, + &sensor_dev_attr_fan_cpld_version.dev_attr.attr, + &sensor_dev_attr_dcscm_cpld_version.dev_attr.attr, + &sensor_dev_attr_sys_cpld_version.dev_attr.attr, + &sensor_dev_attr_cpld1_version.dev_attr.attr, + &sensor_dev_attr_cpld2_version.dev_attr.attr, + NULL, +}; + +static const struct attribute_group as1817_64o_sys_group = { + .attrs = as1817_64o_sys_attrs, +}; + +/* EEPROM read via IPMI OEM command 0x18 */ +static ssize_t sys_eeprom_read(loff_t off, char *buf, size_t count) +{ + int status = 0; + unsigned char length; + + if ((off + count) > EEPROM_SIZE) + return -EINVAL; + + length = (count >= IPMI_READ_MAX_LEN) ? IPMI_READ_MAX_LEN : count; + data->ipmi_tx_data[0] = (off & 0xff); + data->ipmi_tx_data[1] = length; + status = ipmi_send_message(&data->ipmi, IPMI_SYSEEPROM_READ_CMD, + data->ipmi_tx_data, sizeof(data->ipmi_tx_data), + data->ipmi_resp_eeprom + off, length); + if (unlikely(status != 0)) + return status; + + if (unlikely(data->ipmi.rx_result != 0)) + return -EIO; + + memcpy(buf, data->ipmi_resp_eeprom + off, length); + return length; +} + +static ssize_t sysfs_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) + return count; + + mutex_lock(&data->update_lock); + + while (count) { + ssize_t status; + + status = sys_eeprom_read(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 int sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) +{ + sysfs_bin_attr_init(eeprom); + eeprom->attr.name = EEPROM_NAME; + eeprom->attr.mode = S_IRUGO; + eeprom->read = sysfs_bin_read; + eeprom->size = EEPROM_SIZE; + eeprom->write = NULL; + + return sysfs_create_bin_file(kobj, eeprom); +} + +static void sysfs_eeprom_cleanup(struct kobject *kobj, + struct bin_attribute *eeprom) +{ + sysfs_remove_bin_file(kobj, eeprom); +} + +static int as1817_64o_sys_probe(struct platform_device *pdev) +{ + int status; + + status = sysfs_create_group(&pdev->dev.kobj, &as1817_64o_sys_group); + if (status) + return status; + + status = sysfs_eeprom_init(&pdev->dev.kobj, &data->eeprom); + if (status) { + sysfs_remove_group(&pdev->dev.kobj, &as1817_64o_sys_group); + return status; + } + + dev_info(&pdev->dev, "device created\n"); + return 0; +} + +static void as1817_64o_sys_remove(struct platform_device *pdev) +{ + sysfs_eeprom_cleanup(&pdev->dev.kobj, &data->eeprom); + sysfs_remove_group(&pdev->dev.kobj, &as1817_64o_sys_group); +} + +static int __init as1817_64o_sys_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as1817_64o_sys_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as1817_64o_sys_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) + goto ipmi_err; + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as1817_64o_sys_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as1817_64o_sys_exit(void) +{ + platform_device_unregister(data->pdev); + platform_driver_unregister(&as1817_64o_sys_driver); + ipmi_destroy_user(data->ipmi.user); + kfree(data); +} + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("as1817_64o_sys driver"); +MODULE_LICENSE("GPL"); + +module_init(as1817_64o_sys_init); +module_exit(as1817_64o_sys_exit); diff --git a/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-thermal.c b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-thermal.c new file mode 100644 index 000000000..5328e64ec --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/modules/builds/src/x86-64-accton-as1817-64o-thermal.c @@ -0,0 +1,377 @@ +/* + * A thermal driver for accton as1817_64o + * + * Copyright (C) 2026 Accton Technology Corporation. + * rayx_huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as1817_64o_thermal" +#define ACCTON_IPMI_NETFN 0x34 +#define IPMI_THERMAL_READ_CMD 0x12 + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 + +#define THERMAL_COUNT 14 +#define THERMAL_DATA_LEN 3 +#define THERMAL_DATA_SIZE (THERMAL_COUNT * THERMAL_DATA_LEN) + +enum temp_data_index { + TEMP_ADDR, + TEMP_FAULT, + TEMP_INPUT +}; + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static ssize_t show_temp(struct device *dev, struct device_attribute *da, char *buf); +static int as1817_64o_thermal_probe(struct platform_device *pdev); +static void as1817_64o_thermal_remove(struct platform_device *pdev); + +struct ipmi_data { + struct completion read_complete; + struct ipmi_addr address; + struct ipmi_user *user; + int interface; + + struct kernel_ipmi_msg tx_message; + long tx_msgid; + + void *rx_msg_data; + unsigned short rx_msg_len; + unsigned char rx_result; + int rx_recv_type; + + struct ipmi_user_hndl ipmi_hndlrs; +}; + +struct as1817_64o_thermal_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; + unsigned long last_updated; + struct ipmi_data ipmi; + unsigned char ipmi_resp[THERMAL_DATA_SIZE]; +}; + +static struct as1817_64o_thermal_data *data = NULL; + +static struct platform_driver as1817_64o_thermal_driver = { + .probe = as1817_64o_thermal_probe, + .remove = as1817_64o_thermal_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum as1817_64o_thermal_sysfs_attrs { + TEMP1_INPUT, + TEMP2_INPUT, + TEMP3_INPUT, + TEMP4_INPUT, + TEMP5_INPUT, + TEMP6_INPUT, + TEMP7_INPUT, + TEMP8_INPUT, + TEMP9_INPUT, + TEMP10_INPUT, + TEMP11_INPUT, + TEMP12_INPUT, + TEMP13_INPUT, + TEMP14_INPUT, +}; + +#define DECLARE_THERMAL_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(temp##index##_input, S_IRUGO, show_temp, \ + NULL, TEMP##index##_INPUT) + +#define DECLARE_THERMAL_ATTR(index) \ + &sensor_dev_attr_temp##index##_input.dev_attr.attr + +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(1); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(2); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(3); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(4); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(5); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(6); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(7); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(8); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(9); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(10); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(11); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(12); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(13); +DECLARE_THERMAL_SENSOR_DEVICE_ATTR(14); + +static struct attribute *as1817_64o_thermal_attrs[] = { + DECLARE_THERMAL_ATTR(1), + DECLARE_THERMAL_ATTR(2), + DECLARE_THERMAL_ATTR(3), + DECLARE_THERMAL_ATTR(4), + DECLARE_THERMAL_ATTR(5), + DECLARE_THERMAL_ATTR(6), + DECLARE_THERMAL_ATTR(7), + DECLARE_THERMAL_ATTR(8), + DECLARE_THERMAL_ATTR(9), + DECLARE_THERMAL_ATTR(10), + DECLARE_THERMAL_ATTR(11), + DECLARE_THERMAL_ATTR(12), + DECLARE_THERMAL_ATTR(13), + DECLARE_THERMAL_ATTR(14), + NULL +}; +ATTRIBUTE_GROUPS(as1817_64o_thermal); + +/* IPMI functions */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface, struct device *dev) +{ + int err; + + init_completion(&ipmi->read_complete); + + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = ACCTON_IPMI_NETFN; + + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + err = ipmi_create_user(ipmi->interface, &ipmi->ipmi_hndlrs, + ipmi, &ipmi->user); + if (err < 0) { + dev_err(dev, "Unable to register user with IPMI interface %d\n", + ipmi->interface); + return -EACCES; + } + + return 0; +} + +static int _ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int err; + + ipmi->tx_message.cmd = cmd; + ipmi->tx_message.data = tx_data; + ipmi->tx_message.data_len = tx_len; + ipmi->rx_msg_data = rx_data; + ipmi->rx_msg_len = rx_len; + + err = ipmi_validate_addr(&ipmi->address, sizeof(ipmi->address)); + if (err) + return err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + return err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + return -ETIMEDOUT; + + return 0; +} + +static int ipmi_send_message(struct ipmi_data *ipmi, unsigned char cmd, + unsigned char *tx_data, unsigned short tx_len, + unsigned char *rx_data, unsigned short rx_len) +{ + int status = 0, retry = 0; + + for (retry = 0; retry <= IPMI_ERR_RETRY_TIMES; retry++) { + status = _ipmi_send_message(ipmi, cmd, tx_data, tx_len, + rx_data, rx_len); + if (unlikely(status != 0)) + continue; + if (unlikely(ipmi->rx_result != 0)) + continue; + break; + } + + return status; +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + unsigned short rx_len; + struct ipmi_data *ipmi = user_msg_data; + + if (msg->msgid != ipmi->tx_msgid) { + ipmi_free_recv_msg(msg); + return; + } + + ipmi->rx_recv_type = msg->recv_type; + if (msg->msg.data_len > 0) + ipmi->rx_result = msg->msg.data[0]; + else + ipmi->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; + + if (msg->msg.data_len > 1) { + rx_len = msg->msg.data_len - 1; + if (ipmi->rx_msg_len < rx_len) + rx_len = ipmi->rx_msg_len; + ipmi->rx_msg_len = rx_len; + memcpy(ipmi->rx_msg_data, msg->msg.data + 1, ipmi->rx_msg_len); + } else { + ipmi->rx_msg_len = 0; + } + + ipmi_free_recv_msg(msg); + complete(&ipmi->read_complete); +} + +/* Thermal show function using IPMI 0x12 */ +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + int status = 0; + int index; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 5) || !data->valid) { + data->valid = 0; + + status = ipmi_send_message(&data->ipmi, IPMI_THERMAL_READ_CMD, + NULL, 0, data->ipmi_resp, + sizeof(data->ipmi_resp)); + if (unlikely(status != 0)) + goto exit; + + if (unlikely(data->ipmi.rx_result != 0)) { + status = -EIO; + goto exit; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + /* Check fault status */ + index = attr->index * THERMAL_DATA_LEN + TEMP_FAULT; + if (unlikely(data->ipmi_resp[index] == 0)) { + status = -EIO; + goto exit; + } + + /* Return temperature in millidegrees */ + index = attr->index * THERMAL_DATA_LEN + TEMP_INPUT; + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", ((s8)data->ipmi_resp[index]) * 1000); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as1817_64o_thermal_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + + hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + NULL, as1817_64o_thermal_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + data->hwmon_dev = hwmon_dev; + dev_info(&pdev->dev, "device created\n"); + return 0; +} + +static void as1817_64o_thermal_remove(struct platform_device *pdev) +{ + if (data->hwmon_dev) { + hwmon_device_unregister(data->hwmon_dev); + data->hwmon_dev = NULL; + } +} + +static int __init as1817_64o_thermal_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as1817_64o_thermal_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as1817_64o_thermal_driver); + if (ret < 0) + goto dri_reg_err; + + data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(data->pdev)) { + ret = PTR_ERR(data->pdev); + goto dev_reg_err; + } + + ret = init_ipmi_data(&data->ipmi, 0, &data->pdev->dev); + if (ret) + goto ipmi_err; + + return 0; + +ipmi_err: + platform_device_unregister(data->pdev); +dev_reg_err: + platform_driver_unregister(&as1817_64o_thermal_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as1817_64o_thermal_exit(void) +{ + platform_device_unregister(data->pdev); + platform_driver_unregister(&as1817_64o_thermal_driver); + ipmi_destroy_user(data->ipmi.user); + kfree(data); +} + +MODULE_AUTHOR("rayx_huang "); +MODULE_DESCRIPTION("as1817_64o_thermal driver"); +MODULE_LICENSE("GPL"); + +module_init(as1817_64o_thermal_init); +module_exit(as1817_64o_thermal_exit); diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/PKG.yml b/packages/platforms/accton/x86-64/as1817-64o/onlp/PKG.yml new file mode 100644 index 000000000..0125d0f2e --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/onlp-platform-any.yml PLATFORM=x86-64-accton-as1817-64o ARCH=amd64 TOOLCHAIN=x86_64-linux-gnu diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/Makefile new file mode 100644 index 000000000..e7437cb23 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/Makefile @@ -0,0 +1,2 @@ +FILTER=src +include $(ONL)/make/subdirs.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/lib/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/lib/Makefile new file mode 100644 index 000000000..f22d3f1cf --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/lib/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as1817-64o +include $(ONL)/packages/base/any/onlp/builds/platform/libonlp-platform.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/onlpdump/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/onlpdump/Makefile new file mode 100644 index 000000000..b8d8a3058 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/onlpdump/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as1817-64o +include $(ONL)/packages/base/any/onlp/builds/platform/onlps.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.gitignore b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.gitignore new file mode 100644 index 000000000..f071caeb4 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.gitignore @@ -0,0 +1 @@ +x86_64_accton_as1817_64o.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.module b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.module new file mode 100644 index 000000000..454b16524 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/.module @@ -0,0 +1 @@ +name: x86_64_accton_as1817_64o diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/Makefile new file mode 100644 index 000000000..62f11d27e --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +include $(ONL)/make/config.mk +MODULE := x86_64_accton_as1817_64o +AUTOMODULE := x86_64_accton_as1817_64o +include $(BUILDER)/definemodule.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/README b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/README new file mode 100644 index 000000000..d0ab6421e --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/README @@ -0,0 +1,6 @@ +############################################################################### +# +# x86_64_accton_as1817_64o README +# +############################################################################### + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/make.mk b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/make.mk new file mode 100644 index 000000000..4014aa760 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/make.mk @@ -0,0 +1,8 @@ +############################################################################### +# +# x86_64_accton_as1817_64o Autogeneration +# +############################################################################### +x86_64_accton_as1817_64o_AUTO_DEFS := module/auto/x86_64_accton_as1817_64o.yml +x86_64_accton_as1817_64o_AUTO_DIRS := module/inc/x86_64_accton_as1817_64o module/src +include $(BUILDER)/auto.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/x86_64_accton_as1817_64o.yml b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/x86_64_accton_as1817_64o.yml new file mode 100644 index 000000000..f20ea76ae --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/auto/x86_64_accton_as1817_64o.yml @@ -0,0 +1,50 @@ +############################################################################### +# +# x86_64_accton_as1817_64o Autogeneration Definitions. +# +############################################################################### + +cdefs: &cdefs +- X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING: + doc: "Include or exclude logging." + default: 1 +- X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT: + doc: "Default enabled log options." + default: AIM_LOG_OPTIONS_DEFAULT +- X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT: + doc: "Default enabled log bits." + default: AIM_LOG_BITS_DEFAULT +- X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT: + doc: "Default enabled custom log bits." + default: 0 +- X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB: + doc: "Default all porting macros to use the C standard libraries." + default: 1 +- X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS: + doc: "Include standard library headers for stdlib porting macros." + default: X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB +- X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI: + doc: "Include generic uCli support." + default: 0 +- X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION: + doc: "Assume chassis fan direction is the same as the PSU fan direction." + default: 0 + + +definitions: + cdefs: + X86_64_ACCTON_AS1817_64O_CONFIG_HEADER: + defs: *cdefs + basename: x86_64_accton_as1817_64o_config + + portingmacro: + x86_64_accton_as1817_64o: + macros: + - malloc + - free + - memset + - memcpy + + - vsnprintf + - snprintf + - strlen diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o.x b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o.x new file mode 100644 index 000000000..254b3ae65 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o.x @@ -0,0 +1,14 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.xmacro(ALL).define> */ +/* */ + +/* <--auto.start.xenum(ALL).define> */ +/* */ + + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_config.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_config.h new file mode 100644 index 000000000..24c405f1c --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_config.h @@ -0,0 +1,137 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as1817_64o Configuration Header + * + * @addtogroup x86_64_accton_as1817_64o-config + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_AS1817_64O_CONFIG_H__ +#define __X86_64_ACCTON_AS1817_64O_CONFIG_H__ + +#ifdef GLOBAL_INCLUDE_CUSTOM_CONFIG +#include +#endif +#ifdef X86_64_ACCTON_AS1817_64O_INCLUDE_CUSTOM_CONFIG +#include +#endif + +/* */ +#include +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING + * + * Include or exclude logging. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING +#define X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING 1 +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT + * + * Default enabled log options. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT +#define X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT AIM_LOG_OPTIONS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT + * + * Default enabled log bits. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT +#define X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT AIM_LOG_BITS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT + * + * Default enabled custom log bits. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT +#define X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT 0 +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB + * + * Default all porting macros to use the C standard libraries. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB +#define X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB 1 +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + * + * Include standard library headers for stdlib porting macros. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS +#define X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI + * + * Include generic uCli support. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI +#define X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI 0 +#endif + +/** + * X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + * + * Assume chassis fan direction is the same as the PSU fan direction. */ + + +#ifndef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION +#define X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION 0 +#endif + + + +/** + * All compile time options can be queried or displayed + */ + +/** Configuration settings structure. */ +typedef struct x86_64_accton_as1817_64o_config_settings_s { + /** name */ + const char* name; + /** value */ + const char* value; +} x86_64_accton_as1817_64o_config_settings_t; + +/** Configuration settings table. */ +/** x86_64_accton_as1817_64o_config_settings table. */ +extern x86_64_accton_as1817_64o_config_settings_t x86_64_accton_as1817_64o_config_settings[]; + +/** + * @brief Lookup a configuration setting. + * @param setting The name of the configuration option to lookup. + */ +const char* x86_64_accton_as1817_64o_config_lookup(const char* setting); + +/** + * @brief Show the compile-time configuration. + * @param pvs The output stream. + */ +int x86_64_accton_as1817_64o_config_show(struct aim_pvs_s* pvs); + +/* */ + +#include "x86_64_accton_as1817_64o_porting.h" + +#endif /* __X86_64_ACCTON_AS1817_64O_CONFIG_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_dox.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_dox.h new file mode 100644 index 000000000..287d98a0e --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_dox.h @@ -0,0 +1,26 @@ +/**************************************************************************//** + * + * x86_64_accton_as1817_64o Doxygen Header + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_AS1817_64O_DOX_H__ +#define __X86_64_ACCTON_AS1817_64O_DOX_H__ + +/** + * @defgroup x86_64_accton_as1817_64o x86_64_accton_as1817_64o - x86_64_accton_as1817_64o Description + * + +The documentation overview for this module should go here. + + * + * @{ + * + * @defgroup x86_64_accton_as1817_64o-x86_64_accton_as1817_64o Public Interface + * @defgroup x86_64_accton_as1817_64o-config Compile Time Configuration + * @defgroup x86_64_accton_as1817_64o-porting Porting Macros + * + * @} + * + */ + +#endif /* __X86_64_ACCTON_AS1817_64O_DOX_H__ */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_porting.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_porting.h new file mode 100644 index 000000000..90eadee43 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/inc/x86_64_accton_as1817_64o/x86_64_accton_as1817_64o_porting.h @@ -0,0 +1,97 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as1817_64o Porting Macros. + * + * @addtogroup x86_64_accton_as1817_64o-porting + * @{ + * + *****************************************************************************/ +#ifndef __X86_64_ACCTON_AS1817_64O_PORTING_H__ +#define __X86_64_ACCTON_AS1817_64O_PORTING_H__ + + +/* */ +#if X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS == 1 +#include +#include +#include +#include +#include +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_MALLOC + #if defined(GLOBAL_MALLOC) + #define X86_64_ACCTON_AS1817_64O_MALLOC GLOBAL_MALLOC + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_MALLOC malloc + #else + #error The macro X86_64_ACCTON_AS1817_64O_MALLOC is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_FREE + #if defined(GLOBAL_FREE) + #define X86_64_ACCTON_AS1817_64O_FREE GLOBAL_FREE + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_FREE free + #else + #error The macro X86_64_ACCTON_AS1817_64O_FREE is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_MEMSET + #if defined(GLOBAL_MEMSET) + #define X86_64_ACCTON_AS1817_64O_MEMSET GLOBAL_MEMSET + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_MEMSET memset + #else + #error The macro X86_64_ACCTON_AS1817_64O_MEMSET is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_MEMCPY + #if defined(GLOBAL_MEMCPY) + #define X86_64_ACCTON_AS1817_64O_MEMCPY GLOBAL_MEMCPY + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_MEMCPY memcpy + #else + #error The macro X86_64_ACCTON_AS1817_64O_MEMCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_VSNPRINTF + #if defined(GLOBAL_VSNPRINTF) + #define X86_64_ACCTON_AS1817_64O_VSNPRINTF GLOBAL_VSNPRINTF + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_VSNPRINTF vsnprintf + #else + #error The macro X86_64_ACCTON_AS1817_64O_VSNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_SNPRINTF + #if defined(GLOBAL_SNPRINTF) + #define X86_64_ACCTON_AS1817_64O_SNPRINTF GLOBAL_SNPRINTF + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_SNPRINTF snprintf + #else + #error The macro X86_64_ACCTON_AS1817_64O_SNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS1817_64O_STRLEN + #if defined(GLOBAL_STRLEN) + #define X86_64_ACCTON_AS1817_64O_STRLEN GLOBAL_STRLEN + #elif X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS1817_64O_STRLEN strlen + #else + #error The macro X86_64_ACCTON_AS1817_64O_STRLEN is required but cannot be defined. + #endif +#endif + +/* */ + + +#endif /* __X86_64_ACCTON_AS1817_64O_PORTING_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/make.mk b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/make.mk new file mode 100644 index 000000000..64b90d309 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/make.mk @@ -0,0 +1,10 @@ +############################################################################### +# +# +# +############################################################################### +THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +x86_64_accton_as1817_64o_INCLUDES := -I $(THIS_DIR)inc +x86_64_accton_as1817_64o_INTERNAL_INCLUDES := -I $(THIS_DIR)src +x86_64_accton_as1817_64o_DEPENDMODULE_ENTRIES := init:x86_64_accton_as1817_64o ucli:x86_64_accton_as1817_64o + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/Makefile b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/Makefile new file mode 100644 index 000000000..a192b218e --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# Local source generation targets. +# +############################################################################### + +ucli: + @../../../../tools/uclihandlers.py x86_64_accton_as1817_64o_ucli.c + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/fani.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/fani.c new file mode 100644 index 000000000..4646b4073 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/fani.c @@ -0,0 +1,247 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Fan Platform Implementation. + * + ***********************************************************/ +#include +#include +#include +#include "platform_lib.h" + +#define FAN_SYSFS_FMT "/sys/devices/platform/as1817_64o_fan*fan%d_%s" +#define PSU_FAN_SYSFS_FMT "/sys/devices/platform/as1817_64o_psu.%d*psu_fan_input" +#define PSU_FAN_PCT_FMT "/sys/devices/platform/as1817_64o_psu.%d*psu_fan_percentage" +#define PSU_POWER_GOOD_FMT "/sys/devices/platform/as1817_64o_psu.%d*psu_power_good" +#define PSU_FAN_DIR_FMT "/sys/devices/platform/as1817_64o_psu.%d*psu_fan_dir" + +/* + * Reference maps 16 chassis fans as individual rotors: + * fan@1-4: Top Front Fan 1-4 (sysfs fan1-4_front_input) + * fan@5-8: Top Rear Fan 1-4 (sysfs fan1-4_rear_input) + * fan@9-12: Bottom Front Fan 1-4 (sysfs fan5-8_front_input) + * fan@13-16: Bottom Rear Fan 1-4 (sysfs fan5-8_rear_input) + * fan@17-20: PSU 1-4 fans + */ +enum fan_id { + FAN_1 = 1, FAN_2, FAN_3, FAN_4, + FAN_5, FAN_6, FAN_7, FAN_8, + FAN_9, FAN_10, FAN_11, FAN_12, + FAN_13, FAN_14, FAN_15, FAN_16, + FAN_PSU_1, FAN_PSU_2, FAN_PSU_3, FAN_PSU_4, +}; + +/* Map ONLP fan ID to kernel sysfs fan module (1-8) and side (front/rear) */ +struct fan_rotor_map { + int sysfs_fid; /* kernel fan module 1-8 */ + const char *side; /* "front" or "rear" */ +}; + +static const struct fan_rotor_map rotor_map[CHASSIS_FAN_COUNT] = { + { 1, "front" }, /* Fan 1 Front */ + { 1, "rear" }, /* Fan 1 Rear */ + { 2, "front" }, /* Fan 2 Front */ + { 2, "rear" }, /* Fan 2 Rear */ + { 3, "front" }, /* Fan 3 Front */ + { 3, "rear" }, /* Fan 3 Rear */ + { 4, "front" }, /* Fan 4 Front */ + { 4, "rear" }, /* Fan 4 Rear */ + { 5, "front" }, /* Fan 5 Front */ + { 5, "rear" }, /* Fan 5 Rear */ + { 6, "front" }, /* Fan 6 Front */ + { 6, "rear" }, /* Fan 6 Rear */ + { 7, "front" }, /* Fan 7 Front */ + { 7, "rear" }, /* Fan 7 Rear */ + { 8, "front" }, /* Fan 8 Front */ + { 8, "rear" }, /* Fan 8 Rear */ +}; + +#define CHASSIS_FAN_CAPS (ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE) + +static onlp_fan_info_t chassis_finfo[] = { + { }, /* Not used (index 0) */ + { { ONLP_FAN_ID_CREATE(1), "Chassis Fan - 1 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(2), "Chassis Fan - 1 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(3), "Chassis Fan - 2 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(4), "Chassis Fan - 2 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(5), "Chassis Fan - 3 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(6), "Chassis Fan - 3 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(7), "Chassis Fan - 4 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(8), "Chassis Fan - 4 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(9), "Chassis Fan - 5 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(10), "Chassis Fan - 5 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(11), "Chassis Fan - 6 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(12), "Chassis Fan - 6 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(13), "Chassis Fan - 7 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(14), "Chassis Fan - 7 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(15), "Chassis Fan - 8 Front Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(16), "Chassis Fan - 8 Rear Fan", 0 }, 0, CHASSIS_FAN_CAPS, 0, 0, ONLP_FAN_MODE_INVALID }, +}; + +static onlp_fan_info_t psu_finfo[] = { + { { ONLP_FAN_ID_CREATE(FAN_PSU_1), "PSU-1 Fan", ONLP_PSU_ID_CREATE(PSU1_ID) }, + 0x0, ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(FAN_PSU_2), "PSU-2 Fan", ONLP_PSU_ID_CREATE(PSU2_ID) }, + 0x0, ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(FAN_PSU_3), "PSU-3 Fan", ONLP_PSU_ID_CREATE(PSU3_ID) }, + 0x0, ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, 0, 0, ONLP_FAN_MODE_INVALID }, + { { ONLP_FAN_ID_CREATE(FAN_PSU_4), "PSU-4 Fan", ONLP_PSU_ID_CREATE(PSU4_ID) }, + 0x0, ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE, 0, 0, ONLP_FAN_MODE_INVALID }, +}; + +#define VALIDATE(_id) \ +do { \ + if(!ONLP_OID_IS_FAN(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ +} while(0) + +static int _onlp_fani_info_get_chassis(int fid, onlp_fan_info_t* info) +{ + int idx = fid - 1; + const struct fan_rotor_map *m = &rotor_map[idx]; + int value = 0; + char attr[32]; + + /* present — use the sysfs module's present */ + if (onlp_file_read_int(&value, FAN_SYSFS_FMT, m->sysfs_fid, "present") < 0) + return ONLP_STATUS_E_INTERNAL; + + if (value == 0) + return ONLP_STATUS_OK; + + info->status |= ONLP_FAN_STATUS_PRESENT; + + /* direction */ + { + char dir_str[8] = {0}; + int len = 0; + if (onlp_file_read((uint8_t *)dir_str, sizeof(dir_str) - 1, &len, + FAN_SYSFS_FMT, m->sysfs_fid, "dir") == 0) { + if (strncmp(dir_str, "F2B", 3) == 0) + info->status |= ONLP_FAN_STATUS_F2B; + else if (strncmp(dir_str, "B2F", 3) == 0) + info->status |= ONLP_FAN_STATUS_B2F; + } + } + + /* fault */ + snprintf(attr, sizeof(attr), "%s_fault", m->side); + if (onlp_file_read_int(&value, FAN_SYSFS_FMT, m->sysfs_fid, attr) == 0 && value) + info->status |= ONLP_FAN_STATUS_FAILED; + + /* RPM */ + snprintf(attr, sizeof(attr), "%s_input", m->side); + if (onlp_file_read_int(&value, FAN_SYSFS_FMT, m->sysfs_fid, attr) == 0) + info->rpm = value; + + /* percentage */ + snprintf(attr, sizeof(attr), "%s_percentage", m->side); + if (onlp_file_read_int(&value, FAN_SYSFS_FMT, m->sysfs_fid, attr) == 0) + info->percentage = value; + + return ONLP_STATUS_OK; +} + +static int _onlp_fani_info_get_psu(int pid, onlp_fan_info_t* info) +{ + int val = 0; + int len = 0; + char dir[8] = {0}; + + info->status |= ONLP_FAN_STATUS_PRESENT; + + if (onlp_file_read_int(&val, PSU_POWER_GOOD_FMT, pid - 1) == 0) { + if (val != PSU_STATUS_POWER_GOOD) + info->status |= ONLP_FAN_STATUS_FAILED; + } + + if (onlp_file_read_int(&val, PSU_FAN_SYSFS_FMT, pid - 1) == 0) { + info->rpm = val; + } + + if (onlp_file_read_int(&val, PSU_FAN_PCT_FMT, pid - 1) == 0) { + info->percentage = val; + } + + /* Fan direction */ + if (onlp_file_read((uint8_t *)dir, sizeof(dir) - 1, &len, + PSU_FAN_DIR_FMT, pid - 1) == 0) { + if (strncmp(dir, "F2B", 3) == 0) + info->status |= ONLP_FAN_STATUS_F2B; + else if (strncmp(dir, "B2F", 3) == 0) + info->status |= ONLP_FAN_STATUS_B2F; + } + + return ONLP_STATUS_OK; +} + +int onlp_fani_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_fani_info_get(onlp_oid_t id, onlp_fan_info_t* info) +{ + int fid; + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + memset(info, 0, sizeof(*info)); + + if (fid >= FAN_1 && fid <= FAN_16) { + *info = chassis_finfo[fid]; + return _onlp_fani_info_get_chassis(fid, info); + } + + if (fid >= FAN_PSU_1 && fid <= FAN_PSU_4) { + *info = psu_finfo[fid - FAN_PSU_1]; + return _onlp_fani_info_get_psu(fid - FAN_PSU_1 + 1, info); + } + + return ONLP_STATUS_E_INVALID; +} + +int onlp_fani_rpm_set(onlp_oid_t id, int rpm) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_fani_percentage_set(onlp_oid_t id, int p) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_fani_mode_set(onlp_oid_t id, onlp_fan_mode_t mode) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_fani_dir_set(onlp_oid_t id, onlp_fan_dir_t dir) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_fani_ioctl(onlp_oid_t id, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/ledi.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/ledi.c new file mode 100644 index 000000000..d4abc01ee --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/ledi.c @@ -0,0 +1,140 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define LED_SYSFS_FMT "/sys/devices/platform/as1817_64o_led/led_%s" + +#define VALIDATE(_id) \ + do { \ + if (!ONLP_OID_IS_LED(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while (0) + +enum onlp_led_id { + LED_RESERVED = 0, + LED_LOC, + LED_DIAG, + LED_ALARM, + LED_FAN, + LED_PSU, +}; + +static const char *led_sysfs_name[] = { + [LED_RESERVED] = "", + [LED_LOC] = "loc", + [LED_DIAG] = "diag", + [LED_ALARM] = "alarm", + [LED_FAN] = "fan", + [LED_PSU] = "psu", +}; + +static onlp_led_info_t linfo[] = { + { }, /* Not used */ + { + { ONLP_LED_ID_CREATE(LED_LOC), "LED (LOC)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_BLUE_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_DIAG), "LED (DIAG)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_ALARM), "LED (ALARM)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_FAN), "LED (FAN)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU), "LED (PSU)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, +}; + +int onlp_ledi_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t *info) +{ + int lid, value; + + VALIDATE(id); + lid = ONLP_OID_ID_GET(id); + + if (lid < LED_LOC || lid > LED_PSU) + return ONLP_STATUS_E_INVALID; + + *info = linfo[lid]; + + if (onlp_file_read_int(&value, LED_SYSFS_FMT, led_sysfs_name[lid]) < 0) + return ONLP_STATUS_E_INTERNAL; + + info->mode = value; + + if (info->mode != ONLP_LED_MODE_OFF) + info->status |= ONLP_LED_STATUS_ON; + + return ONLP_STATUS_OK; +} + +int onlp_ledi_set(onlp_oid_t id, int on_or_off) +{ + VALIDATE(id); + + if (!on_or_off) + return onlp_ledi_mode_set(id, ONLP_LED_MODE_OFF); + + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode) +{ + int lid; + + VALIDATE(id); + lid = ONLP_OID_ID_GET(id); + + if (onlp_file_write_int(mode, LED_SYSFS_FMT, led_sysfs_name[lid]) != 0) + return ONLP_STATUS_E_INTERNAL; + + return ONLP_STATUS_OK; +} + +int onlp_ledi_ioctl(onlp_oid_t id, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/make.mk b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/make.mk new file mode 100644 index 000000000..902896c44 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### + +LIBRARY := x86_64_accton_as1817_64o +$(LIBRARY)_SUBDIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(BUILDER)/lib.mk diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.c new file mode 100644 index 000000000..1ff12a382 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.c @@ -0,0 +1,28 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.h new file mode 100644 index 000000000..bd96ae4e5 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/platform_lib.h @@ -0,0 +1,63 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#ifndef __PLATFORM_LIB_H__ +#define __PLATFORM_LIB_H__ + +#include +#include "x86_64_accton_as1817_64o_log.h" + +#define CHASSIS_FAN_COUNT 16 +#define CHASSIS_THERMAL_COUNT 15 +#define CHASSIS_PSU_COUNT 4 +#define CHASSIS_LED_COUNT 5 +#define CHASSIS_OSFP_COUNT 64 +#define CHASSIS_SFP_COUNT 2 + +#define PSU1_ID 1 +#define PSU2_ID 2 +#define PSU3_ID 3 +#define PSU4_ID 4 + +#define PSU_STATUS_PRESENT 1 +#define PSU_STATUS_POWER_GOOD 1 + +#define IDPROM_PATH "/sys/devices/platform/as1817_64o_sys/eeprom" + +enum onlp_fan_dir { + FAN_DIR_F2B, + FAN_DIR_B2F, + FAN_DIR_COUNT, +}; + +#define AIM_FREE_IF_PTR(p) \ + do { \ + if (p) { \ + aim_free(p); \ + p = NULL; \ + } \ + } while (0) + +#endif /* __PLATFORM_LIB_H__ */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/psui.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/psui.c new file mode 100644 index 000000000..8952d6f84 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/psui.c @@ -0,0 +1,156 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include "platform_lib.h" + +#define PSU_SYSFS_FMT "/sys/devices/platform/as1817_64o_psu.%d*psu_%s" + +#define VALIDATE(_id) \ + do { \ + if (!ONLP_OID_IS_PSU(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while (0) + +static int psu_read_int(int index, const char *attr, int *value) +{ + return onlp_file_read_int(value, PSU_SYSFS_FMT, index - 1, attr); +} + +static int psu_read_str(int index, const char *attr, char *buf, int len) +{ + int size = 0; + int ret; + + ret = onlp_file_read((uint8_t *)buf, len - 1, &size, + PSU_SYSFS_FMT, index - 1, attr); + if (ret == ONLP_STATUS_OK) { + buf[size] = '\0'; + while (size > 0 && (buf[size-1] == '\n' || buf[size-1] == '\r')) + buf[--size] = '\0'; + } + return ret; +} + +int onlp_psui_init(void) +{ + return ONLP_STATUS_OK; +} + +static onlp_psu_info_t pinfo[] = { + { }, /* Not used */ + { + { ONLP_PSU_ID_CREATE(PSU1_ID), "PSU-1", 0 }, + }, + { + { ONLP_PSU_ID_CREATE(PSU2_ID), "PSU-2", 0 }, + }, + { + { ONLP_PSU_ID_CREATE(PSU3_ID), "PSU-3", 0 }, + }, + { + { ONLP_PSU_ID_CREATE(PSU4_ID), "PSU-4", 0 }, + } +}; + +int onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t *info) +{ + int val = 0; + int index = ONLP_OID_ID_GET(id); + + VALIDATE(id); + + memset(info, 0, sizeof(onlp_psu_info_t)); + *info = pinfo[index]; + + /* Get present state */ + if (psu_read_int(index, "present", &val) != 0) { + return ONLP_STATUS_E_INTERNAL; + } + + if (val != PSU_STATUS_PRESENT) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_OK; + } + + info->status |= ONLP_PSU_STATUS_PRESENT; + info->caps = ONLP_PSU_CAPS_AC; + + /* Read model and serial (FRU EEPROM is readable even when unplugged) */ + psu_read_str(index, "model", info->model, sizeof(info->model)); + psu_read_str(index, "serial", info->serial, sizeof(info->serial)); + + /* Get power good */ + if (psu_read_int(index, "power_good", &val) != 0) { + return ONLP_STATUS_E_INTERNAL; + } + + if (val != PSU_STATUS_POWER_GOOD) { + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + return ONLP_STATUS_OK; + } + + /* Set associated OIDs: 1 fan + 3 thermals per PSU */ + info->hdr.coids[0] = ONLP_FAN_ID_CREATE(index + CHASSIS_FAN_COUNT); + info->hdr.coids[1] = ONLP_THERMAL_ID_CREATE(CHASSIS_THERMAL_COUNT + (index - 1) * 3 + 1); + info->hdr.coids[2] = ONLP_THERMAL_ID_CREATE(CHASSIS_THERMAL_COUNT + (index - 1) * 3 + 2); + info->hdr.coids[3] = ONLP_THERMAL_ID_CREATE(CHASSIS_THERMAL_COUNT + (index - 1) * 3 + 3); + + /* Read voltage, current and power */ + if (psu_read_int(index, "vin", &val) == 0) { + info->mvin = val; + info->caps |= ONLP_PSU_CAPS_VIN; + } + if (psu_read_int(index, "vout", &val) == 0) { + info->mvout = val; + info->caps |= ONLP_PSU_CAPS_VOUT; + } + if (psu_read_int(index, "iin", &val) == 0) { + info->miin = val; + info->caps |= ONLP_PSU_CAPS_IIN; + } + if (psu_read_int(index, "iout", &val) == 0) { + info->miout = val; + info->caps |= ONLP_PSU_CAPS_IOUT; + } + if (psu_read_int(index, "pin", &val) == 0) { + info->mpin = val; + info->caps |= ONLP_PSU_CAPS_PIN; + } + if (psu_read_int(index, "pout", &val) == 0) { + info->mpout = val; + info->caps |= ONLP_PSU_CAPS_POUT; + } + + return ONLP_STATUS_OK; +} + +int onlp_psui_ioctl(onlp_oid_t pid, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sfpi.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sfpi.c new file mode 100644 index 000000000..96a9ac928 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sfpi.c @@ -0,0 +1,533 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include +#include "x86_64_accton_as1817_64o_int.h" +#include "x86_64_accton_as1817_64o_log.h" +#include "platform_lib.h" + +#define NUM_OF_PORT (CHASSIS_OSFP_COUNT + CHASSIS_SFP_COUNT) + +#define MODULE_PRESENT_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_present_%d" +#define MODULE_RESET_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_reset_%d" +#define MODULE_LPMODE_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_lp_mode_%d" +#define MODULE_TXDISABLE_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_tx_disable_%d" +#define MODULE_TXFAULT_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_tx_fault_%d" +#define MODULE_RXLOS_FORMAT \ + "/sys/devices/platform/as1817_64o_fpga/module_rx_los_%d" + +/* + * EEPROM access via optoe sysfs. + * i2c bus layout: bus0=I801, bus1=iSMT, bus2~67=ocores (front-panel port 1~66) + * optoe3 for OSFP ports, optoe2 for SFP28 ports. + * + * Port numbering: ONLP uses 0-based SFP port indices (0..65). + * - OSFP: ONLP index 0..63 (front-panel port 1..64) + * - SFP28: ONLP index 64..65 (front-panel port 65..66) + * Kernel sysfs and i2c bus use 1-based front-panel numbers, so: + * - sysfs module_*_%d attribute number = PORT_NUM(port) + * - i2c bus number = PORT_NUM(port) + 1 + */ +#define PORT_NUM(port) ((port) + 1) +#define PORT_BUS(port) (PORT_NUM(port) + 1) +#define PORT_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom" + +#define OSFP_PORT_MIN 0 +#define OSFP_PORT_MAX 63 +#define SFP_PORT_MIN 64 +#define SFP_PORT_MAX 65 + +/* SFP EEPROM Address */ +#define PORT_EEPROM_DEVADDR 0x50 + +/* CMIS Register Addresses (lower page) */ +#define CMIS_REG_IDENTIFIER 0x00 /* Module type identifier */ +#define CMIS_REG_STATUS 0x02 /* Module status (bit7=flat_mem) */ +#define CMIS_REG_BANK_SELECT 0x7E /* Bank select for upper pages */ +#define CMIS_REG_PAGE_SELECT 0x7F /* Page select for upper memory */ + +/* CMIS Register Addresses (upper page) */ +#define CMIS_P01H_REG_CONTROL_1 0x9B /* Page 01h: supported controls */ +#define CMIS_P10H_REG_TX_DISABLE 0x82 /* Page 10h: per-lane TX disable */ + +/* CMIS Pages */ +#define CMIS_PAGE_ADMIN_INFO 0x00 /* Basic module info */ +#define CMIS_PAGE_ADVERTISING 0x01 /* Module capabilities */ +#define CMIS_PAGE_LANE_CTRL 0x10 /* Per-lane control */ + +/* CMIS Bit Masks */ +#define CMIS_STATUS_FLAT_MEM 0x80 /* Status reg bit7: 1=flat memory */ +#define CMIS_P01H_TX_DISABLE_SUPPORT 0x02 /* Control_1 bit1: TX disable supported */ + + +/* SFP IDENTIFIER */ +#define QSFP_28_IDENTIFIER 0x11 +#define QSFP_PLUS_IDENTIFIER 0x0d +#define QSFP_DD_IDENTIFIER 0x18 +#define OSFP_IDENTIFIER 0x19 + +#define VALIDATE_PORT(port) \ + do { if ((port) < 0 || (port) >= NUM_OF_PORT) return ONLP_STATUS_E_INVALID; } while(0) + +/************************************************************ + * + * SFPI Entry Points + * + ***********************************************************/ + +int onlp_sfpi_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_sfpi_bitmap_get(onlp_sfp_bitmap_t *bmap) +{ + int p; + for (p = 0; p < NUM_OF_PORT; p++) + AIM_BITMAP_SET(bmap, p); + return ONLP_STATUS_OK; +} + +int onlp_sfpi_is_present(int port) +{ + int present; + + VALIDATE_PORT(port); + + if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read present status from port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + return present; +} + +int onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t *dst) +{ + int i; + + for (i = 0; i < NUM_OF_PORT; i++) { + int present = onlp_sfpi_is_present(i); + AIM_BITMAP_MOD(dst, i, (present == 1) ? 1 : 0); + } + + return ONLP_STATUS_OK; +} + +int onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t *dst) +{ + int i; + int rx_los; + + AIM_BITMAP_CLR_ALL(dst); + + /* RX_LOS only applies to SFP28 ports */ + for (i = SFP_PORT_MIN; i <= SFP_PORT_MAX; i++) { + if (onlp_file_read_int(&rx_los, MODULE_RXLOS_FORMAT, PORT_NUM(i)) < 0) { + syslog(LOG_ERR, "Unable to read rx_los status from port(%d)", i); + return ONLP_STATUS_E_INTERNAL; + } + AIM_BITMAP_MOD(dst, i, (rx_los == 1) ? 1 : 0); + } + + return ONLP_STATUS_OK; +} + +int onlp_sfpi_eeprom_read(int port, uint8_t data[256]) +{ + int size = 0; + + VALIDATE_PORT(port); + + memset(data, 0, 256); + + if (onlp_file_read(data, 256, &size, PORT_EEPROM_FORMAT, PORT_BUS(port)) != ONLP_STATUS_OK) { + syslog(LOG_ERR, "Unable to read eeprom from port(%d)", 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}; + + VALIDATE_PORT(port); + sprintf(file, PORT_EEPROM_FORMAT, PORT_BUS(port)); + fp = fopen(file, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "Unable to open the eeprom device file of port(%d)", + port); + return ONLP_STATUS_E_INTERNAL; + } + + if (fseek(fp, 256, SEEK_CUR) != 0) { + fclose(fp); + syslog(LOG_ERR, "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) { + syslog(LOG_ERR, "Unable to read the module_eeprom device file of port(%d)", + port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int onlp_sfpi_dev_readb(int port, uint8_t devaddr, uint8_t addr) +{ + VALIDATE_PORT(port); + return onlp_i2c_readb(PORT_BUS(port), devaddr, addr, ONLP_I2C_F_FORCE); +} + +int onlp_sfpi_dev_writeb(int port, uint8_t devaddr, uint8_t addr, + uint8_t value) +{ + VALIDATE_PORT(port); + return onlp_i2c_writeb(PORT_BUS(port), devaddr, addr, value, ONLP_I2C_F_FORCE); +} + +int onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr) +{ + VALIDATE_PORT(port); + return onlp_i2c_readw(PORT_BUS(port), devaddr, addr, ONLP_I2C_F_FORCE); +} + +int onlp_sfpi_dev_writew(int port, uint8_t devaddr, uint8_t addr, + uint16_t value) +{ + VALIDATE_PORT(port); + return onlp_i2c_writew(PORT_BUS(port), devaddr, addr, value, ONLP_I2C_F_FORCE); +} + +int onlp_sfpi_control_set(int port, onlp_sfp_control_t control, int value) +{ + int rv; + int present = 0; + int identifier = 0; + int status_byte = 0; + int support_ctrls = 0; + + VALIDATE_PORT(port); + + switch (control) { + case ONLP_SFP_CONTROL_TX_DISABLE: + case ONLP_SFP_CONTROL_TX_DISABLE_CHANNEL: + present = onlp_sfpi_is_present(port); + + if (present == 1) { + if (port >= OSFP_PORT_MIN && port < SFP_PORT_MIN) { /* OSFP */ + if ((identifier = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_REG_IDENTIFIER)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Identifier", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + + /* OSFP and QSFP-DD both use the CMIS protocol */ + if (identifier == OSFP_IDENTIFIER || identifier == QSFP_DD_IDENTIFIER) { + /* Flat-memory CMIS modules do not implement page 01h/10h */ + if ((status_byte = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_REG_STATUS)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Status", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + if (status_byte & CMIS_STATUS_FLAT_MEM) { + syslog(LOG_ERR, "Xcvr(%d) is flat mem not support get/set tx disable", port); + return ONLP_STATUS_E_UNSUPPORTED; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_BANK_SELECT, 0)) < 0) { + syslog(LOG_ERR, "Failed to set xcvr(%d) Bank 0", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, CMIS_PAGE_ADVERTISING)) < 0) { + syslog(LOG_ERR, "Failed to switch to xcvr(%d) Advertising Page", port); + goto restore; + } + if ((support_ctrls = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_P01H_REG_CONTROL_1)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Support Control", port); + rv = support_ctrls; + goto restore; + } + if (support_ctrls & CMIS_P01H_TX_DISABLE_SUPPORT) { + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_BANK_SELECT, 0)) < 0) { + syslog(LOG_ERR, "Failed to set xcvr(%d) Bank 0", port); + goto restore; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, CMIS_PAGE_LANE_CTRL)) < 0) { + syslog(LOG_ERR, "Failed to switch xcvr(%d) Lane Control Page (Page 0x%02x)", + port, CMIS_PAGE_LANE_CTRL); + goto restore; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_P10H_REG_TX_DISABLE, (value & 0xff))) < 0) { + syslog(LOG_ERR, "Failed to write xcvr(%d) tx disable(0x%02x)", port, value); + goto restore; + } + } else { + syslog(LOG_ERR, "Xcvr(%d) not support get/set tx disable", port); + rv = ONLP_STATUS_E_UNSUPPORTED; + goto restore; + } + + restore: + if ((onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, + CMIS_PAGE_ADMIN_INFO)) < 0) { + syslog(LOG_ERR, "Failed to restore xcvr(%d) Page Select to Admin Info!", port); + } + exit: + if (rv < 0) { + syslog(LOG_ERR, "Unable to set port(%d) tx disable", port); + rv = (rv == ONLP_STATUS_E_UNSUPPORTED) ? rv : ONLP_STATUS_E_INTERNAL; + } + } else { + syslog(LOG_ERR, "Unable to recognize xcvr(%d) identifier", port); + rv = ONLP_STATUS_E_UNSUPPORTED; + } + } else { /* SFP */ + if (onlp_file_write_int(value, MODULE_TXDISABLE_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to set port(%d) tx disable", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + } + } else { + rv = ONLP_STATUS_E_MISSING; + } + break; + + case ONLP_SFP_CONTROL_RESET: + case ONLP_SFP_CONTROL_RESET_STATE: + if (port >= SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + /* Latching reset: value=1 asserts reset, value=0 deasserts it. */ + if (onlp_file_write_int(value, MODULE_RESET_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to set reset to port(%d)", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + break; + + case ONLP_SFP_CONTROL_LP_MODE: + if (port >= SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + if (onlp_file_write_int(value, MODULE_LPMODE_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to set lp_mode to port(%d)", 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 present = 0; + int identifier = 0; + int status_byte = 0; + int support_ctrls = 0; + + VALIDATE_PORT(port); + + switch (control) { + case ONLP_SFP_CONTROL_RX_LOS: + if (port < SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + if (onlp_file_read_int(value, MODULE_RXLOS_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read rx_los from port(%d)", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + break; + + case ONLP_SFP_CONTROL_TX_FAULT: + if (port < SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + if (onlp_file_read_int(value, MODULE_TXFAULT_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read tx_fault from port(%d)", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + break; + + case ONLP_SFP_CONTROL_TX_DISABLE: + case ONLP_SFP_CONTROL_TX_DISABLE_CHANNEL: + present = onlp_sfpi_is_present(port); + + if (present == 1) { + if (port >= OSFP_PORT_MIN && port < SFP_PORT_MIN) { /* OSFP */ + if ((identifier = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_REG_IDENTIFIER)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Identifier", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + + /* OSFP and QSFP-DD both use the CMIS protocol */ + if (identifier == OSFP_IDENTIFIER || identifier == QSFP_DD_IDENTIFIER) { + /* Flat-memory CMIS modules do not implement page 01h/10h */ + if ((status_byte = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_REG_STATUS)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Status", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + if (status_byte & CMIS_STATUS_FLAT_MEM) { + syslog(LOG_ERR, "Xcvr(%d) is flat mem not support get/set tx disable", port); + return ONLP_STATUS_E_UNSUPPORTED; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_BANK_SELECT, 0)) < 0) { + syslog(LOG_ERR, "Failed to set xcvr(%d) Bank 0", port); + rv = ONLP_STATUS_E_INTERNAL; + goto exit; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, CMIS_PAGE_ADVERTISING)) < 0) { + syslog(LOG_ERR, "Failed to switch to xcvr(%d) Advertising Page", port); + goto restore; + } + if ((support_ctrls = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_P01H_REG_CONTROL_1)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) Support Control", port); + rv = support_ctrls; + goto restore; + } + if (support_ctrls & CMIS_P01H_TX_DISABLE_SUPPORT) { + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_BANK_SELECT, 0)) < 0) { + syslog(LOG_ERR, "Failed to set xcvr(%d) Bank 0", port); + goto restore; + } + if ((rv = onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, CMIS_PAGE_LANE_CTRL)) < 0) { + syslog(LOG_ERR, "Failed to switch xcvr(%d) Lane Control Page (Page 0x%02x)", + port, CMIS_PAGE_LANE_CTRL); + goto restore; + } + if ((rv = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, CMIS_P10H_REG_TX_DISABLE)) < 0) { + syslog(LOG_ERR, "Failed to read xcvr(%d) TX_DISABLE from Lane Control register", port); + goto restore; + } + *value = rv & 0xff; + rv = ONLP_STATUS_OK; + } else { + syslog(LOG_ERR, "Xcvr(%d) not support get/set tx disable", port); + rv = ONLP_STATUS_E_UNSUPPORTED; + goto restore; + } + + restore: + if ((onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, CMIS_REG_PAGE_SELECT, + CMIS_PAGE_ADMIN_INFO)) < 0) { + syslog(LOG_ERR, "Failed to restore xcvr(%d) Page Select to Admin Info!", port); + } + exit: + if (rv < 0) { + syslog(LOG_ERR, "Unable to read port(%d) tx disable status", port); + rv = (rv == ONLP_STATUS_E_UNSUPPORTED) ? rv : ONLP_STATUS_E_INTERNAL; + } + } else { + syslog(LOG_ERR, "Unable to recognize xcvr(%d) identifier", port); + rv = ONLP_STATUS_E_UNSUPPORTED; + } + } else { /* SFP */ + if (onlp_file_read_int(value, MODULE_TXDISABLE_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read port(%d) tx disable status", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + } + } else { + rv = ONLP_STATUS_E_MISSING; + } + break; + + case ONLP_SFP_CONTROL_RESET_STATE: + if (port >= SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + if (onlp_file_read_int(value, MODULE_RESET_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read reset from port(%d)", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + break; + + case ONLP_SFP_CONTROL_LP_MODE: + if (port >= SFP_PORT_MIN) { + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + if (onlp_file_read_int(value, MODULE_LPMODE_FORMAT, PORT_NUM(port)) < 0) { + syslog(LOG_ERR, "Unable to read lp_mode from port(%d)", port); + rv = ONLP_STATUS_E_INTERNAL; + } else { + rv = ONLP_STATUS_OK; + } + break; + + default: + rv = ONLP_STATUS_E_UNSUPPORTED; + } + + return rv; +} + +int onlp_sfpi_denit(void) +{ + return ONLP_STATUS_OK; +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sysi.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sysi.c new file mode 100644 index 000000000..433df0e2b --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/sysi.c @@ -0,0 +1,133 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * + * + ***********************************************************/ +#include +#include +#include +#include +#include +#include "platform_lib.h" + +#define CPLD1_VER_PATH "/sys/devices/platform/as1817_64o_sys/cpld1_version" +#define CPLD2_VER_PATH "/sys/devices/platform/as1817_64o_sys/cpld2_version" +#define FPGA_VER_PATH "/sys/devices/platform/as1817_64o_sys/fpga_version" +#define FAN_CPLD_VER_PATH "/sys/devices/platform/as1817_64o_sys/fan_cpld_version" +#define BIOS_VER_PATH "/sys/devices/virtual/dmi/id/bios_version" + +const char *onlp_sysi_platform_get(void) +{ + return "x86-64-accton-as1817-64o-r0"; +} + +int onlp_sysi_onie_data_get(uint8_t **data, int *size) +{ + uint8_t *rdata = aim_zmalloc(256); + + if (onlp_file_read(rdata, 256, size, IDPROM_PATH) == ONLP_STATUS_OK) { + if (*size == 256) { + *data = rdata; + return ONLP_STATUS_OK; + } + } + + aim_free(rdata); + *size = 0; + return ONLP_STATUS_E_INTERNAL; +} + +int onlp_sysi_oids_get(onlp_oid_t *table, int max) +{ + int i; + onlp_oid_t *e = table; + memset(table, 0, max * sizeof(onlp_oid_t)); + + for (i = 1; i <= CHASSIS_THERMAL_COUNT; i++) + *e++ = ONLP_THERMAL_ID_CREATE(i); + + for (i = 1; i <= CHASSIS_LED_COUNT; i++) + *e++ = ONLP_LED_ID_CREATE(i); + + for (i = 1; i <= CHASSIS_PSU_COUNT; i++) + *e++ = ONLP_PSU_ID_CREATE(i); + + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) + *e++ = ONLP_FAN_ID_CREATE(i); + + return 0; +} + +int onlp_sysi_platform_info_get(onlp_platform_info_t *pi) +{ + char *cpld1_ver = NULL; + char *cpld2_ver = NULL; + char *fpga_ver = NULL; + char *fan_cpld_ver = NULL; + char *bios_ver = NULL; + + onlp_file_read_str(&cpld1_ver, CPLD1_VER_PATH); + onlp_file_read_str(&cpld2_ver, CPLD2_VER_PATH); + onlp_file_read_str(&fpga_ver, FPGA_VER_PATH); + onlp_file_read_str(&fan_cpld_ver, FAN_CPLD_VER_PATH); + onlp_file_read_str(&bios_ver, BIOS_VER_PATH); + + pi->cpld_versions = aim_fstrdup( + "\r\n\t Port CPLD0: %s" + "\r\n\t Port CPLD1: %s" + "\r\n\t Fan CPLD: %s", + cpld1_ver ? cpld1_ver : "N/A", + cpld2_ver ? cpld2_ver : "N/A", + fan_cpld_ver ? fan_cpld_ver : "N/A"); + + pi->other_versions = aim_fstrdup( + "\r\n\t FPGA: %s" + "\r\n\t BIOS: %s", + fpga_ver ? fpga_ver : "N/A", + bios_ver ? bios_ver : "N/A"); + + AIM_FREE_IF_PTR(cpld1_ver); + AIM_FREE_IF_PTR(cpld2_ver); + AIM_FREE_IF_PTR(fpga_ver); + AIM_FREE_IF_PTR(fan_cpld_ver); + AIM_FREE_IF_PTR(bios_ver); + + return ONLP_STATUS_OK; +} + +void onlp_sysi_platform_info_free(onlp_platform_info_t *pi) +{ + aim_free(pi->cpld_versions); + aim_free(pi->other_versions); +} + +/* Fan and LED management handled by BMC */ +int onlp_sysi_platform_manage_fans(void) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + +int onlp_sysi_platform_manage_leds(void) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/thermali.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/thermali.c new file mode 100644 index 000000000..fcff24571 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/thermali.c @@ -0,0 +1,221 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2026 Accton Technology Corporation. + * + * Licensed under the Eclipse Public License, Version 1.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.eclipse.org/legal/epl-v10.html + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + * + * + ************************************************************ + * + * Thermal Sensor Platform Implementation. + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if (!ONLP_OID_IS_THERMAL(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while (0) + +enum onlp_thermal_id { + THERMAL_RESERVED = 0, + THERMAL_CPU_CORE, + THERMAL_1_ON_CARRIER_BOARD, + THERMAL_2_ON_CARRIER_BOARD, + THERMAL_1_ON_MAIN_BOARD, + THERMAL_2_ON_MAIN_BOARD, + THERMAL_3_ON_MAIN_BOARD, + THERMAL_4_ON_MAIN_BOARD, + THERMAL_5_ON_MAIN_BOARD, + THERMAL_6_ON_MAIN_BOARD, + THERMAL_1_ON_FAN_BOARD, + THERMAL_2_ON_FAN_BOARD, + THERMAL_TMP464_LO, + THERMAL_TMP464_MAC1, + THERMAL_TMP464_MAC2, + THERMAL_TMP464_MAC3, + THERMAL_1_ON_PSU1, + THERMAL_2_ON_PSU1, + THERMAL_3_ON_PSU1, + THERMAL_1_ON_PSU2, + THERMAL_2_ON_PSU2, + THERMAL_3_ON_PSU2, + THERMAL_1_ON_PSU3, + THERMAL_2_ON_PSU3, + THERMAL_3_ON_PSU3, + THERMAL_1_ON_PSU4, + THERMAL_2_ON_PSU4, + THERMAL_3_ON_PSU4, + THERMAL_COUNT, +}; + +static char *cpu_coretemp_files[] = { + "/sys/devices/platform/coretemp.0*temp1_input", + "/sys/devices/platform/coretemp.0*temp2_input", + "/sys/devices/platform/coretemp.0*temp3_input", + "/sys/devices/platform/coretemp.0*temp4_input", + "/sys/devices/platform/coretemp.0*temp5_input", + NULL, +}; + +static char *devfiles[] = { + [THERMAL_RESERVED] = NULL, + [THERMAL_CPU_CORE] = NULL, + [THERMAL_1_ON_CARRIER_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp1_input", + [THERMAL_2_ON_CARRIER_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp2_input", + [THERMAL_1_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp3_input", + [THERMAL_2_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp4_input", + [THERMAL_3_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp5_input", + [THERMAL_4_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp6_input", + [THERMAL_5_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp7_input", + [THERMAL_6_ON_MAIN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp8_input", + [THERMAL_1_ON_FAN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp9_input", + [THERMAL_2_ON_FAN_BOARD] = "/sys/devices/platform/as1817_64o_thermal*temp10_input", + [THERMAL_TMP464_LO] = "/sys/devices/platform/as1817_64o_thermal*temp11_input", + [THERMAL_TMP464_MAC1] = "/sys/devices/platform/as1817_64o_thermal*temp12_input", + [THERMAL_TMP464_MAC2] = "/sys/devices/platform/as1817_64o_thermal*temp13_input", + [THERMAL_TMP464_MAC3] = "/sys/devices/platform/as1817_64o_thermal*temp14_input", + [THERMAL_1_ON_PSU1] = "/sys/devices/platform/as1817_64o_psu.0*psu_temp1_input", + [THERMAL_2_ON_PSU1] = "/sys/devices/platform/as1817_64o_psu.0*psu_temp2_input", + [THERMAL_3_ON_PSU1] = "/sys/devices/platform/as1817_64o_psu.0*psu_temp3_input", + [THERMAL_1_ON_PSU2] = "/sys/devices/platform/as1817_64o_psu.1*psu_temp1_input", + [THERMAL_2_ON_PSU2] = "/sys/devices/platform/as1817_64o_psu.1*psu_temp2_input", + [THERMAL_3_ON_PSU2] = "/sys/devices/platform/as1817_64o_psu.1*psu_temp3_input", + [THERMAL_1_ON_PSU3] = "/sys/devices/platform/as1817_64o_psu.2*psu_temp1_input", + [THERMAL_2_ON_PSU3] = "/sys/devices/platform/as1817_64o_psu.2*psu_temp2_input", + [THERMAL_3_ON_PSU3] = "/sys/devices/platform/as1817_64o_psu.2*psu_temp3_input", + [THERMAL_1_ON_PSU4] = "/sys/devices/platform/as1817_64o_psu.3*psu_temp1_input", + [THERMAL_2_ON_PSU4] = "/sys/devices/platform/as1817_64o_psu.3*psu_temp2_input", + [THERMAL_3_ON_PSU4] = "/sys/devices/platform/as1817_64o_psu.3*psu_temp3_input", +}; + +#define THERMAL_CAPS (ONLP_THERMAL_CAPS_GET_TEMPERATURE | \ + ONLP_THERMAL_CAPS_GET_WARNING_THRESHOLD | \ + ONLP_THERMAL_CAPS_GET_ERROR_THRESHOLD | \ + ONLP_THERMAL_CAPS_GET_SHUTDOWN_THRESHOLD) + +static onlp_thermal_info_t tinfo[] = { + { }, /* Not used */ + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE), "CPU Core", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 90000, 100000, 105000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_CARRIER_BOARD), "CB RearCenter(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 67000, 72000, 77000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_CARRIER_BOARD), "CB FrontLeft(0x49)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 67000, 72000, 77000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_MAIN_BOARD), "MB FrontRight(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 67000, 72000, 77000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_MAIN_BOARD), "MB RearRight(0x49)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 65000, 70000, 75000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_MAIN_BOARD), "MB RearCenter(0x4a)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 64000, 69000, 74000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_4_ON_MAIN_BOARD), "MB RearLeft(0x4b)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 67000, 72000, 77000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_5_ON_MAIN_BOARD), "MB RearCenter(0x4c)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 62000, 67000, 72000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_6_ON_MAIN_BOARD), "RJ45 FrontLeft(0x4d)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 64000, 69000, 74000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_FAN_BOARD), "FB Top RearCenter(0x4d)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 55000, 60000, 65000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_FAN_BOARD), "FB Bottom RearCenter(0x4d)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 55000, 60000, 65000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP464_LO), "TMP464 Lo(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 75000, 80000, 85000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP464_MAC1), "TMP464 MAC1(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 95000, 100000, 105000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP464_MAC2), "TMP464 MAC2(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 95000, 100000, 105000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_TMP464_MAC3), "TMP464 MAC3(0x48)", 0 }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 95000, 100000, 105000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU1), "PSU-1 Thermal 1", ONLP_PSU_ID_CREATE(PSU1_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU1), "PSU-1 Thermal 2", ONLP_PSU_ID_CREATE(PSU1_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU1), "PSU-1 Thermal 3", ONLP_PSU_ID_CREATE(PSU1_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU2), "PSU-2 Thermal 1", ONLP_PSU_ID_CREATE(PSU2_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU2), "PSU-2 Thermal 2", ONLP_PSU_ID_CREATE(PSU2_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU2), "PSU-2 Thermal 3", ONLP_PSU_ID_CREATE(PSU2_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU3), "PSU-3 Thermal 1", ONLP_PSU_ID_CREATE(PSU3_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU3), "PSU-3 Thermal 2", ONLP_PSU_ID_CREATE(PSU3_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU3), "PSU-3 Thermal 3", ONLP_PSU_ID_CREATE(PSU3_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU4), "PSU-4 Thermal 1", ONLP_PSU_ID_CREATE(PSU4_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU4), "PSU-4 Thermal 2", ONLP_PSU_ID_CREATE(PSU4_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU4), "PSU-4 Thermal 3", ONLP_PSU_ID_CREATE(PSU4_ID) }, + ONLP_THERMAL_STATUS_PRESENT, THERMAL_CAPS, 0, { 78000, 83000, 88000 } + }, +}; + +int onlp_thermali_init(void) +{ + return ONLP_STATUS_OK; +} + +int onlp_thermali_info_get(onlp_oid_t id, onlp_thermal_info_t *info) +{ + int tid; + + VALIDATE(id); + tid = ONLP_OID_ID_GET(id); + + if (tid <= THERMAL_RESERVED || tid >= THERMAL_COUNT) + return ONLP_STATUS_E_INVALID; + + *info = tinfo[tid]; + + if (tid == THERMAL_CPU_CORE) { + return onlp_file_read_int_max(&info->mcelsius, cpu_coretemp_files); + } + + return onlp_file_read_int(&info->mcelsius, devfiles[tid]); +} diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_config.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_config.c new file mode 100644 index 000000000..9d27d9b73 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_config.c @@ -0,0 +1,80 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* */ +#define __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(_x) #_x +#define __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(_x) __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(_x) +x86_64_accton_as1817_64o_config_settings_t x86_64_accton_as1817_64o_config_settings[] = +{ +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_LOGGING(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_STDLIB(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_UCLI(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + { __x86_64_accton_as1817_64o_config_STRINGIFY_NAME(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION), __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE(X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION) }, +#else +{ X86_64_ACCTON_AS1817_64O_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION(__x86_64_accton_as1817_64o_config_STRINGIFY_NAME), "__undefined__" }, +#endif + { NULL, NULL } +}; +#undef __x86_64_accton_as1817_64o_config_STRINGIFY_VALUE +#undef __x86_64_accton_as1817_64o_config_STRINGIFY_NAME + +const char* +x86_64_accton_as1817_64o_config_lookup(const char* setting) +{ + int i; + for(i = 0; x86_64_accton_as1817_64o_config_settings[i].name; i++) { + if(!strcmp(x86_64_accton_as1817_64o_config_settings[i].name, setting)) { + return x86_64_accton_as1817_64o_config_settings[i].value; + } + } + return NULL; +} + +int +x86_64_accton_as1817_64o_config_show(struct aim_pvs_s* pvs) +{ + int i; + for(i = 0; x86_64_accton_as1817_64o_config_settings[i].name; i++) { + aim_printf(pvs, "%s = %s\n", x86_64_accton_as1817_64o_config_settings[i].name, x86_64_accton_as1817_64o_config_settings[i].value); + } + return i; +} + +/* */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_enums.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_enums.c new file mode 100644 index 000000000..685c6f387 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_enums.c @@ -0,0 +1,10 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.enum(ALL).source> */ +/* */ + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_int.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_int.h new file mode 100644 index 000000000..7103f9fbc --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_int.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * x86_64_accton_as1817_64o Internal Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_as1817_64o_INT_H__ +#define __x86_64_accton_as1817_64o_INT_H__ + +#include + + +#endif /* __x86_64_accton_as1817_64o_INT_H__ */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.c new file mode 100644 index 000000000..95d8a7588 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.c @@ -0,0 +1,18 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as1817_64o_log.h" +/* + * x86_64_accton_as1817_64o log struct. + */ +AIM_LOG_STRUCT_DEFINE( + X86_64_ACCTON_AS1817_64O_CONFIG_LOG_OPTIONS_DEFAULT, + X86_64_ACCTON_AS1817_64O_CONFIG_LOG_BITS_DEFAULT, + NULL, /* Custom log map */ + X86_64_ACCTON_AS1817_64O_CONFIG_LOG_CUSTOM_BITS_DEFAULT + ); + diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.h b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.h new file mode 100644 index 000000000..fdf1e75d3 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_log.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#ifndef __x86_64_accton_as1817_64o_LOG_H__ +#define __x86_64_accton_as1817_64o_LOG_H__ + +#define AIM_LOG_MODULE_NAME x86_64_accton_as1817_64o +#include + +#endif /* __x86_64_accton_as1817_64o_LOG_H__ */ diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_module.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_module.c new file mode 100644 index 000000000..400ca9071 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_module.c @@ -0,0 +1,24 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as1817_64o_log.h" + +static int +datatypes_init__(void) +{ +#define x86_64_accton_as1817_64o_ENUMERATION_ENTRY(_enum_name, _desc) AIM_DATATYPE_MAP_REGISTER(_enum_name, _enum_name##_map, _desc, AIM_LOG_INTERNAL); +#include + return 0; +} + +void __x86_64_accton_as1817_64o_module_init__(void) +{ + AIM_LOG_STRUCT_REGISTER(); + datatypes_init__(); +} + +int __onlp_platform_version__ = 1; diff --git a/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_ucli.c b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_ucli.c new file mode 100644 index 000000000..05a413e1c --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/onlp/builds/x86_64_accton_as1817_64o/module/src/x86_64_accton_as1817_64o_ucli.c @@ -0,0 +1,50 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#if x86_64_accton_as1817_64o_CONFIG_INCLUDE_UCLI == 1 + +#include +#include +#include + +static ucli_status_t +x86_64_accton_as1817_64o_ucli_ucli__config__(ucli_context_t* uc) +{ + UCLI_HANDLER_MACRO_MODULE_CONFIG(x86_64_accton_as1817_64o) +} + +/* */ +/* */ + +static ucli_module_t +x86_64_accton_as1817_64o_ucli_module__ = + { + "x86_64_accton_as1817_64o_ucli", + NULL, + x86_64_accton_as1817_64o_ucli_ucli_handlers__, + NULL, + NULL, + }; + +ucli_node_t* +x86_64_accton_as1817_64o_ucli_node_create(void) +{ + ucli_node_t* n; + ucli_module_init(&x86_64_accton_as1817_64o_ucli_module__); + n = ucli_node_create("x86_64_accton_as1817_64o", NULL, &x86_64_accton_as1817_64o_ucli_module__); + ucli_node_subnode_add(n, ucli_module_log_node_create("x86_64_accton_as1817_64o")); + return n; +} + +#else +void* +x86_64_accton_as1817_64o_ucli_node_create(void) +{ + return NULL; +} +#endif + diff --git a/packages/platforms/accton/x86-64/as1817-64o/platform-config/Makefile b/packages/platforms/accton/x86-64/as1817-64o/platform-config/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/platform-config/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/Makefile b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/PKG.yml b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/PKG.yml new file mode 100644 index 000000000..e592e735f --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-config-platform.yml ARCH=amd64 VENDOR=accton BASENAME=x86-64-accton-as1817-64o REVISION=r0 diff --git a/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/lib/x86-64-accton-as1817-64o-r0.yml b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/lib/x86-64-accton-as1817-64o-r0.yml new file mode 100644 index 000000000..478996bd7 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/lib/x86-64-accton-as1817-64o-r0.yml @@ -0,0 +1,34 @@ +--- + +###################################################################### +# +# platform-config for AS1817-64O +# +###################################################################### + +x86-64-accton-as1817-64o-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-6-12 + + args: >- + console=tty0 + console=ttyS0,115200n8 + intel_iommu=off + pcie_aspm=off + intremap=off quiet + + ##network: + ## interfaces: + ## ma1: + ## name: ~ + ## syspath: pci0000:00/0000:00:1c.0/0000:0a:00.0 diff --git a/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/python/x86_64_accton_as1817_64o_r0/__init__.py b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/python/x86_64_accton_as1817_64o_r0/__init__.py new file mode 100644 index 000000000..1b5f1b354 --- /dev/null +++ b/packages/platforms/accton/x86-64/as1817-64o/platform-config/r0/src/python/x86_64_accton_as1817_64o_r0/__init__.py @@ -0,0 +1,112 @@ +from onl.platform.base import * +from onl.platform.accton import * +from time import sleep + +init_ipmi_dev = [ + 'echo "remove,kcs,i/o,0xca2" > /sys/module/ipmi_si/parameters/hotmod', + 'echo "add,kcs,i/o,0xca2" > /sys/module/ipmi_si/parameters/hotmod'] + +ATTEMPTS = 5 +INTERVAL = 3 + +def init_ipmi_dev_intf(): + attempts = ATTEMPTS + interval = INTERVAL + + while attempts: + if os.path.exists('/dev/ipmi0') or os.path.exists('/dev/ipmidev/0'): + return (True, (ATTEMPTS - attempts) * interval) + + for cmd in init_ipmi_dev: + subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + + attempts -= 1 + sleep(interval) + + return (False, ATTEMPTS * interval) + +def init_ipmi_oem_cmd(): + attempts = ATTEMPTS + interval = INTERVAL + + while attempts: + process = subprocess.Popen("ipmitool raw 0x34 0x95", shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process.communicate() + if process.returncode == 0: + return (True, (ATTEMPTS - attempts) * interval) + + attempts -= 1 + sleep(interval) + + return (False, ATTEMPTS * interval) + +def init_ipmi(): + attempts = ATTEMPTS + interval = 60 + + while attempts: + attempts -= 1 + + (status, elapsed_dev) = init_ipmi_dev_intf() + if not status: + sleep(interval - elapsed_dev) + continue + + (status, elapsed_oem) = init_ipmi_oem_cmd() + if not status: + sleep(interval - elapsed_dev - elapsed_oem) + continue + + print('IPMI dev interface is ready.') + return True + + print('Failed to initialize IPMI dev interface') + return False + +class OnlPlatform_x86_64_accton_as1817_64o_r0(OnlPlatformAccton, + OnlPlatformPortConfig_64x800_2x25): + + PLATFORM='x86-64-accton-as1817-64o-r0' + MODEL="AS1817-64O" + SYS_OBJECT_ID=".1817.64" + + def modprobe(self, module): + subprocess.check_call("modprobe %s" % module, shell=True) + + def baseconfig(self): + if init_ipmi() is not True: + return False + + self.modprobe('optoe') + + # Load platform drivers + for m in ['fpga', 'i2c-ocores', 'fan', 'psu', 'thermal', 'sys', 'leds']: + self.insmod("x86-64-accton-as1817-64o-%s" % m) + + # Wait for FPGA driver to create i2c adapters + sleep(2) + + # Release reset for all OSFP ports + for port in range(1, 65): + subprocess.call('echo 0 > /sys/devices/platform/as1817_64o_fpga/module_reset_%d' % port, + shell=True) + + # Enable EFUSE for all OSFP ports + for reg in range(0x70, 0x78): + subprocess.call('ipmitool raw 0x34 0x23 0x61 0x%02x 0xff' % reg, + shell=True) + + # i2c bus layout: bus0=I801, bus1=iSMT, bus2~67=ocores (port1~66) + bus_start = 2 + + # Instantiate optoe devices on each port + for port in range(1, 67): + bus = bus_start + port - 1 + devtype = 'optoe3' if port <= 64 else 'optoe2' + self.new_i2c_device(devtype, 0x50, bus) + subprocess.call('echo port%d > /sys/bus/i2c/devices/%d-0050/port_name' % (port, bus), + shell=True) + + return True