From 93cafb3ab859c7374759dd9157ee0c59195ec0a1 Mon Sep 17 00:00:00 2001 From: vincent_chiang Date: Mon, 29 Jun 2026 08:09:57 +0000 Subject: [PATCH] [AS7327-56X] Upgrade kernel from 4.14 to 6.12 Bring in the AS7327-56X platform from support_new_platform_and_sync and port it to kernel 6.12 LTS. ### Config - modules/PKG.yml, modules/builds/Makefile: 4.14 -> 6.12 - platform-config r0.yml: *kernel-4-14 -> *kernel-6-12 ### Build layout - modules/builds/ uses KMODULES := src (was \$(wildcard *.c)) Several .c export symbols (cpld_read/write, fpga_pcie_*, at24_read_eeprom) consumed by sibling modules; without a shared src/ build dir each .c is compiled in its own tmp dir and modpost cannot resolve the EXPORT_SYMBOLs. ### Driver API (4.14 -> 6.12) - i2c_driver.probe is single-arg now; pull the device id with i2c_client_get_device_id(client) where needed. - i2c_driver.remove and platform_driver.remove return void. - hwmon_device_register_with_info(chip=NULL) returns -EINVAL on 6.12. psu.c grows a stub hwmon_chip_info; thermal_bmc.c switches to hwmon_device_register_with_groups which fits its sysfs-only use. - class_create lost its owner argument in 6.4 (fpga.c). - access_ok lost VERIFY_READ/WRITE in 5.0 (fpga.c). The bare unsigned long ioctl argument also needs an explicit (const void __user *) cast to satisfy the stricter prototype check. - pci_channel_state is no longer an enum tag in 6.12; switch the PCI error handler to pci_channel_state_t (fpga.c). - I2C_CLASS_SPD was removed; drop it from the i2c_adapter class mask (the controller does not actually decode SPD). - i2c_new_dummy was removed in 5.7; switch to i2c_new_dummy_device, which returns ERR_PTR (cleanup uses IS_ERR_OR_NULL now). - was removed in 5.5; inline the small struct + AT24_FLAG_* macros the driver actually consumes into at24_as7327_56x.c. ### IDPROM / SPD collision - Kernel 6.12 i2c-i801 now auto-instantiates a kernel-owned i2c_client named "ee1004" at every populated DDR4 SPD address (0x50 + slot), and the IDPROM EEPROM on this platform is wired to i2c-0 0x50. The SPD scan therefore claims the slot before the platform's new_i2c_device('accton_24c64', 0x50, 0) call, leaves the device unbound (no driver matches "ee1004"), and onlpdump shows no system info. The kernel-owned device cannot be removed from userspace. Workaround: alias "ee1004" onto the 24c64 (8KByte, ADDR16) IDPROM layout in at24_as7327_56x's id_table so our driver binds to the pre-existing client and exposes the eeprom sysfs file. Verified on DUT: TLV header decodes, MAC/Product/Serial all populated. --- .../any/templates/platform-modules_as7327.yml | 28 + .../accton/x86-64/as7327-56x/.gitignore | 3 + .../accton/x86-64/as7327-56x/Makefile | 1 + .../accton/x86-64/as7327-56x/modules/Makefile | 1 + .../accton/x86-64/as7327-56x/modules/PKG.yml | 1 + .../as7327-56x/modules/builds/.gitignore | 1 + .../x86-64/as7327-56x/modules/builds/Makefile | 6 + .../as7327-56x/modules/builds/src/Makefile | 7 + .../modules/builds/src/at24_as7327_56x.c | 986 +++++++++++ .../src/x86-64-accton-as7327-56x-cpld.c | 1514 +++++++++++++++++ .../src/x86-64-accton-as7327-56x-fpga.c | 1493 ++++++++++++++++ .../src/x86-64-accton-as7327-56x-leds.c | 485 ++++++ .../builds/src/x86-64-accton-as7327-56x-psu.c | 737 ++++++++ .../src/x86-64-accton-as7327-56x-psu_bmc.c | 972 +++++++++++ .../x86-64-accton-as7327-56x-thermal_bmc.c | 387 +++++ .../modules/builds/utils/fan_monitor.py | 959 +++++++++++ .../modules/builds/utils/fan_sysfs.config | 3 + .../modules/builds/utils/monitor_led.py | 224 +++ .../builds/utils/thermal_sensor_pid.config | 12 + .../accton/x86-64/as7327-56x/onlp/Makefile | 1 + .../accton/x86-64/as7327-56x/onlp/PKG.yml | 1 + .../x86-64/as7327-56x/onlp/builds/Makefile | 2 + .../as7327-56x/onlp/builds/lib/Makefile | 2 + .../as7327-56x/onlp/builds/onlpdump/Makefile | 2 + .../builds/x86_64_accton_as7327_56x/.module | 1 + .../builds/x86_64_accton_as7327_56x/Makefile | 9 + .../builds/x86_64_accton_as7327_56x/README | 6 + .../module/auto/make.mk | 9 + .../module/auto/x86_64_accton_as7327_56x.yml | 50 + .../x86_64_accton_as7327_56x.x | 14 + .../x86_64_accton_as7327_56x_config.h | 137 ++ .../x86_64_accton_as7327_56x_dox.h | 26 + .../x86_64_accton_as7327_56x_porting.h | 97 ++ .../x86_64_accton_as7327_56x/module/make.mk | 10 + .../module/src/Makefile | 9 + .../module/src/debug.c | 45 + .../module/src/fani.c | 463 +++++ .../module/src/ledi.c | 281 +++ .../module/src/make.mk | 9 + .../module/src/platform_lib.c | 207 +++ .../module/src/platform_lib.h | 104 ++ .../module/src/psui.c | 324 ++++ .../module/src/sfpi.c | 549 ++++++ .../module/src/sysi.c | 175 ++ .../module/src/thermali.c | 255 +++ .../src/x86_64_accton_as7327_56x_config.c | 81 + .../src/x86_64_accton_as7327_56x_enums.c | 10 + .../module/src/x86_64_accton_as7327_56x_int.h | 12 + .../module/src/x86_64_accton_as7327_56x_log.c | 18 + .../module/src/x86_64_accton_as7327_56x_log.h | 12 + .../src/x86_64_accton_as7327_56x_module.c | 24 + .../src/x86_64_accton_as7327_56x_ucli.c | 50 + .../as7327-56x/platform-config/Makefile | 1 + .../as7327-56x/platform-config/r0/Makefile | 1 + .../as7327-56x/platform-config/r0/PKG.yml | 1 + .../src/lib/x86-64-accton-as7327-56x-r0.yml | 39 + .../x86_64_accton_as7327_56x_r0/__init__.py | 125 ++ 57 files changed, 10982 insertions(+) create mode 100644 packages/base/any/templates/platform-modules_as7327.yml create mode 100644 packages/platforms/accton/x86-64/as7327-56x/.gitignore create mode 100644 packages/platforms/accton/x86-64/as7327-56x/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/.gitignore create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/at24_as7327_56x.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-cpld.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-fpga.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-leds.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu_bmc.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-thermal_bmc.c create mode 100755 packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_monitor.py create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_sysfs.config create mode 100755 packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/monitor_led.py create mode 100644 packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/thermal_sensor_pid.config create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/lib/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/onlpdump/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/.module create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/README create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/make.mk create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/x86_64_accton_as7327_56x.yml create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x.x create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_config.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_dox.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_porting.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/make.mk create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/debug.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/fani.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/ledi.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/make.mk create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/psui.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sfpi.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sysi.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/thermali.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_config.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_enums.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_int.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.h create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_module.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_ucli.c create mode 100644 packages/platforms/accton/x86-64/as7327-56x/platform-config/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/Makefile create mode 100644 packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/PKG.yml create mode 100644 packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/lib/x86-64-accton-as7327-56x-r0.yml create mode 100755 packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/python/x86_64_accton_as7327_56x_r0/__init__.py diff --git a/packages/base/any/templates/platform-modules_as7327.yml b/packages/base/any/templates/platform-modules_as7327.yml new file mode 100644 index 000000000..d625125d0 --- /dev/null +++ b/packages/base/any/templates/platform-modules_as7327.yml @@ -0,0 +1,28 @@ +############################################################ +# +# PKG template for all platform module packages +# +############################################################ +variables: + PLATFORM_NAME : ${BASENAME}-r0 + +prerequisites: + packages: + - $KERNELS + - onl-vendor-${VENDOR}-modules:$ARCH + +packages: + - name: onl-platform-modules-${BASENAME} + version: 1.0.0 + arch: $ARCH + copyright: Copyright 2013, 2014, 2015 Big Switch Networks + maintainer: support@bigswitch.com + support: opennetworklinux@googlegroups.com + summary: ONL Platform Modules Package for the ${BASENAME} + depends: onl-vendor-${VENDOR}-modules + + files: + - builds/lib: /lib + - builds/utils: /lib/platform-config/${PLATFORM_NAME}/onl/bin + + changelog: Changes diff --git a/packages/platforms/accton/x86-64/as7327-56x/.gitignore b/packages/platforms/accton/x86-64/as7327-56x/.gitignore new file mode 100644 index 000000000..7ec006278 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/.gitignore @@ -0,0 +1,3 @@ +*x86*64*accton*as7327*56x*.mk +onlpdump.mk + diff --git a/packages/platforms/accton/x86-64/as7327-56x/Makefile b/packages/platforms/accton/x86-64/as7327-56x/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/Makefile b/packages/platforms/accton/x86-64/as7327-56x/modules/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/PKG.yml b/packages/platforms/accton/x86-64/as7327-56x/modules/PKG.yml new file mode 100644 index 000000000..de4cfaa10 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-modules_as7327.yml VENDOR=accton BASENAME=x86-64-accton-as7327-56x ARCH=amd64 KERNELS="onl-kernel-6.12-lts-x86-64-all:amd64" diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/.gitignore b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/.gitignore new file mode 100644 index 000000000..a65b41774 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/Makefile b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/Makefile new file mode 100644 index 000000000..27cc9bfc4 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/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-as7327-56x +ARCH := x86_64 +include $(ONL)/make/kmodule.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/Makefile b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/Makefile new file mode 100644 index 000000000..39d848de3 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/Makefile @@ -0,0 +1,7 @@ +obj-m += x86-64-accton-as7327-56x-cpld.o +obj-m += x86-64-accton-as7327-56x-fpga.o +obj-m += x86-64-accton-as7327-56x-leds.o +obj-m += x86-64-accton-as7327-56x-psu.o +obj-m += x86-64-accton-as7327-56x-psu_bmc.o +obj-m += x86-64-accton-as7327-56x-thermal_bmc.o +obj-m += at24_as7327_56x.o diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/at24_as7327_56x.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/at24_as7327_56x.c new file mode 100644 index 000000000..018044ef6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/at24_as7327_56x.c @@ -0,0 +1,986 @@ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * 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 +#include +#include +#include + +/* + * Inlined from , which was removed from the + * kernel in 5.5 (the in-tree at24 driver moved this to a private header). + * Only the bits this driver consumes are kept. + */ +struct at24_platform_data { + u32 byte_len; + u16 page_size; + u8 flags; +#define AT24_FLAG_ADDR16 BIT(7) +#define AT24_FLAG_READONLY BIT(6) +#define AT24_FLAG_IRUGO BIT(5) +#define AT24_FLAG_TAKE8ADDR BIT(4) +#define AT24_FLAG_SERIAL BIT(3) +#define AT24_FLAG_MAC BIT(2) + + void (*setup)(struct nvmem_device *nvmem, void *context); + void *context; +}; + +#define MAX_EEPROM_NUM (10) +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_data { + struct at24_platform_data chip; + int use_smbus; + int use_smbus_write; + + ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t); + ssize_t (*write_func)(struct at24_data *, + const char *, unsigned int, size_t); + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + + u8 *writebuf; + unsigned write_max; + unsigned num_addresses; + + struct nvmem_config nvmem_config; + struct nvmem_device *nvmem; + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct i2c_client *client[]; +}; + +struct at24_data *accton_at24[MAX_EEPROM_NUM] = {NULL}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned io_limit = 128; +module_param(io_limit, uint, 0); +MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned write_timeout = 25; +module_param(write_timeout, uint, 0); +MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); + +#define AT24_SIZE_BYTELEN 5 +#define AT24_SIZE_FLAGS 8 + +#define AT24_BITMASK(x) (BIT(x) - 1) + +/* create non-zero magic value for given eeprom parameters */ +#define AT24_DEVICE_MAGIC(_len, _flags) \ + ((1 << AT24_SIZE_FLAGS | (_flags)) \ + << AT24_SIZE_BYTELEN | ilog2(_len)) + +/* + * Both reads and writes fail if the previous write didn't complete yet. This + * macro loops a few times waiting at least long enough for one entire page + * write to work while making sure that at least one iteration is run before + * checking the break condition. + * + * It takes two parameters: a variable in which the future timeout in jiffies + * will be stored and a temporary variable holding the time of the last + * iteration of processing the request. Both should be unsigned integers + * holding at least 32 bits. + */ +#define loop_until_timeout(tout, op_time) \ + for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \ + op_time ? time_before(op_time, tout) : true; \ + usleep_range(1000, 1500), op_time = jiffies) + +static const struct i2c_device_id at24_ids[] = { + /* needs 8 addresses as A0-A2 are ignored */ + { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, + /* old variants can't be handled with this generic entry! */ + { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24cs01", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + { "24cs02", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24mac402", AT24_DEVICE_MAGIC(48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, + { "24mac602", AT24_DEVICE_MAGIC(64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, + /* spd is a 24c02 in memory DIMMs */ + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + { "24cs04", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + /* 24rf08 quirk is handled at i2c-core */ + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24cs08", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24cs16", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24cs32", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, + { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24cs64", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, + { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, + { "accton_24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, + { "accton_24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + /* + * Kernel 6.12 i2c-i801 auto-instantiates an i2c_client named "ee1004" + * for every populated DDR4 SPD address (0x50+slot). The IDPROM EEPROM + * shares this address range, so the SPD scan grabs the slot before + * our userspace new_device call can. We accept the kernel-instantiated + * device by aliasing "ee1004" onto the 24c64 (8KByte, 16-bit-addressed) + * IDPROM layout this platform actually ships. + */ + { "ee1004", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, + { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "24c2048", AT24_DEVICE_MAGIC(2097152 / 8, AT24_FLAG_ADDR16) }, + { "at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +static const struct acpi_device_id at24_acpi_ids[] = { + { "INT3499", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { } +}; +MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); + +/*-------------------------------------------------------------------------*/ + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + * + * REVISIT some multi-address chips don't rollover page reads to + * the next slave address, so we may need to truncate the count. + * Those chips might need another quirk flag. + * + * If the real hardware used four adjacent 24c02 chips and that + * were misconfigured as one 24c08, that would be a similar effect: + * one "eeprom" file not four, but larger reads would fail when + * they crossed certain pages. + */ +static struct i2c_client *at24_translate_offset(struct at24_data *at24, + unsigned int *offset) +{ + unsigned i; + + if (at24->chip.flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return at24->client[i]; +} + +static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + int status; + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + /* Smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + + loop_until_timeout(timeout, read_time) { +/* + status = i2c_smbus_read_i2c_block_data_or_emulated(client, + offset, + count, buf); + + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); +*/ + int i=0; + + /*Write 2-byte offset*/ + unsigned char bb = offset&0xff; + status = i2c_smbus_write_i2c_block_data(client, offset>>8, 1, &bb); + if (status < 0) { + return status; + } + + while (i < count) { + status = i2c_smbus_read_byte(client); + if (status < 0) { + return status; + } + buf[i] = status; + i++; + } + + + if (i == count) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + int status, i; + u8 msgbuf[2]; + + memset(msg, 0, sizeof(msg)); + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + /* + * When we have a better choice than SMBus calls, use a combined I2C + * message. Write address; then read up to io_limit data bytes. Note + * that read page rollover helps us here (unlike writes). msgbuf is + * u8 and will cast to our needs. + */ + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; + + client = at24_translate_offset(at24, &offset); + + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; + + /* + * The address pointer of the device is shared between the regular + * EEPROM array and the serial number block. The dummy write (part of + * the sequential read protocol) ensures the address pointer is reset + * to the desired position. + */ + if (at24->chip.flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + addrbuf[0] = 0x08; + addrbuf[1] = offset; + msg[0].len = 2; + } else { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + addrbuf[0] = 0x80 + offset; + msg[0].len = 1; + } + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_read_mac(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; + + client = at24_translate_offset(at24, &offset); + + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + addrbuf[0] = 0xa0 - at24->chip.byte_len + offset; + msg[0].len = 1; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; + } + + return -ETIMEDOUT; +} + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. These routines + * write at most one page. + */ + +static size_t at24_adjust_write_count(struct at24_data *at24, + unsigned int offset, size_t count) +{ + unsigned next_page; + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->chip.page_size); + if (offset + count > next_page) + count = next_page - offset; + + return count; +} + +static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24, + const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + ssize_t status = 0; + + client = at24_translate_offset(at24, &offset); + count = at24_adjust_write_count(at24, offset, count); + + loop_until_timeout(timeout, write_time) { + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24, + const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + ssize_t status = 0; + + client = at24_translate_offset(at24, &offset); + + loop_until_timeout(timeout, write_time) { + status = i2c_smbus_write_byte_data(client, offset, buf[0]); + if (status == 0) + status = count; + + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status = 0; + int i = 0; + + client = at24_translate_offset(at24, &offset); + count = at24_adjust_write_count(at24, offset, count); + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + + loop_until_timeout(timeout, write_time) { + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} + +static int at24_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24 = priv; + char *buf = val; + + if (unlikely(!count)) + return count; + + if (off + count > at24->chip.byte_len) + return -EINVAL; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + int status; + + status = at24->read_func(at24, buf, off, count); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + + buf += status; + off += status; + count -= status; + } + + mutex_unlock(&at24->lock); + + return 0; +} + +ssize_t at24_read_eeprom(unsigned int adapter_nr, unsigned int addr, uint8_t *eeprom, unsigned int offset, unsigned int count) +{ + struct at24_data *at24 = NULL; + int i; + struct i2c_client *accton_client; + int ret; + void *temp = eeprom; + + for(i=0; iadapter->nr == adapter_nr) && (accton_client->addr == addr)) + { + at24 = accton_at24[i]; + break; + } + } + } + + if(at24 != NULL) + ret = at24_read(at24, offset, temp, (size_t)count); + else + return -ENXIO; + + return ret; +} +EXPORT_SYMBOL_GPL(at24_read_eeprom); + +static int at24_write(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24 = priv; + char *buf = val; + + if (unlikely(!count)) + return -EINVAL; + + if (off + count > at24->chip.byte_len) + return -EINVAL; + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + int status; + + status = at24->write_func(at24, buf, off, count); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + buf += status; + off += status; + count -= status; + } + + mutex_unlock(&at24->lock); + + return 0; +} + +#ifdef CONFIG_OF +static void at24_get_ofdata(struct i2c_client *client, + struct at24_platform_data *chip) +{ + const __be32 *val; + struct device_node *node = client->dev.of_node; + + if (node) { + if (of_get_property(node, "read-only", NULL)) + chip->flags |= AT24_FLAG_READONLY; + val = of_get_property(node, "pagesize", NULL); + if (val) + chip->page_size = be32_to_cpup(val); + } +} +#else +static void at24_get_ofdata(struct i2c_client *client, + struct at24_platform_data *chip) +{ } +#endif /* CONFIG_OF */ + +static int at24_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct at24_platform_data chip; + kernel_ulong_t magic = 0; + bool writable; + int use_smbus = 0; + int use_smbus_write = 0; + struct at24_data *at24; + int err; + unsigned i, num_addresses; + struct device *dev = &client->dev; + u8 test_byte; + + if (client->dev.platform_data) { + chip = *(struct at24_platform_data *)client->dev.platform_data; + } else { + if (id) { + magic = id->driver_data; + } else { + const struct acpi_device_id *aid; + + aid = acpi_match_device(at24_acpi_ids, &client->dev); + if (aid) + magic = aid->driver_data; + } + if (!magic) + return -ENODEV; + + chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); + magic >>= AT24_SIZE_BYTELEN; + chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + + /* update chipdata if OF is present */ + at24_get_ofdata(client, &chip); + + chip.setup = NULL; + chip.context = NULL; + } + + if (!is_power_of_2(chip.byte_len)) + dev_warn(&client->dev, + "byte_len looks suspicious (no power of 2)!\n"); + if (!chip.page_size) { + dev_err(&client->dev, "page_size must not be 0!\n"); + return -EINVAL; + } + if (!is_power_of_2(chip.page_size)) + dev_warn(&client->dev, + "page_size looks suspicious (no power of 2)!\n"); + + /* + * REVISIT: the size of the EUI-48 byte array is 6 in at24mac402, while + * the call to ilog2() in AT24_DEVICE_MAGIC() rounds it down to 4. + * + * Eventually we'll get rid of the magic values altoghether in favor of + * real structs, but for now just manually set the right size. + */ + if (chip.flags & AT24_FLAG_MAC && chip.byte_len == 4) + chip.byte_len = 6; + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + //if (chip.flags & AT24_FLAG_ADDR16) + //return -EPFNOSUPPORT; + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + use_smbus = I2C_SMBUS_WORD_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + use_smbus = I2C_SMBUS_BYTE_DATA; + } else { + return -EPFNOSUPPORT; + } + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA; + } else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + use_smbus_write = I2C_SMBUS_BYTE_DATA; + chip.page_size = 1; + } + } + + if (chip.flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(chip.byte_len, + (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + + at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); + if (!at24) + return -ENOMEM; + + mutex_init(&at24->lock); + at24->use_smbus = use_smbus; + at24->use_smbus_write = use_smbus_write; + at24->chip = chip; + at24->num_addresses = num_addresses; + + if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) { + dev_err(&client->dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + mutex_destroy(&at24->lock); + return -EINVAL; + } + + if (chip.flags & AT24_FLAG_SERIAL) { + at24->read_func = at24_eeprom_read_serial; + } else if (chip.flags & AT24_FLAG_MAC) { + at24->read_func = at24_eeprom_read_mac; + } else { + at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus + : at24_eeprom_read_i2c; + } + + if (at24->use_smbus) { + if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA) + at24->write_func = at24_eeprom_write_smbus_block; + else + at24->write_func = at24_eeprom_write_smbus_byte; + } else { + at24->write_func = at24_eeprom_write_i2c; + } + + writable = !(chip.flags & AT24_FLAG_READONLY); + if (writable) { + if (!use_smbus || use_smbus_write) { + + unsigned write_max = chip.page_size; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + at24->write_max = write_max; + + /* buffer (data + address at the beginning) */ + at24->writebuf = devm_kzalloc(&client->dev, + write_max + 2, GFP_KERNEL); + if (!at24->writebuf) { + mutex_destroy(&at24->lock); + return -ENOMEM; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + } + + at24->client[0] = client; + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + at24->client[i] = i2c_new_dummy_device(client->adapter, + client->addr + i); + if (IS_ERR(at24->client[i])) { + dev_err(&client->dev, "address 0x%02x unavailable\n", + client->addr + i); + err = PTR_ERR(at24->client[i]); + goto err_clients; + } + } + + i2c_set_clientdata(client, at24); + + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + if (err) { + err = -ENODEV; + goto err_clients; + } + + at24->nvmem_config.name = dev_name(&client->dev); + at24->nvmem_config.dev = &client->dev; + at24->nvmem_config.read_only = !writable; + at24->nvmem_config.root_only = !(chip.flags & AT24_FLAG_IRUGO); + at24->nvmem_config.owner = THIS_MODULE; + at24->nvmem_config.compat = true; + at24->nvmem_config.base_dev = &client->dev; + at24->nvmem_config.reg_read = at24_read; + at24->nvmem_config.reg_write = at24_write; + at24->nvmem_config.priv = at24; + at24->nvmem_config.stride = 1; + at24->nvmem_config.word_size = 1; + at24->nvmem_config.size = chip.byte_len; + + at24->nvmem = nvmem_register(&at24->nvmem_config); + + if (IS_ERR(at24->nvmem)) { + err = PTR_ERR(at24->nvmem); + goto err_clients; + } + + dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", + chip.byte_len, client->name, + writable ? "writable" : "read-only", at24->write_max); + if (use_smbus == I2C_SMBUS_WORD_DATA || + use_smbus == I2C_SMBUS_BYTE_DATA) { + dev_notice(&client->dev, "Falling back to %s reads, " + "performance will suffer\n", use_smbus == + I2C_SMBUS_WORD_DATA ? "word" : "byte"); + } + + /* export data to kernel code */ + if (chip.setup) + chip.setup(at24->nvmem, chip.context); + + for(i=0; ilock); + return -1; + } + + break; + } + } + + return 0; + +err_clients: + for (i = 1; i < num_addresses; i++) + if (!IS_ERR_OR_NULL(at24->client[i])) + i2c_unregister_device(at24->client[i]); + mutex_destroy(&at24->lock); + + return err; +} + +static void at24_remove(struct i2c_client *client) +{ + struct at24_data *at24; + int i; + + at24 = i2c_get_clientdata(client); + + nvmem_unregister(at24->nvmem); + + for (i = 1; i < at24->num_addresses; i++) + i2c_unregister_device(at24->client[i]); + + for(i=0; ilock); +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver at24_driver = { + .driver = { + .name = "at24_as7327_56x", + .acpi_match_table = ACPI_PTR(at24_acpi_ids), + }, + .probe = at24_probe, + .remove = at24_remove, + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + if (!io_limit) { + pr_err("at24: io_limit must not be 0!\n"); + return -EINVAL; + } + + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for I2C EEPROMs on accton switch, as7327_56x"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-cpld.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-cpld.c new file mode 100644 index 000000000..77202fee0 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-cpld.c @@ -0,0 +1,1514 @@ +/* + * Copyright (C) Brandon Cheng + * + * This module supports the accton cpld that hold the channel select + * mechanism for other i2c slave devices, such as SFP. + * This includes the: + * Accton as7327_56x CPLD1/CPLD2 + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ +#define FAN_MAX_DUTY_CYCLE 100 +#define FAN_TECK_SPEED_CNT 150 // 1/167.67*1000/2*60 = 178.92 + +#define BMC_PRESENT_OFFSET 0x7 +#define BMC_PRESENT_MASK 0x1 +#define BMC_HEART_OFFSET 0x0 +#define BMC_HEART_MASK 0x7 + +#define BMC_PRESENT 0 +#define BMC_NOT_PRESENT 1 + +#define BMC_ENABLE_OFFSET 0x0 +#define BMC_ENABLE_MASK 0x1 + +#define BMC_EN_ENABLE 1 +#define BMC_EN_DISABLE 0 + +#define BMC_HEART_FRQ_10HZ 0 +#define BMC_HEART_FRQ_2HZ 1 +#define BMC_HEART_FRQ_05HZ 2 +#define BMC_HEART_FRQ_01HZ 3 +#define BMC_HEART_FRQ_0HZ 4 + +static LIST_HEAD(cpld_client_list); +static struct mutex list_lock; + +struct cpld_client_node { + struct i2c_client *client; + struct list_head list; +}; + +enum cpld_type { + as7327_56x_cpld1, + as7327_56x_cpld2 +}; + +enum fan_box_id { + FAN_BOX1_ID, + FAN_BOX2_ID, +}; + +/* 2 fan-tray modules with 4 pcs of 40mmx40mmx56mm 12V fans, hot-swappable */ +static const u8 fan_reg[] = { + 0x52, /* fan box's status and direction */ + 0x36, /* fan PWM(for fan1), duty cycle */ + 0x37, /* fan PWM(for fan2), duty cycle */ + 0x38, /* fan PWM(for fan3), duty cycle */ + 0x39, /* fan PWM(for fan4), duty cycle */ + 0x3A, /* front fan1 speed(rpm) */ + 0x3B, /* front fan2 speed(rpm) */ + 0x3C, /* front fan3 speed(rpm) */ + 0x3D, /* front fan4 speed(rpm) */ + 0x3E, /* rear fan1 speed(rpm) */ + 0x3F, /* rear fan2 speed(rpm) */ + 0x40, /* rear fan3 speed(rpm) */ + 0x41, /* rear fan4 speed(rpm) */ + 0x2e, /* fan watchdog en */ + 0x2f, /* fan watchdog setting */ +}; + +#define FAN_WATCHDOG_EN_REG 0x2E + +struct as7327_56x_cpld_data { + enum cpld_type type; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_fan_val[ARRAY_SIZE(fan_reg)]; /* Register value */ + +}; + +static const struct i2c_device_id as7327_56x_cpld_id[] = { + { "as7327_56x_cpld1", as7327_56x_cpld1 }, + { "as7327_56x_cpld2", as7327_56x_cpld2 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as7327_56x_cpld_id); + +#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index +#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index +#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index +#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index +#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index +#define TRANSCEIVER_LPMODE_ATTR_ID(index) MODULE_LPMODE_##index + +#define FAN_DIRECTION_ID(index) FAN_DIRECTION_##index // by fan box(module) +#define FAN_PRESENT_ATTR_ID(index) FAN_PRESENT_##index // by fan box(module) +#define FAN_FRONT_SPEED_RPM_ATTR_ID(index) FAN_FRONT_SPEED_RPM_##index // by each fan +#define FAN_REAR_SPEED_RPM_ATTR_ID(index) FAN_REAR_SPEED_RPM_##index // by each fan + +enum as7327_56x_cpld_sysfs_attributes { + CPLD_VERSION, + ACCESS, + MODULE_PRESENT_ALL, + MODULE_RXLOS_ALL, + /* transceiver attributes */ + TRANSCEIVER_PRESENT_ATTR_ID(1), + TRANSCEIVER_PRESENT_ATTR_ID(2), + TRANSCEIVER_PRESENT_ATTR_ID(3), + TRANSCEIVER_PRESENT_ATTR_ID(4), + TRANSCEIVER_PRESENT_ATTR_ID(5), + TRANSCEIVER_PRESENT_ATTR_ID(6), + TRANSCEIVER_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_TXDISABLE_ATTR_ID(1), + TRANSCEIVER_TXDISABLE_ATTR_ID(2), + TRANSCEIVER_TXDISABLE_ATTR_ID(3), + TRANSCEIVER_TXDISABLE_ATTR_ID(4), + TRANSCEIVER_TXDISABLE_ATTR_ID(5), + TRANSCEIVER_TXDISABLE_ATTR_ID(6), + TRANSCEIVER_TXDISABLE_ATTR_ID(7), + TRANSCEIVER_TXDISABLE_ATTR_ID(8), + TRANSCEIVER_TXDISABLE_ATTR_ID(9), + TRANSCEIVER_TXDISABLE_ATTR_ID(10), + TRANSCEIVER_TXDISABLE_ATTR_ID(11), + TRANSCEIVER_TXDISABLE_ATTR_ID(12), + TRANSCEIVER_TXDISABLE_ATTR_ID(13), + TRANSCEIVER_TXDISABLE_ATTR_ID(14), + TRANSCEIVER_TXDISABLE_ATTR_ID(15), + TRANSCEIVER_TXDISABLE_ATTR_ID(16), + TRANSCEIVER_TXDISABLE_ATTR_ID(17), + TRANSCEIVER_TXDISABLE_ATTR_ID(18), + TRANSCEIVER_TXDISABLE_ATTR_ID(19), + TRANSCEIVER_TXDISABLE_ATTR_ID(20), + TRANSCEIVER_TXDISABLE_ATTR_ID(21), + TRANSCEIVER_TXDISABLE_ATTR_ID(22), + TRANSCEIVER_TXDISABLE_ATTR_ID(23), + TRANSCEIVER_TXDISABLE_ATTR_ID(24), + TRANSCEIVER_TXDISABLE_ATTR_ID(25), + TRANSCEIVER_TXDISABLE_ATTR_ID(26), + TRANSCEIVER_TXDISABLE_ATTR_ID(27), + TRANSCEIVER_TXDISABLE_ATTR_ID(28), + TRANSCEIVER_TXDISABLE_ATTR_ID(29), + TRANSCEIVER_TXDISABLE_ATTR_ID(30), + TRANSCEIVER_TXDISABLE_ATTR_ID(31), + TRANSCEIVER_TXDISABLE_ATTR_ID(32), + TRANSCEIVER_TXDISABLE_ATTR_ID(33), + TRANSCEIVER_TXDISABLE_ATTR_ID(34), + TRANSCEIVER_TXDISABLE_ATTR_ID(35), + TRANSCEIVER_TXDISABLE_ATTR_ID(36), + TRANSCEIVER_TXDISABLE_ATTR_ID(37), + TRANSCEIVER_TXDISABLE_ATTR_ID(38), + TRANSCEIVER_TXDISABLE_ATTR_ID(39), + TRANSCEIVER_TXDISABLE_ATTR_ID(40), + TRANSCEIVER_TXDISABLE_ATTR_ID(41), + TRANSCEIVER_TXDISABLE_ATTR_ID(42), + TRANSCEIVER_TXDISABLE_ATTR_ID(43), + TRANSCEIVER_TXDISABLE_ATTR_ID(44), + TRANSCEIVER_TXDISABLE_ATTR_ID(45), + TRANSCEIVER_TXDISABLE_ATTR_ID(46), + TRANSCEIVER_TXDISABLE_ATTR_ID(47), + TRANSCEIVER_TXDISABLE_ATTR_ID(48), + TRANSCEIVER_RXLOS_ATTR_ID(1), + TRANSCEIVER_RXLOS_ATTR_ID(2), + TRANSCEIVER_RXLOS_ATTR_ID(3), + TRANSCEIVER_RXLOS_ATTR_ID(4), + TRANSCEIVER_RXLOS_ATTR_ID(5), + TRANSCEIVER_RXLOS_ATTR_ID(6), + TRANSCEIVER_RXLOS_ATTR_ID(7), + TRANSCEIVER_RXLOS_ATTR_ID(8), + TRANSCEIVER_RXLOS_ATTR_ID(9), + TRANSCEIVER_RXLOS_ATTR_ID(10), + TRANSCEIVER_RXLOS_ATTR_ID(11), + TRANSCEIVER_RXLOS_ATTR_ID(12), + TRANSCEIVER_RXLOS_ATTR_ID(13), + TRANSCEIVER_RXLOS_ATTR_ID(14), + TRANSCEIVER_RXLOS_ATTR_ID(15), + TRANSCEIVER_RXLOS_ATTR_ID(16), + TRANSCEIVER_RXLOS_ATTR_ID(17), + TRANSCEIVER_RXLOS_ATTR_ID(18), + TRANSCEIVER_RXLOS_ATTR_ID(19), + TRANSCEIVER_RXLOS_ATTR_ID(20), + TRANSCEIVER_RXLOS_ATTR_ID(21), + TRANSCEIVER_RXLOS_ATTR_ID(22), + TRANSCEIVER_RXLOS_ATTR_ID(23), + TRANSCEIVER_RXLOS_ATTR_ID(24), + TRANSCEIVER_RXLOS_ATTR_ID(25), + TRANSCEIVER_RXLOS_ATTR_ID(26), + TRANSCEIVER_RXLOS_ATTR_ID(27), + TRANSCEIVER_RXLOS_ATTR_ID(28), + TRANSCEIVER_RXLOS_ATTR_ID(29), + TRANSCEIVER_RXLOS_ATTR_ID(30), + TRANSCEIVER_RXLOS_ATTR_ID(31), + TRANSCEIVER_RXLOS_ATTR_ID(32), + TRANSCEIVER_RXLOS_ATTR_ID(33), + TRANSCEIVER_RXLOS_ATTR_ID(34), + TRANSCEIVER_RXLOS_ATTR_ID(35), + TRANSCEIVER_RXLOS_ATTR_ID(36), + TRANSCEIVER_RXLOS_ATTR_ID(37), + TRANSCEIVER_RXLOS_ATTR_ID(38), + TRANSCEIVER_RXLOS_ATTR_ID(39), + TRANSCEIVER_RXLOS_ATTR_ID(40), + TRANSCEIVER_RXLOS_ATTR_ID(41), + TRANSCEIVER_RXLOS_ATTR_ID(42), + TRANSCEIVER_RXLOS_ATTR_ID(43), + TRANSCEIVER_RXLOS_ATTR_ID(44), + TRANSCEIVER_RXLOS_ATTR_ID(45), + TRANSCEIVER_RXLOS_ATTR_ID(46), + TRANSCEIVER_RXLOS_ATTR_ID(47), + TRANSCEIVER_RXLOS_ATTR_ID(48), + TRANSCEIVER_TXFAULT_ATTR_ID(1), + TRANSCEIVER_TXFAULT_ATTR_ID(2), + TRANSCEIVER_TXFAULT_ATTR_ID(3), + TRANSCEIVER_TXFAULT_ATTR_ID(4), + TRANSCEIVER_TXFAULT_ATTR_ID(5), + TRANSCEIVER_TXFAULT_ATTR_ID(6), + TRANSCEIVER_TXFAULT_ATTR_ID(7), + TRANSCEIVER_TXFAULT_ATTR_ID(8), + TRANSCEIVER_TXFAULT_ATTR_ID(9), + TRANSCEIVER_TXFAULT_ATTR_ID(10), + TRANSCEIVER_TXFAULT_ATTR_ID(11), + TRANSCEIVER_TXFAULT_ATTR_ID(12), + TRANSCEIVER_TXFAULT_ATTR_ID(13), + TRANSCEIVER_TXFAULT_ATTR_ID(14), + TRANSCEIVER_TXFAULT_ATTR_ID(15), + TRANSCEIVER_TXFAULT_ATTR_ID(16), + TRANSCEIVER_TXFAULT_ATTR_ID(17), + TRANSCEIVER_TXFAULT_ATTR_ID(18), + TRANSCEIVER_TXFAULT_ATTR_ID(19), + TRANSCEIVER_TXFAULT_ATTR_ID(20), + TRANSCEIVER_TXFAULT_ATTR_ID(21), + TRANSCEIVER_TXFAULT_ATTR_ID(22), + TRANSCEIVER_TXFAULT_ATTR_ID(23), + TRANSCEIVER_TXFAULT_ATTR_ID(24), + TRANSCEIVER_TXFAULT_ATTR_ID(25), + TRANSCEIVER_TXFAULT_ATTR_ID(26), + TRANSCEIVER_TXFAULT_ATTR_ID(27), + TRANSCEIVER_TXFAULT_ATTR_ID(28), + TRANSCEIVER_TXFAULT_ATTR_ID(29), + TRANSCEIVER_TXFAULT_ATTR_ID(30), + TRANSCEIVER_TXFAULT_ATTR_ID(31), + TRANSCEIVER_TXFAULT_ATTR_ID(32), + TRANSCEIVER_TXFAULT_ATTR_ID(33), + TRANSCEIVER_TXFAULT_ATTR_ID(34), + TRANSCEIVER_TXFAULT_ATTR_ID(35), + TRANSCEIVER_TXFAULT_ATTR_ID(36), + TRANSCEIVER_TXFAULT_ATTR_ID(37), + TRANSCEIVER_TXFAULT_ATTR_ID(38), + TRANSCEIVER_TXFAULT_ATTR_ID(39), + TRANSCEIVER_TXFAULT_ATTR_ID(40), + TRANSCEIVER_TXFAULT_ATTR_ID(41), + TRANSCEIVER_TXFAULT_ATTR_ID(42), + TRANSCEIVER_TXFAULT_ATTR_ID(43), + TRANSCEIVER_TXFAULT_ATTR_ID(44), + TRANSCEIVER_TXFAULT_ATTR_ID(45), + TRANSCEIVER_TXFAULT_ATTR_ID(46), + TRANSCEIVER_TXFAULT_ATTR_ID(47), + TRANSCEIVER_TXFAULT_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_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), + FAN_PRESENT_ATTR_ID(1), + FAN_PRESENT_ATTR_ID(2), + FAN_DIRECTION_ID(1), + FAN_DIRECTION_ID(2), + FAN_FRONT_SPEED_RPM_ATTR_ID(1), + FAN_FRONT_SPEED_RPM_ATTR_ID(2), + FAN_FRONT_SPEED_RPM_ATTR_ID(3), + FAN_FRONT_SPEED_RPM_ATTR_ID(4), + FAN_REAR_SPEED_RPM_ATTR_ID(1), + FAN_REAR_SPEED_RPM_ATTR_ID(2), + FAN_REAR_SPEED_RPM_ATTR_ID(3), + FAN_REAR_SPEED_RPM_ATTR_ID(4), + FAN_DUTY_CYCLE_PERCENTAGE, + FAN_WDT_ENABLE, + FAN_WDT_CLEAR, + FAN_WDT_COUNT, + SYSLED_WDT_CLEAR, + BMC_STATUS, + BMC_ENABLE +}; + +/* sysfs attributes for hwmon + */ +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_bmc_enable(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_qsfp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t show_version(struct device *dev, struct device_attribute *da, + char *buf); +static int as7327_56x_cpld_read_internal(struct i2c_client *client, u8 reg); +static int as7327_56x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value); + + +/*fan sysfs*/ +static struct as7327_56x_cpld_data *as7327_56x_fan_update_device(struct device *dev); +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_fan_wdt_status(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_fan_wdt_clear(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); +static ssize_t set_fan_wdt_count(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +/*sysled wdt sysfs*/ +static ssize_t sysled_wdt_show_value(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t set_sysled_wdt_clear(struct device *dev, struct device_attribute *da, + const char *buf, size_t count); + +/* transceiver attributes */ +#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO | S_IWUSR, show_status, set_tx_disable, MODULE_TXDISABLE_##index); \ + static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \ + static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index) +#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_present_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \ + &sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \ + &sensor_dev_attr_module_tx_fault_##index.dev_attr.attr + +#define DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \ + static SENSOR_DEVICE_ATTR(module_lpmode_##index, S_IRUGO | S_IWUSR, show_status, set_qsfp, MODULE_LPMODE_##index); \ + static SENSOR_DEVICE_ATTR(module_reset_##index, S_IRUGO | S_IWUSR, show_status, set_qsfp, MODULE_RESET_##index); \ + static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index); + +#define DECLARE_QSFP_TRANSCEIVER_ATTR(index) \ + &sensor_dev_attr_module_lpmode_##index.dev_attr.attr, \ + &sensor_dev_attr_module_reset_##index.dev_attr.attr, \ + &sensor_dev_attr_module_present_##index.dev_attr.attr + +#define DECLARE_FAN_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_present_##index, S_IRUGO, fan_show_value, NULL, FAN_PRESENT_##index); \ + static SENSOR_DEVICE_ATTR(fan_direction_##index, S_IRUGO, fan_show_value, NULL, FAN_DIRECTION_##index); +#define DECLARE_FAN_BOX_ATTR(index) \ + &sensor_dev_attr_fan_present_##index.dev_attr.attr, \ + &sensor_dev_attr_fan_direction_##index.dev_attr.attr + +#define DECLARE_FAN_SENSOR_ATTR1(index) \ + static SENSOR_DEVICE_ATTR(fan_front_speed_rpm_##index, S_IRUGO, fan_show_value, NULL, FAN_FRONT_SPEED_RPM_##index); +#define DECLARE_FAN_FRONT_ATTR(index) \ + &sensor_dev_attr_fan_front_speed_rpm_##index.dev_attr.attr + +#define DECLARE_FAN_SENSOR_ATTR2(index) \ + static SENSOR_DEVICE_ATTR(fan_rear_speed_rpm_##index, S_IRUGO, fan_show_value, NULL, FAN_REAR_SPEED_RPM_##index); +#define DECLARE_FAN_REAR_ATTR(index) \ + &sensor_dev_attr_fan_rear_speed_rpm_##index.dev_attr.attr + +#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \ + static SENSOR_DEVICE_ATTR(fan_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN_DUTY_CYCLE_PERCENTAGE); +#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) \ + &sensor_dev_attr_fan_duty_cycle_percentage.dev_attr.attr + +#define DECLARE_FAN_WDT_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(fan_wdt_status, S_IWUSR | S_IRUGO, fan_show_value, set_fan_wdt_status, FAN_WDT_ENABLE);\ + static SENSOR_DEVICE_ATTR(fan_wdt_clear, S_IWUSR | S_IRUGO, fan_show_value, set_fan_wdt_clear, FAN_WDT_CLEAR);\ + static SENSOR_DEVICE_ATTR(fan_wdt_count, S_IWUSR | S_IRUGO, fan_show_value, set_fan_wdt_count, FAN_WDT_COUNT) +#define DECLARE_FAN_WDT_STATUS_ATTR() &sensor_dev_attr_fan_wdt_status.dev_attr.attr +#define DECLARE_FAN_WDT_CLEAR_ATTR() &sensor_dev_attr_fan_wdt_clear.dev_attr.attr +#define DECLARE_FAN_WDT_COUNT_ATTR() &sensor_dev_attr_fan_wdt_count.dev_attr.attr + +#define DECLARE_SYSLED_WDT_SENSOR_DEV_ATTR() \ + static SENSOR_DEVICE_ATTR(sysled_wdt_clear, S_IWUSR | S_IRUGO, sysled_wdt_show_value, set_sysled_wdt_clear, SYSLED_WDT_CLEAR); +#define DECLARE_SYSLED_WDT_CLEAR_ATTR() \ + &sensor_dev_attr_sysled_wdt_clear.dev_attr.attr + +static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION); +static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS); +/* transceiver attributes */ +static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL); +static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL); +/* bmc enable */ +static SENSOR_DEVICE_ATTR(bmc_enable, S_IRUGO, show_bmc_enable, NULL, BMC_ENABLE); + +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(5); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(6); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(7); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(8); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(9); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(10); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(11); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(12); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(13); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(14); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(15); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(16); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(17); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(18); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(19); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(20); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(21); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(22); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(23); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(24); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(25); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(26); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(27); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(28); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(29); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(30); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(31); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(32); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(33); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(34); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(35); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(36); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(37); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(38); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(39); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(40); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(41); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(42); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(43); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(44); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(45); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(46); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(47); +DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(48); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(49); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(50); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(51); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(52); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(53); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(54); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(55); +DECLARE_QSFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(56); + +/* fan attributes */ +DECLARE_FAN_SENSOR_DEV_ATTR(1); +DECLARE_FAN_SENSOR_DEV_ATTR(2); +DECLARE_FAN_SENSOR_ATTR1(1); +DECLARE_FAN_SENSOR_ATTR1(2); +DECLARE_FAN_SENSOR_ATTR1(3); +DECLARE_FAN_SENSOR_ATTR1(4); +DECLARE_FAN_SENSOR_ATTR2(1); +DECLARE_FAN_SENSOR_ATTR2(2); +DECLARE_FAN_SENSOR_ATTR2(3); +DECLARE_FAN_SENSOR_ATTR2(4); +DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(1); +DECLARE_FAN_WDT_SENSOR_DEV_ATTR(); + +/* sysled wdt attribute */ +DECLARE_SYSLED_WDT_SENSOR_DEV_ATTR(); + +static struct attribute *as7327_56x_cpld1_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + /* bmc enable */ + &sensor_dev_attr_bmc_enable.dev_attr.attr, + DECLARE_SFP_TRANSCEIVER_ATTR(1), + DECLARE_SFP_TRANSCEIVER_ATTR(2), + DECLARE_SFP_TRANSCEIVER_ATTR(3), + DECLARE_SFP_TRANSCEIVER_ATTR(4), + DECLARE_SFP_TRANSCEIVER_ATTR(5), + DECLARE_SFP_TRANSCEIVER_ATTR(6), + DECLARE_SFP_TRANSCEIVER_ATTR(7), + DECLARE_SFP_TRANSCEIVER_ATTR(8), + DECLARE_SFP_TRANSCEIVER_ATTR(9), + DECLARE_SFP_TRANSCEIVER_ATTR(10), + DECLARE_SFP_TRANSCEIVER_ATTR(11), + DECLARE_SFP_TRANSCEIVER_ATTR(12), + DECLARE_FAN_BOX_ATTR(1), + DECLARE_FAN_BOX_ATTR(2), + DECLARE_FAN_FRONT_ATTR(1), + DECLARE_FAN_FRONT_ATTR(2), + DECLARE_FAN_FRONT_ATTR(3), + DECLARE_FAN_FRONT_ATTR(4), + DECLARE_FAN_REAR_ATTR(1), + DECLARE_FAN_REAR_ATTR(2), + DECLARE_FAN_REAR_ATTR(3), + DECLARE_FAN_REAR_ATTR(4), + DECLARE_FAN_DUTY_CYCLE_ATTR(1), + DECLARE_FAN_WDT_STATUS_ATTR(), + DECLARE_FAN_WDT_CLEAR_ATTR(), + DECLARE_FAN_WDT_COUNT_ATTR(), + DECLARE_SYSLED_WDT_CLEAR_ATTR(), + NULL +}; + +static const struct attribute_group as7327_56x_cpld1_group = { + .attrs = as7327_56x_cpld1_attributes, +}; + +static struct attribute *as7327_56x_cpld2_attributes[] = { + &sensor_dev_attr_version.dev_attr.attr, + &sensor_dev_attr_access.dev_attr.attr, + /* transceiver attributes */ + &sensor_dev_attr_module_present_all.dev_attr.attr, + &sensor_dev_attr_module_rx_los_all.dev_attr.attr, + DECLARE_SFP_TRANSCEIVER_ATTR(13), + DECLARE_SFP_TRANSCEIVER_ATTR(14), + DECLARE_SFP_TRANSCEIVER_ATTR(15), + DECLARE_SFP_TRANSCEIVER_ATTR(16), + DECLARE_SFP_TRANSCEIVER_ATTR(17), + DECLARE_SFP_TRANSCEIVER_ATTR(18), + DECLARE_SFP_TRANSCEIVER_ATTR(19), + DECLARE_SFP_TRANSCEIVER_ATTR(20), + DECLARE_SFP_TRANSCEIVER_ATTR(21), + DECLARE_SFP_TRANSCEIVER_ATTR(22), + DECLARE_SFP_TRANSCEIVER_ATTR(23), + DECLARE_SFP_TRANSCEIVER_ATTR(24), + DECLARE_SFP_TRANSCEIVER_ATTR(25), + DECLARE_SFP_TRANSCEIVER_ATTR(26), + DECLARE_SFP_TRANSCEIVER_ATTR(27), + DECLARE_SFP_TRANSCEIVER_ATTR(28), + DECLARE_SFP_TRANSCEIVER_ATTR(29), + DECLARE_SFP_TRANSCEIVER_ATTR(30), + DECLARE_SFP_TRANSCEIVER_ATTR(31), + DECLARE_SFP_TRANSCEIVER_ATTR(32), + DECLARE_SFP_TRANSCEIVER_ATTR(33), + DECLARE_SFP_TRANSCEIVER_ATTR(34), + DECLARE_SFP_TRANSCEIVER_ATTR(35), + DECLARE_SFP_TRANSCEIVER_ATTR(36), + DECLARE_SFP_TRANSCEIVER_ATTR(37), + DECLARE_SFP_TRANSCEIVER_ATTR(38), + DECLARE_SFP_TRANSCEIVER_ATTR(39), + DECLARE_SFP_TRANSCEIVER_ATTR(40), + DECLARE_SFP_TRANSCEIVER_ATTR(41), + DECLARE_SFP_TRANSCEIVER_ATTR(42), + DECLARE_SFP_TRANSCEIVER_ATTR(43), + DECLARE_SFP_TRANSCEIVER_ATTR(44), + DECLARE_SFP_TRANSCEIVER_ATTR(45), + DECLARE_SFP_TRANSCEIVER_ATTR(46), + DECLARE_SFP_TRANSCEIVER_ATTR(47), + DECLARE_SFP_TRANSCEIVER_ATTR(48), + DECLARE_QSFP_TRANSCEIVER_ATTR(49), + DECLARE_QSFP_TRANSCEIVER_ATTR(50), + DECLARE_QSFP_TRANSCEIVER_ATTR(51), + DECLARE_QSFP_TRANSCEIVER_ATTR(52), + DECLARE_QSFP_TRANSCEIVER_ATTR(53), + DECLARE_QSFP_TRANSCEIVER_ATTR(54), + DECLARE_QSFP_TRANSCEIVER_ATTR(55), + DECLARE_QSFP_TRANSCEIVER_ATTR(56), + NULL +}; + +static const struct attribute_group as7327_56x_cpld2_group = { + .attrs = as7327_56x_cpld2_attributes, +}; + +static ssize_t show_present_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[6] = {0}; + u8 regs_cpld1[] = {0x17, 0x18}; + u8 regs_cpld2[] = {0xf, 0x10, 0x11, 0x12, 0x13, 0x1f}; + u8 *regs[] = {regs_cpld1, regs_cpld2}; + u8 size[] = {ARRAY_SIZE(regs_cpld1), ARRAY_SIZE(regs_cpld2)}; + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < size[data->type]; i++) { + status = as7327_56x_cpld_read_internal(client, regs[data->type][i]); + if (status < 0) { + goto exit; + } + + values[i] = ~(u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values in order */ + if (data->type == as7327_56x_cpld1) { + values[1] &= 0xF; + return sprintf(buf, "%.2x %.2x\n", + values[0], values[1]); + } + else { /* as7327_56x_cpld2 */ + values[4] &= 0xf; + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3], values[4], values[5]); + } + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da, + char *buf) +{ + int i, status; + u8 values[5] = {0}; + u8 regs_cpld1[] = {0x15, 0x16}; + u8 regs_cpld2[] = {0xa, 0xb, 0xc, 0xd, 0xe}; + u8 *regs[] = {regs_cpld1, regs_cpld2}; + u8 size[] = {ARRAY_SIZE(regs_cpld1), ARRAY_SIZE(regs_cpld2)}; + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + for (i = 0; i < size[data->type]; i++) { + status = as7327_56x_cpld_read_internal(client, regs[data->type][i]); + if (status < 0) { + goto exit; + } + + values[i] = (u8)status; + } + + mutex_unlock(&data->update_lock); + + /* Return values in order */ + if (data->type == as7327_56x_cpld1) { + values[1] &= 0xF; + return sprintf(buf, "%.2x %.2x\n", + values[0], values[1]); + } + else { /* as7327_56x_cpld2 */ + values[4] &= 0xF; + return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x\n", + values[0], values[1], values[2], values[3], values[4]); + } + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + int status = 0; + u8 reg = 0, mask = 0, revert = 0; + + switch (attr->index) { + case MODULE_PRESENT_1 ... MODULE_PRESENT_12: + reg = 0x17 + (attr->index-MODULE_PRESENT_1)/8; + mask = 0x1 << ((attr->index - MODULE_PRESENT_1)%8); + break; + case MODULE_PRESENT_13 ... MODULE_PRESENT_48: + reg = 0xf + (attr->index-MODULE_PRESENT_13)/8; + mask = 0x1 << ((attr->index - MODULE_PRESENT_13)%8); + break; + case MODULE_PRESENT_49 ... MODULE_PRESENT_56: /*QSFP*/ + reg = 0x1f ; + mask = 0x1 << ((attr->index - MODULE_PRESENT_49)%8); + break; + case MODULE_TXFAULT_1 ... MODULE_TXFAULT_12: + reg = 0x19 + (attr->index - MODULE_TXFAULT_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXFAULT_1)%8); + break; + case MODULE_TXFAULT_13 ... MODULE_TXFAULT_48: + reg = 0x14 + (attr->index-MODULE_TXFAULT_13)/8; + mask = 0x1 << ((attr->index - MODULE_TXFAULT_13)%8); + break; + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_12: + reg = 0x1b + (attr->index - MODULE_TXDISABLE_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_1)%8); + break; + case MODULE_TXDISABLE_13 ... MODULE_TXDISABLE_48: + reg = 0x19 + (attr->index-MODULE_TXDISABLE_13)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_13)%8); + break; + case MODULE_RXLOS_1 ... MODULE_RXLOS_12: + reg = 0x15 + (attr->index - MODULE_RXLOS_1)/8; + mask = 0x1 << ((attr->index - MODULE_RXLOS_1)%8); + break; + case MODULE_RXLOS_13 ... MODULE_RXLOS_48: + reg = 0xa + (attr->index-MODULE_RXLOS_13)/8; + mask = 0x1 << ((attr->index - MODULE_RXLOS_13)%8); + break; + case MODULE_RESET_49 ... MODULE_RESET_56: + reg = 0x20 ; + mask = 0x1 << ((attr->index - MODULE_RESET_49)%8); + revert = 1; + break; + case MODULE_LPMODE_49 ... MODULE_LPMODE_56: + reg = 0x21 ; + mask = 0x1 << ((attr->index - MODULE_LPMODE_49)%8); + revert = 0; + break; + default: + return 0; + } + + if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_56) { + revert = 1; + } + + mutex_lock(&data->update_lock); + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask)); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_qsfp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + long disable; + int status; + u8 reg = 0, mask = 0, revert = 0; + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_RESET_49 ... MODULE_RESET_56: + reg = 0x20; + mask = 0x1 << ((attr->index - MODULE_RESET_49)%8); + revert = 1; + break; + case MODULE_LPMODE_49 ... MODULE_LPMODE_56: + reg = 0x21; + mask = 0x1 << ((attr->index - MODULE_LPMODE_49)%8); + revert = 0; + break; + default: + return 0; + } + + disable = revert ? disable : !disable; + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update status */ + if (disable) { + status &= ~mask; + } + else { + status |= mask; + } + + status = as7327_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + long disable; + int status; + u8 reg = 0, mask = 0; + + status = kstrtol(buf, 10, &disable); + if (status) { + return status; + } + + switch (attr->index) { + case MODULE_TXDISABLE_1 ... MODULE_TXDISABLE_12: + reg = 0x1b + (attr->index - MODULE_TXDISABLE_1)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_1)%8); + break; + case MODULE_TXDISABLE_13 ... MODULE_TXDISABLE_48: + reg = 0x19 + (attr->index - MODULE_TXDISABLE_13)/8; + mask = 0x1 << ((attr->index - MODULE_TXDISABLE_13)%8); + break; + default: + return 0; + } + + /* Read current status */ + mutex_lock(&data->update_lock); + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + goto exit; + } + + /* Update tx_disable status */ + if (disable) { + status |= mask; + } + else { + status &= ~mask; + } + + status = as7327_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + goto exit; + } + + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t access(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status; + u32 addr, val; + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + + if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) { + return -EINVAL; + } + + if (addr > 0xFF || val > 0xFF) { + return -EINVAL; + } + + mutex_lock(&data->update_lock); + status = as7327_56x_cpld_write_internal(client, addr, val); + if (unlikely(status < 0)) { + goto exit; + } + mutex_unlock(&data->update_lock); + return count; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static void as7327_56x_cpld_add_client(struct i2c_client *client) +{ + struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL); + + if (!node) { + dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr); + return; + } + + node->client = client; + + mutex_lock(&list_lock); + list_add(&node->list, &cpld_client_list); + mutex_unlock(&list_lock); +} + +static void as7327_56x_cpld_remove_client(struct i2c_client *client) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int found = 0; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client == client) { + found = 1; + break; + } + } + + if (found) { + list_del(list_node); + kfree(cpld_node); + } + + mutex_unlock(&list_lock); +} + +static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int major = 0, minor = 0; + struct i2c_client *client = to_i2c_client(dev); + + major = i2c_smbus_read_byte_data(client, 0x0); + + if (major < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x0) err %d\n", client->addr, major); + } + + minor = i2c_smbus_read_byte_data(client, 0x1); + + if (minor < 0) { + dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, minor); + } + + return sprintf(buf, "%x.%x", major, minor); +} + +static struct as7327_56x_cpld_data *as7327_56x_fan_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting as7327_56x_fan update\n"); + data->valid = 0; + + /* Update fan data + */ + for (i = 0; i < ARRAY_SIZE(data->reg_fan_val); i++) { + int status = as7327_56x_cpld_read_internal(client, fan_reg[i]); + if (status < 0) { + data->valid = 0; + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "reg 0x%x, err %d\n", fan_reg[i], status); + return data; + } + else { + data->reg_fan_val[i] = status & 0xff; + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* fan utility functions */ +static u32 reg_val_to_duty_cycle(u8 reg_val) +{ + int val; + val = reg_val; + + return (val + 2) * 100 / 256; +} + +static u8 duty_cycle_to_reg_val(u8 duty_cycle) +{ + int val; + val = duty_cycle; + if(val < 30) + val = 30; + return (val * 255 / 100); +} + +static u32 reg_val_to_speed_rpm(u8 reg_val) +{ + return (u32)reg_val * FAN_TECK_SPEED_CNT; +} + +static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int error, value; + struct i2c_client *client = to_i2c_client(dev); + error = kstrtoint(buf, 10, &value); + if (error) + return error; + if (value < 0 || value > FAN_MAX_DUTY_CYCLE) + return -EINVAL; + + /* Disable FAN watch dog first.*/ + /* as7327_56x_cpld_write_internal(client, FAN_WATCHDOG_EN_REG, 0); + msleep(2000); + */ + as7327_56x_cpld_write_internal(client, fan_reg[1], duty_cycle_to_reg_val(value)); + as7327_56x_cpld_write_internal(client, fan_reg[2], duty_cycle_to_reg_val(value)); + as7327_56x_cpld_write_internal(client, fan_reg[3], duty_cycle_to_reg_val(value)); + as7327_56x_cpld_write_internal(client, fan_reg[4], duty_cycle_to_reg_val(value)); + + return count; +} + +static ssize_t set_fan_wdt_status(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status, value; + struct i2c_client *client = to_i2c_client(dev); + int reg = 0x2e; + + status = kstrtoint(buf, 10, &value); + if (status) { + return status; + } + if (value < 0 || value > 1) { + return -EINVAL; + } + + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + return status; + } + + if (value) { + status |= value; + } + else { + status = 0; + } + + status = as7327_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + return status; + } + + return count; +} + +static ssize_t set_fan_wdt_clear(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status, value; + struct i2c_client *client = to_i2c_client(dev); + int reg = 0x2e; + + status = kstrtoint(buf, 10, &value); + if (status) { + return status; + } + if (value != 1) { + return -EINVAL; + } + + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + return status; + } + + status |= (value << 1); + + status = as7327_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + return status; + } + + return count; +} + +static ssize_t set_fan_wdt_count(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status, value; + struct i2c_client *client = to_i2c_client(dev); + int reg = 0x2f; + + status = kstrtoint(buf, 10, &value); + if (status) { + return status; + } + if (value < 0 || value > 0x3f) { + return -EINVAL; + } + + status = as7327_56x_cpld_write_internal(client, reg, value); + if (unlikely(status < 0)) { + return status; + } + + return count; +} + +static u8 reg_val_to_direction(u8 reg_val, enum fan_box_id id) +{ + u8 mask = (1 << (1-id)); + reg_val &= mask; + + return reg_val ? 1 : 0; +} + +static u8 reg_val_to_is_present(u8 reg_val, enum fan_box_id id) +{ + u8 mask = (1 << (7-id)); + reg_val &= mask; + + return reg_val ? 0 : 1; +} + +static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf) +{ + u32 duty_cycle; + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7327_56x_cpld_data *data = as7327_56x_fan_update_device(dev); + ssize_t ret = 0; + + if (data->valid) { + switch (attr->index) + { + case FAN_PRESENT_1: + case FAN_PRESENT_2: + ret = sprintf(buf, "%d\n", reg_val_to_is_present(data->reg_fan_val[0], attr->index - FAN_PRESENT_1)); + break; + case FAN_DIRECTION_1: + case FAN_DIRECTION_2: + ret = sprintf(buf, "%d\n", reg_val_to_direction(data->reg_fan_val[0], attr->index - FAN_DIRECTION_1)); + break; + case FAN_DUTY_CYCLE_PERCENTAGE: + duty_cycle = reg_val_to_duty_cycle(data->reg_fan_val[1]); + ret = sprintf(buf, "%u\n", duty_cycle); + break; + case FAN_FRONT_SPEED_RPM_1: + case FAN_FRONT_SPEED_RPM_2: + case FAN_FRONT_SPEED_RPM_3: + case FAN_FRONT_SPEED_RPM_4: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_fan_val[attr->index - FAN_FRONT_SPEED_RPM_1 + 5])); + break; + case FAN_REAR_SPEED_RPM_1: + case FAN_REAR_SPEED_RPM_2: + case FAN_REAR_SPEED_RPM_3: + case FAN_REAR_SPEED_RPM_4: + ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_fan_val[attr->index - FAN_REAR_SPEED_RPM_1 + 9])); + break; + case FAN_WDT_ENABLE: + ret = sprintf(buf, "%d\n", (data->reg_fan_val[13] & 0x1)); + break; + case FAN_WDT_CLEAR: + ret = sprintf(buf, "%d\n", ((data->reg_fan_val[13] & 0x2) >> 1)); + break; + case FAN_WDT_COUNT: + ret = sprintf(buf, "%d\n", (data->reg_fan_val[14] & 0x3f)); + break; + default: + break; + } + } + return ret; +} + +static ssize_t sysled_wdt_show_value(struct device *dev, struct device_attribute *da, char *buf) +{ + return sprintf(buf, "%d\n", 0); +} + +static ssize_t set_sysled_wdt_clear(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int status, value; + struct i2c_client *client = to_i2c_client(dev); + int reg = 0x85; + + status = kstrtoint(buf, 10, &value); + if (status) { + return status; + } + if (value != 1) { + return -EINVAL; + } + + status = as7327_56x_cpld_read_internal(client, reg); + if (unlikely(status < 0)) { + return status; + } + + status |= 0x8; + + status = as7327_56x_cpld_write_internal(client, reg, status); + if (unlikely(status < 0)) { + return status; + } + + return count; +} + +static ssize_t show_bmc_enable(struct device *dev, struct device_attribute *da, char *buf) +{ + int status; + int bmc_enable = -1; + int bmc_present = -1; + int bmc_heart = -1; + struct i2c_client *client = to_i2c_client(dev); + + status = as7327_56x_cpld_read_internal(client, 0x82); + if(unlikely(status < 0)) + return status; + + bmc_enable = (status >> BMC_ENABLE_OFFSET) & BMC_ENABLE_MASK; + + status = as7327_56x_cpld_read_internal(client, 0x80); + if(unlikely(status < 0)) + return status; + + bmc_present = (status >> BMC_PRESENT_OFFSET) & BMC_PRESENT_MASK; + bmc_heart = (status >> BMC_HEART_OFFSET) & BMC_HEART_MASK; + + if((bmc_enable == BMC_EN_ENABLE) && (bmc_present == BMC_PRESENT) && (bmc_heart == BMC_HEART_FRQ_2HZ)) + { + return sprintf(buf, "%d\n", 1); + } + else + { + return sprintf(buf, "%d\n", 0); + } + + return -1; +} + +/* + * I2C init/probing/exit functions + */ +static int as7327_56x_cpld_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct as7327_56x_cpld_data *data; + int ret = -ENODEV; + const struct attribute_group *group = NULL; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto exit; + + data = kzalloc(sizeof(struct as7327_56x_cpld_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->type = id->driver_data; + + /* Register sysfs hooks */ + switch (data->type) { + case as7327_56x_cpld1: + group = &as7327_56x_cpld1_group; + break; + case as7327_56x_cpld2: + group = &as7327_56x_cpld2_group; + break; + default: + break; + } + + if (group) { + ret = sysfs_create_group(&client->dev.kobj, group); + if (ret) { + goto exit_free; + } + } + + as7327_56x_cpld_add_client(client); + return 0; + +exit_free: + kfree(data); +exit: + return ret; +} + +static void as7327_56x_cpld_remove(struct i2c_client *client) +{ + struct as7327_56x_cpld_data *data = i2c_get_clientdata(client); + const struct attribute_group *group = NULL; + + as7327_56x_cpld_remove_client(client); + + /* Remove sysfs hooks */ + switch (data->type) { + case as7327_56x_cpld1: + group = &as7327_56x_cpld1_group; + break; + case as7327_56x_cpld2: + group = &as7327_56x_cpld2_group; + break; + default: + break; + } + + if (group) { + sysfs_remove_group(&client->dev.kobj, group); + } + + kfree(data); +} + +static int as7327_56x_cpld_read_internal(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7327_56x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_write_byte_data(client, reg, value); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +int as7327_56x_cpld_read(unsigned short cpld_addr, u8 reg) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EPERM; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7327_56x_cpld_read_internal(cpld_node->client, reg); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7327_56x_cpld_read); + +int as7327_56x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value) +{ + struct list_head *list_node = NULL; + struct cpld_client_node *cpld_node = NULL; + int ret = -EIO; + + mutex_lock(&list_lock); + + list_for_each(list_node, &cpld_client_list) + { + cpld_node = list_entry(list_node, struct cpld_client_node, list); + + if (cpld_node->client->addr == cpld_addr) { + ret = as7327_56x_cpld_write_internal(cpld_node->client, reg, value); + break; + } + } + + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(as7327_56x_cpld_write); + +static struct i2c_driver as7327_56x_cpld_driver = { + .driver = { + .name = "as7327_56x_cpld", + .owner = THIS_MODULE, + }, + .probe = as7327_56x_cpld_probe, + .remove = as7327_56x_cpld_remove, + .id_table = as7327_56x_cpld_id, +}; + +static int __init as7327_56x_cpld_init(void) +{ + mutex_init(&list_lock); + return i2c_add_driver(&as7327_56x_cpld_driver); +} + +static void __exit as7327_56x_cpld_exit(void) +{ + i2c_del_driver(&as7327_56x_cpld_driver); +} + +MODULE_AUTHOR("Brandon Cheng "); +MODULE_DESCRIPTION("as7327_56x I2C CPLD driver"); +MODULE_LICENSE("GPL"); + +module_init(as7327_56x_cpld_init); +module_exit(as7327_56x_cpld_exit); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-fpga.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-fpga.c new file mode 100644 index 000000000..954b5328d --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-fpga.c @@ -0,0 +1,1493 @@ +#include /* + => module_init() + => module_exit() + => MODULE_LICENSE() + => MODULE_VERSION() + => MODULE_AUTHOR() + => struct module + */ +#include /* + => typedef int (*initcall_t)(void); + Note: the 'initcall_t' function returns 0 when succeed. + In the Linux kernel, error codes are negative numbers + belonging to the set defined in . + => typedef void (*exitcall_t)(void); + => __init + => __exit + */ +#include /* + => moduleparam() + */ +#include /* + => dev_t (u32) + */ +#include /* + => MAJOR() + => MINOR() + */ +#include /* + => register_chrdev_region() + => unregister_chrdev_region() + => alloc_chrdev_region() + => struct file_operations + => struct file + => struct inode + => unsigned int imajor() + => unsigned int iminor() + */ +#include /* + => struct cdev + */ +#include /* + => void *memset() + */ +#include /* + => void kfree() + */ +#include /* + => class_create() + => device_create() + */ +#include +#include +#include /* + => void cdev_init() + */ +#include /* + => access_ok() + */ +#include /* + => current + */ +#include /* + => struct semaphore + */ +#include /* + => struct wait_queue_head_t + => init_waitqueue_head() + */ +#include /* + => O_NONBLOCK + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define I2C_DEBUG(...) +/*#define I2C_DEBUG(...) printk(KERN_ALERT __VA_ARGS__)*/ +#define ERROR_DEBUG(...) printk(KERN_ALERT __VA_ARGS__) + +#ifndef ACCESS_ONCE +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#endif + +#define FPGA_DRIVER_VERSION "0.0.f" + +MODULE_LICENSE("GPL"); +MODULE_VERSION(FPGA_DRIVER_VERSION); +MODULE_AUTHOR("Will Chen, Junhao He"); + +#define FPGA_DEV_NAME "fpga" +#define FPGA_CLASS_NAME "fpga_class" +#define FPGA_DRIVER_NAME "fpgaio" +#define FPGA_REGION_NAME "fpga_register" +#define FPGA_SMBUS_NAME "Accton_FPGA_SMBus_" +#define BAR0 0 + +#if 1 +/*Please copy the following definition into the c files if you want to use fpga_access*/ +#define MAX_BUFF_SIZE 128 +#define MAX_EEPROM_SIZE 256 + +/* file_operations: ioctl */ +struct fpga_access { + /* input */ + unsigned char addr; /* 0x0 - 0x7F */ + unsigned char offset; + unsigned char length; + unsigned char channel; + /* intput output buff */ + unsigned char buff[MAX_BUFF_SIZE]; +}; + +#define FPGA_MAGIC 0xA7 +#define FPGA_IO_CMD 0x10 +#define FPGA_SPI_IO 0x11 +#define FPGA_LOCK_CMD 0xEE +#define FPGA_UNLOCK_CMD 0xDD + +#define FPGA_READ _IOR(FPGA_MAGIC, FPGA_IO_CMD, struct fpga_access) +#define FPGA_WRITE _IOW(FPGA_MAGIC, FPGA_IO_CMD, struct fpga_access) +#define FPGA_LOCK _IOW(FPGA_MAGIC, FPGA_LOCK_CMD, struct fpga_access) +#define FPGA_UNLOCK _IOW(FPGA_MAGIC, FPGA_UNLOCK_CMD, struct fpga_access) +#define FPGA_SPI_READ _IOR(FPGA_MAGIC, FPGA_SPI_IO, struct fpga_access) +#define FPGA_SPI_WRITE _IOW(FPGA_MAGIC, FPGA_SPI_IO, struct fpga_access) +/*Please copy the above definition into the c files if you want to use fpga_access*/ +#endif + +/* FPGA Bus control parameters */ +static unsigned int max_retry_time = 20; +static unsigned int delay_ack = 50; +static unsigned int delay_tip = 26; +static unsigned int delay_busy = 10; +static unsigned int fpga_dev_count = 1; +struct spinlock fpga_pcie_lock; +static bool fpga_initialized = false; + +module_param(max_retry_time, uint, S_IRUGO); +MODULE_PARM_DESC(max_retry_time, "Max retry times to wait TIP/ACK (20 by default)"); +module_param(delay_ack, uint, S_IRUGO); +MODULE_PARM_DESC(delay_ack, "Delay time before the next time to check ACK bit (50 us by default)"); +module_param(delay_tip, uint, S_IRUGO); +MODULE_PARM_DESC(delay_tip, "Delay time before the next time to check TIP bit (26 us by default)"); +module_param(delay_busy, uint, S_IRUGO); +MODULE_PARM_DESC(delay_busy, "Delay time before the next time to check BUSY bit (10 us by default)"); + +#define MAX_CHANNEL 60 +#define FPGA_SLEEP(X) usleep_range((X*8/10), X) +#define I2C_PRESCALE_LOW_OFFSET 0x0400 // PRESCALE registers, PRERlo +#define I2C_PRESCALE_HIGH_OFFSET 0x0404 // PRESCALE registers, PRERhi +#define I2C_CONTROLLER_OFFSET 0x0408 // Control Register, CTR +#define I2C_TRANSMIT_RECEIVE_OFFSET 0x040C // Transmit Register, TXR +#define I2C_COMMAND_STATUS_OFFSET 0x0410 // Command Register CR/Status Registesr, SR + +#define PRER_LO_DEFAULT 0x007f +#define PRER_HI_DEFAULT 0x0000 +#define CONTROL_DISABLE 0x0000 +#define CONTROL_ENABLE 0x0080 +#define COMMAND_START_WRITE 0x0090 +#define COMMAND_ENABLE_WRITE 0x0010 +#define COMMAND_READ 0x0020 +#define COMMAND_ACK_STOP_READ 0x0068 +#define COMMAND_STOP 0x0040 +#define COMMAND_STOP_WRITE 0x0050 +#define BUSY_BIT 0x40 +#define TIP_BIT 0x2 +#define ACK_BIT 0x80 +#define SET_TXR_WRITE(x) (x &= ~(1UL)) +#define SET_TXR_READ(x) (x |= (1UL)) +#define MODULE_ATTR_NUM 4 + +struct fpga_pci_device { + char *name; + dev_t fpga_dev_number; + struct class *fpga_class; + struct device *fpga_udev; + struct cdev fpga_cdev; + resource_size_t start; + resource_size_t len; + void *fpga_base; + int irq_vec_count; + unsigned int irq; + int pci_enabled; + int pci_region_requested; + int cdev_added; + struct semaphore sem[MAX_CHANNEL]; + /*struct spinlock fpga_pcie_lock;*/ + struct i2c_adapter adapter[MAX_CHANNEL]; +}; + +#define FPGA_ACCESS(cmd) \ + do { \ + unsigned long flags; \ + spin_lock_irqsave(&(fpga_pcie_lock), flags); \ + cmd \ + spin_unlock_irqrestore(&(fpga_pcie_lock), flags); \ + }while(0); + +/* 10ee:7021 Maybe need modify*/ +static struct pci_device_id fpga_id_table[] = { \ + { PCI_DEVICE(0x10ee, 0x7021) }, + {0,}, +}; + +struct fpga_base_data { + void *fpga_base; + struct mutex update_lock; + +}; +struct fpga_base_data fpga_data; + +int fpga_pcie_write( u32 reg, u32 value) +{ + int status = 0 ; + if(fpga_initialized) + { + FPGA_ACCESS(*((unsigned int *) (fpga_data.fpga_base + reg)) = value;) + I2C_DEBUG("fpga_write_internal 0x%x reg 0x%x\n",value,reg); + } + else + { + status=-ENXIO; + I2C_DEBUG("FPGA PCIE is uninitialize!\n"); + } + return status; +} +EXPORT_SYMBOL(fpga_pcie_write); + +int fpga_pcie_read( u32 reg) +{ + int status = 0; + if(fpga_initialized) + { + FPGA_ACCESS(status = ACCESS_ONCE(*((unsigned int *)(fpga_data.fpga_base + reg)));) + I2C_DEBUG("fpga_read_internal 0x%x reg 0x%x\n",status,reg); + } + else + { + status=-ENXIO; + I2C_DEBUG("FPGA PCIE is uninitialize!\n"); + } + return status; +} +EXPORT_SYMBOL(fpga_pcie_read); + + +static int fpga_smbus_check_busy(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_busy; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + FPGA_SLEEP(transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + I2C_DEBUG("[check_busy] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & BUSY_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int fpga_smbus_check_tip(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int transaction_delay = delay_tip; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + FPGA_SLEEP(transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + I2C_DEBUG("[check_tip] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & TIP_BIT) == 0) + { + return 0; + } + } + + return -1; +} + +static int fpga_smbus_check_ack(struct fpga_pci_device *fpga_pci_dev, unsigned int i2c_cmd_stat_addr, unsigned int *i2c_stat) +{ + unsigned int retry_times = 0; + unsigned int ack_transaction_delay = delay_ack; + + for(retry_times = 0; retry_times < max_retry_time; retry_times++) + { + I2C_DEBUG("[check_ack] i2c_stat:%x\n", *i2c_stat); + if((*i2c_stat & ACK_BIT) == 0) + { + return 0; + } + FPGA_SLEEP(ack_transaction_delay); + FPGA_ACCESS(*i2c_stat = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)));) + } + + if(*i2c_stat & ACK_BIT) + { + return -1; + } + + return 0; +} + +int fpga_smbus_read(struct fpga_pci_device *fpga_pci_dev, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_cmd_stat_addr = 0; + unsigned int i2c_txr_data = 0, i2c_cmd_stat_data = 0; + unsigned int i2c_stat = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + unsigned char buff[MAX_BUFF_SIZE]; + if(data == NULL) + { + return(-EINVAL); + } + + //I2C_DEBUG("[%s] READ: %x \n", __func__, data->byte); + + if (addr > 0x7F || channel >= MAX_CHANNEL) + { + return(-EINVAL); + } + udelay(5); + + retval = down_interruptible(&(fpga_pci_dev->sem[channel])); + if (retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_RECEIVE_OFFSET + channel*0x20; + i2c_cmd_stat_addr = I2C_COMMAND_STATUS_OFFSET + channel*0x20; + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TRX with the slave memory address, where the data is to be read from. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TRX with a value of Slave address + READ bit. + 8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device. + 11. Check the TIP bit of SR, to make sure the command is done. + 12. Repeat steps 10 and 11 to continue to read data from the slave device. + 13. When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK. + Check the TIP bit of SR, to make sure the command is done. + */ + + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[2] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto no_ack_response; + } + + /*4. Set TRX with the slave memory address, where the data is to be read from.*/ + i2c_txr_data = offset; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[5] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*6. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto no_ack_response; + } + + /*7. Set TRX with a value of Slave address + READ bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_READ(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /*8. Set CR with the 8'h90 to enable the START (repeated START in this case) and WRITE the value in TXR to the slave device.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[8] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*9. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto no_ack_response; + } + + /*12. Repeat steps 10 and 11 to continue to read data from the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 1) + { + /*10. Set CR with 8'h20 to issue a READ command and then an ACK command. This enables the reading of data from the slave device.*/ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[10] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*11. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 11; + goto device_busy; + } + + /*Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 11; + goto no_ack_response; + } + + /*Get TXR with 8-bit data from the slave device.*/ + FPGA_ACCESS(i2c_txr_data = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_txr_addr)));) + buff[temp_offset] = i2c_txr_data; + I2C_DEBUG("[11-2] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + byte_to_rw--; + temp_offset++; + } + + /*13.When the Master is ready to stop reading from the Slave, set CR to 8'h28. This will read the last byte of data and then issue a NACK.*/ + i2c_cmd_stat_data = COMMAND_ACK_STOP_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[13-1] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /*Check the BUSY bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 13; + goto device_busy; + } + + if(byte_to_rw) + { + /*Get the last byte from TXR with 8-bit data from the slave device.*/ + FPGA_ACCESS(i2c_txr_data = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + i2c_txr_addr)));) + buff[temp_offset] = i2c_txr_data; + I2C_DEBUG("[13-3] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + } + + memcpy(data, buff, length); + + up(&(fpga_pci_dev->sem[channel])); + return(0); + +device_busy: + I2C_DEBUG(KERN_NOTICE "device_busy, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat); + + /*Re-initailize BUS.*/ + /*0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_DISABLE;) + /*1. Program the clock PRESCALE registers, PRERlo and PRERhi, with the desired value. + This value is determined by the clock frequency and the speed of the I2C bus.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_LOW_OFFSET + channel*0x20)) = PRER_LO_DEFAULT;) + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_HIGH_OFFSET + channel*0x20)) = PRER_HI_DEFAULT;) + /*2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_ENABLE;) + FPGA_SLEEP(500); + + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "device_busy read, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + I2C_DEBUG(KERN_NOTICE "device_busy, fail to issue STOP, i2c_stat:0x%x.\n", i2c_stat); + } + + I2C_DEBUG(KERN_NOTICE "device_busy, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat); + up(&(fpga_pci_dev->sem[channel])); + return (-EBUSY); + +no_ack_response: + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "no_ack_response read, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + I2C_DEBUG(KERN_NOTICE "no_ack_response, read failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat); + + /*Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 14; + goto device_busy; + } + + up(&(fpga_pci_dev->sem[channel])); + return (-ETIMEDOUT); +} + +int fpga_smbus_write(struct fpga_pci_device *fpga_pci_dev, + unsigned char channel, unsigned char addr, unsigned char offset, unsigned char length, + unsigned char *data) +{ + int retval = 0; + int stop_step = 0; + unsigned int i2c_txr_addr = 0, i2c_cmd_stat_addr = 0; + unsigned int i2c_txr_data = 0, i2c_cmd_stat_data = 0; + unsigned int i2c_stat = 0; + unsigned char byte_to_rw = 0, temp_offset = 0; + unsigned char buff[MAX_BUFF_SIZE]; + if(data == NULL) + { + return(-EINVAL); + } + + I2C_DEBUG("[%s] WRITE\n", __func__); + + if (addr > 0x7F || channel >= MAX_CHANNEL) + { + return(-EINVAL); + } + udelay(5); + retval = down_interruptible(&(fpga_pci_dev->sem[channel])); + if (retval) + { + return(-ERESTARTSYS); + } + + i2c_txr_addr = I2C_TRANSMIT_RECEIVE_OFFSET + channel*0x20; + i2c_cmd_stat_addr = I2C_COMMAND_STATUS_OFFSET + channel*0x20; + + memcpy(buff, data, length); + + /* + 1. Set the Transmit Register TXR with a value of Slave address + Write bit. + 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus. + 3. Check the Transfer In Progress (TIP) bit of the Status Register, SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 4. Set TXR with a slave memory address for the data to be written to. + 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address. + 6. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 7. Set TXR with 8-bit data for the slave device. + 8. Set CR to 8'h10 to enable a WRITE to send data. + 9. Check the TIP bit of SR, to make sure the command is done. + Check the Received Acknowledge (RxACK) bit of the Status Register, SR, to make sure I2C slave has ACK. + 10. Repeat steps 7 to 9 to continue to send data to the slave device. + 11. Set CR to 8'h40 to then issue a STOP command. + Check the TIP bit of SR, to make sure the command is done. + */ + + /* 1. Set the Transmit Register TXR with a value of Slave address + Write bit.*/ + i2c_txr_data = (addr) << 1; + SET_TXR_WRITE(i2c_txr_data); + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[1] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 2. Set the Command Register CR to 8'h90 to enable the START and WRITE. This starts the transmission on the I2C bus.*/ + i2c_cmd_stat_data = COMMAND_START_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[2] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 3. Check the Transfer In Progress (TIP) bit of the Status Registesr, SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 3; + goto no_ack_response; + } + + /* 4. Set TXR with a slave memory address for the data to be written to.*/ + i2c_txr_data = offset; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[4] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 5. Set CR with 8'h10 to enable a WRITE to send to the slave memory address.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[5] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 6. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 6; + goto no_ack_response; + } + + /* 10. Repeat steps 7 to 9 to continue to send data to the slave device.*/ + byte_to_rw = length; + temp_offset = 0; + while(byte_to_rw > 0) + { + /* 7. Set TXR with 8-bit data for the slave device.*/ + i2c_txr_data = buff[temp_offset]; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_txr_addr)) = i2c_txr_data;) + I2C_DEBUG("[7] i2c_txr_addr:%x i2c_txr_data:%x\n", i2c_txr_addr, i2c_txr_data); + + /* 8. Set CR to 8'h10 to enable a WRITE to send data.*/ + i2c_cmd_stat_data = COMMAND_ENABLE_WRITE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[8] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* 9. Check the TIP bit of SR, to make sure the command is done.*/ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto device_busy; + } + + /* Check the ACK bit of the Status Registesr, SR, to make sure ACK is received.*/ + if(fpga_smbus_check_ack(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 9; + goto no_ack_response; + } + + byte_to_rw--; + temp_offset++; + } + + /* 11. Set CR to 8'h40 to issue a STOP command.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + I2C_DEBUG("[12] i2c_cmd_stat_addr:%x i2c_cmd_stat_data:%x\n", i2c_cmd_stat_addr, i2c_cmd_stat_data); + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 12; + goto device_busy; + } + + up(&(fpga_pci_dev->sem[channel])); + return(0); + +device_busy: + I2C_DEBUG( "device_busy, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat); + + /* Re-initailize BUS.*/ + /* 0. Disable the core by writing 8'h00 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_DISABLE;) + /* 1. Program the clock PRESCALE registers, PRERlo and PRERhi, with + the desired value. This value is determined by the clock frequency and the speed of the I2C bus.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_LOW_OFFSET + channel*0x20)) = PRER_LO_DEFAULT;) + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_PRESCALE_HIGH_OFFSET + channel*0x20)) = PRER_HI_DEFAULT;) + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + I2C_CONTROLLER_OFFSET + channel*0x20)) = CONTROL_ENABLE;) + FPGA_SLEEP(500); + + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "device_busy write, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /*Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + I2C_DEBUG( "device_busy, fail to issue STOP, i2c_stat:0x%x.\n", i2c_stat); + } + + up(&(fpga_pci_dev->sem[channel])); + return (-EBUSY); + +no_ack_response: + /* Set CR with 8'h20 to issue a READ command. This is going to issue a 9-clock command. */ + i2c_cmd_stat_data = COMMAND_READ; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + /* Check the TIP bit of SR, to make sure the command is done. */ + if(fpga_smbus_check_tip(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + printk(KERN_NOTICE "no_ack_response write, fail to check TIP, i2c_stat:0x%x.\n", i2c_stat); + } + + /* Set CR to 8'h40 to issue a STOP command to avoid error.*/ + i2c_cmd_stat_data = COMMAND_STOP; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_cmd_stat_addr)) = i2c_cmd_stat_data;) + + I2C_DEBUG( "no_ack_response, write failed, channel:%d addr:0x%x, length:0x%x, offset:0x%x, stop_step:%d, i2c_stat:0x%x.\n", channel, addr, length, offset, stop_step, i2c_stat); + + /* Check the BUSY bit of SR, to make sure STOP is sent.*/ + if(fpga_smbus_check_busy(fpga_pci_dev, i2c_cmd_stat_addr, &i2c_stat)) + { + stop_step = 13; + goto device_busy; + } + + up(&(fpga_pci_dev->sem[channel])); + return (-ETIMEDOUT); +} + +static u32 fpga_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK; +} + +/* Return negative errno on error. */ +static s32 fpga_smbus_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct fpga_pci_device *fpga_pci_dev; + int retval = 0; + unsigned char length = 0, channel = 0; + char cha_num_str[3] = {0}; + + I2C_DEBUG("[%s]name:%s addr:0x%x flags:0x%x read_write:0x%x command:0x%x size:0x%x\n", + __func__, adap->name, addr, flags, read_write, command, size); + + switch(size) + { + case I2C_SMBUS_BYTE: + length = 1; + break; + case I2C_SMBUS_BYTE_DATA: + length = 1; + break; + case I2C_SMBUS_WORD_DATA: + length = 2; + break; + case I2C_SMBUS_BLOCK_DATA: + length = data->block[0]; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + length = 32; + break; + case I2C_SMBUS_QUICK: + length = 0; + break; + default: + length = 1; + break; + } + + memcpy(cha_num_str, (adap->name+18), 2); + retval = kstrtou8(cha_num_str, 10, &channel); + if(retval == 0) + { + I2C_DEBUG("channel:%d \n", channel); + } + else + { + ERROR_DEBUG("Error:%d, channel:%s \n", retval, cha_num_str); + } + + fpga_pci_dev = i2c_get_adapdata(adap); + if(fpga_pci_dev == NULL) + { + return(-EINVAL); + } + + if(length > 32+2) + { + return(-EINVAL); + } + if(data == NULL) + { + return(-EINVAL); + } + + if(I2C_SMBUS_READ == read_write) + { + retval = fpga_smbus_read(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data); + } + else if(I2C_SMBUS_WRITE == read_write) + { + if(size==I2C_SMBUS_BLOCK_DATA) + retval = fpga_smbus_write(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data->block+1); + else + retval = fpga_smbus_write(fpga_pci_dev, channel, addr, command, length, (unsigned char *)data); + } + + return retval; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = fpga_smbus_access, + .functionality = fpga_smbus_func, +}; + +static pci_ers_result_t fpga_error_detected(struct pci_dev *pdev, pci_channel_state_t error) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t fpga_mmio_enabled(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t fpga_slot_reset(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static void fpga_resume(struct pci_dev *pdev) +{ + I2C_DEBUG( "[%s]\n", __func__); + return; +} + +static const struct pci_error_handlers fpga_error_handlers = { + .error_detected = fpga_error_detected, + .mmio_enabled = fpga_mmio_enabled, + .slot_reset = fpga_slot_reset, + .resume = fpga_resume, +}; + +static int fpga_open(struct inode *inode, struct file *filp) +{ + I2C_DEBUG( "[%s]\n", __func__); + filp->private_data = (void *) container_of(inode->i_cdev, struct fpga_pci_device, fpga_cdev); + + return(0); +} + +static int fpga_release(struct inode *inode, struct file *filp) +{ + I2C_DEBUG( "[%s]\n", __func__); + return(0); +} +struct fpga_reg { + int reg; + int data; +}; +ssize_t fpga_read(struct file *filp, char __user *user_data, size_t size, loff_t *offset) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + struct fpga_reg reg_date; + int retval = 0; + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + + I2C_DEBUG( "[%s] %s\n", __func__, fpga_pci_dev->name); + + retval = copy_from_user((void *) ®_date, (const void *) user_data, sizeof(struct fpga_reg)); + if (retval) + { + return(-EFAULT); + } + reg_date.data = fpga_pcie_read(reg_date.reg); + retval = copy_to_user((void *) user_data, (const void *) (®_date), sizeof(reg_date)); + if (retval) + { + return(-EFAULT); + } + + return(sizeof(reg_date)); +} + +ssize_t fpga_write(struct file *filp, const char __user *user_data, size_t size, loff_t *offset) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + struct fpga_reg reg_date; + int retval = 0; + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + + I2C_DEBUG( "[%s] %s\n", __func__, fpga_pci_dev->name); + + retval = copy_from_user((void *) ®_date, (const void *) user_data, sizeof(struct fpga_reg)); + if (retval) + { + return(-EFAULT); + } + fpga_pcie_write(reg_date.reg, reg_date.data); + + return(sizeof(reg_date)); +} + +long fpga_init_smbus(struct fpga_pci_device *fpga_pci_dev) +{ + unsigned int i2c_ctrl_addr = 0; + unsigned int i2c_ctrl_data = 0; + unsigned int i2c_pre_low_addr = 0, i2c_pre_high_addr = 0; + unsigned int i2c_pre_low_data = 0, i2c_pre_high_data = 0; + int temp_channel = 0; + + for(temp_channel = 0; temp_channelfpga_base + i2c_pre_low_addr)) = i2c_pre_low_data;) + I2C_DEBUG( "[pre1-1] i2c_pre_low_addr:%x %x\n", i2c_pre_low_addr, i2c_pre_low_addr); + + i2c_pre_high_data = PRER_HI_DEFAULT; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_pre_high_addr)) = i2c_pre_high_data;) + I2C_DEBUG( "[pre1-2] i2c_pre_high_addr:%x %x\n", i2c_pre_high_addr, i2c_pre_high_addr); + + /* 2. Enable the core by writing 8'h80 to the Control Register, CTR.*/ + i2c_ctrl_data= CONTROL_ENABLE; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + i2c_ctrl_addr)) = i2c_ctrl_data;) + I2C_DEBUG( "[pre1] i2c_ctrl_addr:%x %x\n", i2c_ctrl_addr, i2c_ctrl_addr); + } + + return 0; +} + +long fpga_mask_all_intr(struct fpga_pci_device *fpga_pci_dev) +{ + unsigned int SI5395_INT; + /* 1. Mask all I2C Module Interrupt*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x5c)) = 0x01FFFFF;) + + /* 2. Mask CDR Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x44)) = 0x00001FFF;) + + /* 3. Mask TEMP Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x48)) = 0x00000003;) + + /* 4. Mask QSFP Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x4c)) = 0x0000FFFF;) + + /* 5. Mask QDD Interrupt Mask*/ + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x50)) = 0x0000000F;) + + /* 6. Mask SI5395_INT_mask Interrupt Mask*/ + FPGA_ACCESS(SI5395_INT = ACCESS_ONCE(*((unsigned int *)(fpga_pci_dev->fpga_base + 0x34)));) + SI5395_INT |= 0x00110000; + FPGA_ACCESS(*((unsigned int *) (fpga_pci_dev->fpga_base + 0x34)) = SI5395_INT;) + + return 0; +} + +long fpga_ioctl(struct file *filp, unsigned int cmd, unsigned long user_addr) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + struct fpga_access acc = {0}; + int retval = 0; + unsigned short spi_offset = 0; + + if (cmd == FPGA_READ) + { + I2C_DEBUG( "[%s] READ\n", __func__); + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + + if (!access_ok((const void __user *)user_addr, _IOC_SIZE(cmd))) + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (acc.length + acc.offset > MAX_EEPROM_SIZE) + { + return(-EINVAL); + } + + retval = fpga_smbus_read(fpga_pci_dev, acc.channel, acc.addr, acc.offset, acc.length, acc.buff); + if(retval != 0) + { + return retval; + } + + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + return(0); + } + else if (cmd == FPGA_WRITE) + { + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + + if (!access_ok((const void __user *)user_addr, _IOC_SIZE(cmd))) + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (acc.length + acc.offset > MAX_EEPROM_SIZE) + { + return(-EINVAL); + } + + retval = fpga_smbus_write(fpga_pci_dev, acc.channel, acc.addr, acc.offset, acc.length, acc.buff); + if(retval != 0) + { + return retval; + } + + return(0); + } + else if (cmd == FPGA_LOCK) + { + return(0); + } + else if (cmd == FPGA_UNLOCK) + { + return(0); + } + else if (cmd == FPGA_SPI_READ || cmd == FPGA_SPI_WRITE) + { + fpga_pci_dev = (struct fpga_pci_device *) filp->private_data; + + if (!access_ok((const void __user *)user_addr, _IOC_SIZE(cmd))) + { + I2C_DEBUG( "[%s] Access NOT OK\n", __func__); + return(-EINVAL); + } + + retval = copy_from_user((void *) &acc, (const void *) user_addr, sizeof(struct fpga_access)); + if (retval) + { + return(-EFAULT); + } + + if (retval) + { + return(-ERESTARTSYS); + } + spi_offset = (acc.buff[0]<<8) + acc.buff[1]; + if (cmd == FPGA_SPI_READ) + { + FPGA_ACCESS(acc.buff[2] = ACCESS_ONCE(*((unsigned char *)(fpga_pci_dev->fpga_base + spi_offset)));) + } + else if (cmd == FPGA_SPI_WRITE) + { + FPGA_ACCESS(*((unsigned char *) (fpga_pci_dev->fpga_base + spi_offset)) = acc.buff[2];) + } + retval = copy_to_user((void *) user_addr, (const void *) &acc, sizeof(struct fpga_access)); + + return(0); + } + else + { + ERROR_DEBUG( "[%s] Unknown command\n", __func__); + return(-EINVAL); + } +} + +#ifdef INTERRUPT_SUPPORT +static irqreturn_t fpga_intr_msi(int irq, void *data) +{ + static int count=0; + + disable_irq_nosync(irq); + + enable_irq(irq); + + I2C_DEBUG( "MSI irq:%d, count=%d\n", irq, count); + count++; + + return IRQ_HANDLED; +} +#endif + +static struct file_operations fpga_fops = { + .owner = THIS_MODULE, + .open = fpga_open, + .read = fpga_read, + .write = fpga_write, + .unlocked_ioctl = fpga_ioctl, + .release = fpga_release, +}; + + +static ssize_t show_fpga_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int val = 0; + + val = fpga_pcie_read(0x00); + if (val < 0) { + ERROR_DEBUG( "[%s] Fail to read fpga version\n", __func__); + } + + return sprintf(buf, "%x.%x\n", (val>>24)&0xff, (val>>16)&0xff); +} + +static struct device_attribute ver = __ATTR(version, 0600, show_fpga_version, NULL); + + +static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int retval = 0; + struct fpga_pci_device *fpga_pci_dev = NULL; + int smbus_temp_num,smbus_adapter_num = 0; + + fpga_pci_dev = (struct fpga_pci_device *) kzalloc(sizeof(struct fpga_pci_device), GFP_KERNEL); + if (!fpga_pci_dev) + { + ERROR_DEBUG( "[%s] Fail to alloc fpga_pci_dev memory\n", __func__); + retval = -ENOMEM; + goto fail_to_alloc_fpga_pci_dev; + } + + pci_set_drvdata(pdev, (void *) fpga_pci_dev); + + fpga_pci_dev->name = "Accton FPGA"; + + retval = alloc_chrdev_region(&(fpga_pci_dev->fpga_dev_number), 0, fpga_dev_count, FPGA_DEV_NAME); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to alloc fpga_dev_number %d\n", __func__, fpga_pci_dev->fpga_dev_number); + goto fail_to_alloc_dev_number; + } + + fpga_pci_dev->fpga_class = class_create(FPGA_CLASS_NAME); + if (IS_ERR(fpga_pci_dev->fpga_class)) + { + ERROR_DEBUG( "[%s] Fail to create %s [fpga_class: %p]\n", __func__, FPGA_CLASS_NAME, fpga_pci_dev->fpga_class); + retval = PTR_ERR(fpga_pci_dev->fpga_class); + goto fail_to_create_fpga_class; + } + I2C_DEBUG( "[%s] fpga_class created\n", __func__); + + fpga_pci_dev->fpga_udev = device_create(fpga_pci_dev->fpga_class, NULL, fpga_pci_dev->fpga_dev_number, NULL, FPGA_DEV_NAME); + + if (IS_ERR(fpga_pci_dev->fpga_udev)) + { + ERROR_DEBUG( "[%s] Fail to create fpga_udev\n", __func__); + retval = PTR_ERR(fpga_pci_dev->fpga_udev); + goto fail_to_create_fpga_udev; + } + I2C_DEBUG( "[%s] fpga_udev created\n", __func__); + + if (sysfs_create_file(&fpga_pci_dev->fpga_udev->kobj, &ver.attr)) + { + goto fail_to_enable_pci_device; + } + I2C_DEBUG( "[%s] fpga version sysfs created\n", __func__); + + retval = pci_enable_device(pdev); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to enable PCIe device\n", __func__); + goto fail_to_enable_pci_device; + } + fpga_pci_dev->pci_enabled = 1; + + fpga_pci_dev->start = pci_resource_start(pdev, BAR0); + I2C_DEBUG( "[%s] Get PCI resource address 0x%llx\n", __func__, fpga_pci_dev->start); + + fpga_pci_dev->len = pci_resource_len(pdev, BAR0); + I2C_DEBUG( "[%s] Get PCI resource length %llu\n", __func__, fpga_pci_dev->len); + + retval = pci_request_region(pdev, BAR0, FPGA_REGION_NAME); + if (retval) + { + ERROR_DEBUG( "[%s] Fail to request PCI resource region\n", __func__); + goto fail_to_request_pci_region; + } + fpga_pci_dev->pci_region_requested = 1; + + fpga_pci_dev->fpga_base = pci_iomap(pdev, BAR0, fpga_pci_dev->len); + if (!fpga_pci_dev->fpga_base) { + ERROR_DEBUG( "[%s] Fail to remap PCI resource address\n", __func__); + retval = -ENODEV; + goto fail_to_remap_pci_resource; + } + else + { + I2C_DEBUG( "[%s] Remap PCI resource address 0x%p\n", __func__, fpga_pci_dev->fpga_base); + } + + /* Init these items before interrupt is enabled */ + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + sema_init(&(fpga_pci_dev->sem[smbus_temp_num]), 1); + } + spin_lock_init(&(fpga_pcie_lock)); + + pci_set_master(pdev); + + /* Register sysfs hooks */ + fpga_data.fpga_base = fpga_pci_dev->fpga_base; + +#ifdef INTERRUPT_SUPPORT + fpga_mask_all_intr(fpga_pci_dev); + + /* Register interrupt */ + retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (retval > 0) + { + fpga_pci_dev->irq_vec_count = retval; + I2C_DEBUG( "[%s] PCI irq vector count %d IRQ number: %u\n", __func__, retval, pdev->irq); + } + else + { + I2C_DEBUG( "[%s] Fail to alloc PCI irq vectors\n", __func__); + goto fail_to_alloc_irq_vectors; + } + + retval = request_irq(pci_irq_vector(pdev, 0), fpga_intr_msi, 0, "FPGA MSI", (void *) fpga_pci_dev); + if (retval < 0) { + I2C_DEBUG( "[%s] Fail to request irq\n", __func__); + goto fail_to_request_irq; + } else { + I2C_DEBUG( "[%s] Request irq no. %d\n", __func__, pdev->irq); + fpga_pci_dev->irq = pdev->irq; + } +#endif + + cdev_init(&(fpga_pci_dev->fpga_cdev), &fpga_fops); + fpga_pci_dev->fpga_cdev.owner = THIS_MODULE; + + retval = cdev_add(&(fpga_pci_dev->fpga_cdev), fpga_pci_dev->fpga_dev_number, fpga_dev_count); + if (retval) + { + goto fail_to_add_fpga_cdev; + } + fpga_pci_dev->cdev_added = 1; + + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + i2c_set_adapdata(&fpga_pci_dev->adapter[smbus_temp_num], fpga_pci_dev); + fpga_pci_dev->adapter[smbus_temp_num].owner = THIS_MODULE; + fpga_pci_dev->adapter[smbus_temp_num].class = I2C_CLASS_HWMON; + fpga_pci_dev->adapter[smbus_temp_num].algo = &smbus_algorithm; + fpga_pci_dev->adapter[smbus_temp_num].dev.parent = &pdev->dev; + fpga_pci_dev->adapter[smbus_temp_num].retries = 3; + + /* Default timeout in interrupt mode: 200 ms */ + fpga_pci_dev->adapter[smbus_temp_num].timeout = HZ / 5; + + snprintf(fpga_pci_dev->adapter[smbus_temp_num].name, sizeof(fpga_pci_dev->adapter[smbus_temp_num].name), + "%s%02d adapter", FPGA_SMBUS_NAME, smbus_temp_num); + + fpga_pci_dev->adapter[smbus_temp_num].nr = 101+smbus_temp_num; + retval = i2c_add_numbered_adapter(&fpga_pci_dev->adapter[smbus_temp_num]); + + if (retval) { + ERROR_DEBUG( "[%s] err: 0x%x\n", __func__, retval); + goto fail_to_add_i2c_adapter; + } + } + + fpga_init_smbus(fpga_pci_dev); + + dev_info(&(pdev->dev), "FPGA SMBus is running with delay_ack %d us, delay_tip %d us, delay_busy %d us, and max_retry_time %d, PRER_LO_DEFAULT: 0x%x \n", delay_ack, delay_tip, delay_busy, max_retry_time, PRER_LO_DEFAULT); + fpga_initialized=true; + return(0); + +fail_to_add_i2c_adapter: + for(smbus_adapter_num = 0; smbus_adapter_num < smbus_temp_num; smbus_adapter_num++) + { + i2c_del_adapter(&fpga_pci_dev->adapter[smbus_adapter_num]); + } + +fail_to_add_fpga_cdev: + free_irq(pdev->irq, (void *) fpga_pci_dev); + fpga_pci_dev->cdev_added = 0; +#ifdef INTERRUPT_SUPPORT +fail_to_request_irq: + pci_free_irq_vectors(pdev); + fpga_pci_dev->irq = 0; +fail_to_alloc_irq_vectors: + iounmap(fpga_pci_dev->fpga_base); + fpga_pci_dev->irq_vec_count = 0; +#endif +fail_to_remap_pci_resource: + pci_release_region(pdev, BAR0); + fpga_pci_dev->fpga_base = NULL; +fail_to_request_pci_region: + pci_disable_device(pdev); + fpga_pci_dev->pci_region_requested = 0; +fail_to_enable_pci_device: + device_destroy(fpga_pci_dev->fpga_class, fpga_pci_dev->fpga_dev_number); + fpga_pci_dev->pci_enabled = 0; +fail_to_create_fpga_udev: + class_destroy(fpga_pci_dev->fpga_class); + fpga_pci_dev->fpga_udev = NULL; +fail_to_create_fpga_class: + unregister_chrdev_region(fpga_pci_dev->fpga_dev_number, fpga_dev_count); + fpga_pci_dev->fpga_class = NULL; +fail_to_alloc_dev_number: + fpga_pci_dev->fpga_dev_number = 0; + kfree(fpga_pci_dev); +fail_to_alloc_fpga_pci_dev: + pci_set_drvdata(pdev, (void *) NULL); + + return(retval); +} + +static void fpga_pci_remove(struct pci_dev *pdev) +{ + struct fpga_pci_device *fpga_pci_dev = NULL; + int smbus_temp_num; + + I2C_DEBUG( "[%s]\n", __func__); + + fpga_pci_dev = (struct fpga_pci_device *) pci_get_drvdata(pdev); + if (fpga_pci_dev) + { + if (fpga_pci_dev->cdev_added) + { + cdev_del(&(fpga_pci_dev->fpga_cdev)); + } + + if (fpga_pci_dev->irq > 0) + { + free_irq(pci_irq_vector(pdev, 0), (void *) fpga_pci_dev); + I2C_DEBUG( "[%s] free_irq()\n", __func__); + } + + if (fpga_pci_dev->irq_vec_count > 0) + { + pci_free_irq_vectors(pdev); + I2C_DEBUG( "[%s] pci_free_irq_vectors()\n", __func__); + } + + if (fpga_pci_dev->fpga_base != NULL) + { + iounmap(fpga_pci_dev->fpga_base); + I2C_DEBUG( "[%s] iounmap()\n", __func__); + } + + if (fpga_pci_dev->pci_region_requested) + { + pci_release_region(pdev, BAR0); + I2C_DEBUG( "[%s] pci_release_region()\n", __func__); + } + + if (fpga_pci_dev->pci_enabled) + { + pci_disable_device(pdev); + I2C_DEBUG( "[%s] pci_disable_device()\n", __func__); + } + + if (fpga_pci_dev->fpga_udev) + { + device_destroy(fpga_pci_dev->fpga_class, fpga_pci_dev->fpga_dev_number); + } + + if (fpga_pci_dev->fpga_class) + { + class_destroy(fpga_pci_dev->fpga_class); + I2C_DEBUG( "[%s] class_destroy()\n", __func__); + } + + if (fpga_pci_dev->fpga_dev_number) + { + unregister_chrdev_region(fpga_pci_dev->fpga_dev_number, fpga_dev_count); + I2C_DEBUG( "[%s] unregister_chrdev_region()\n", __func__); + } + + for(smbus_temp_num = 0; smbus_temp_num < MAX_CHANNEL; smbus_temp_num++) + { + i2c_del_adapter(&fpga_pci_dev->adapter[smbus_temp_num]); + } + + kfree(fpga_pci_dev); + I2C_DEBUG( "[%s] kfree(fpga_pci_dev)\n", __func__); + } + fpga_initialized=false; + return; +} + +static struct pci_driver fpga_pci_driver = { + .name = FPGA_DRIVER_NAME, + .id_table = fpga_id_table, + .probe = fpga_pci_probe, + .remove = fpga_pci_remove, + .err_handler = &fpga_error_handlers, +}; + +static int __init fpga_module_init(void) +{ + I2C_DEBUG( "[%s]\n", __func__); + + mutex_init(&fpga_data.update_lock); + return pci_register_driver(&fpga_pci_driver); +} + +static void __exit fpga_module_cleanup(void) +{ + I2C_DEBUG( "[%s]\n", __func__); + + mutex_destroy(&fpga_data.update_lock); + pci_unregister_driver(&fpga_pci_driver); + return; +} + +module_init(fpga_module_init); +module_exit(fpga_module_cleanup); + +MODULE_DEVICE_TABLE(pci, fpga_id_table); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-leds.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-leds.c new file mode 100644 index 000000000..78d6db6b6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-leds.c @@ -0,0 +1,485 @@ +/* + * A LED driver for the accton_as7327_56x_led + * + * Copyright (C) 2014 Accton Technology Corporation. + * Brandon Chuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int as7327_56x_cpld_read (unsigned short cpld_addr, u8 reg); +extern int as7327_56x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value); + +#define DRVNAME "accton_as7327_56x_led" + +struct accton_as7327_56x_led_data { + struct platform_device *pdev; + struct mutex update_lock; + char valid; /* != 0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 reg_val[2]; +}; + +static struct accton_as7327_56x_led_data *ledctl = NULL; + +/* LED related data + */ + +#define LED_CNTRLER_I2C_ADDRESS (0x62) + +#define LED_TYPE_SYS_REG_MASK (0x7) +#define LED_MODE_SYS_GREEN_VALUE (0x0) +#define LED_MODE_SYS_RED_VALUE (0x1) +#define LED_MODE_SYS_GREEN_BLINK_VALUE (0x2) + +#define LED_TYPE_BMC_REG_MASK (0x38) +#define LED_MODE_BMC_AMBER_VALUE (0x0) +#define LED_MODE_BMC_GREEN_BLINK_VALUE (0x20) +#define LED_MODE_BMC_GREEN_VALUE (0x28) +#define LED_MODE_BMC_RED_VALUE (0x30) +#define LED_MODE_BMC_OFF_VALUE (0x38) + +#define LED_TYPE_USB_REG_MASK (0xC0) +#define LED_MODE_USB_OFF_VALUE (0xC0) +#define LED_MODE_USB_GREEN_VALUE (0x40) +#define LED_MODE_USB_RED_VALUE (0x80) +#define LED_MODE_USB_GREEN_BLINK_VALUE (0x00) + +#define LED_TYPE_ID_REG_MASK (0x10) +#define LED_MODE_ID_OFF_VALUE (0x0) +#define LED_MODE_ID_BLUE_VALUE (0x10) + +enum led_type { + LED_TYPE_SYS, + LED_TYPE_BMC, + LED_TYPE_ID, + LED_TYPE_USB, + LED_TYPE_FAN, + LED_TYPE_PSU1, + LED_TYPE_PSU2 +}; + +struct led_reg { + u32 types; + u8 reg_addr; +}; + +static const struct led_reg led_reg_map[] = { + { (1<update_lock); + + if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2) + || !ledctl->valid) { + int i; + + dev_dbg(&ledctl->pdev->dev, "Starting accton_as7327_56x_led update\n"); + ledctl->valid = 0; + + /* Update LED data + */ + for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) { + int status = accton_as7327_56x_led_read_value(led_reg_map[i].reg_addr); + + if (status < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status); + goto exit; + } + else + { + ledctl->reg_val[i] = status; + } + } + + ledctl->last_updated = jiffies; + ledctl->valid = 1; + } + +exit: + mutex_unlock(&ledctl->update_lock); +} + +static void accton_as7327_56x_led_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode, + enum led_type type) +{ + int reg_val; + u8 reg; + + mutex_lock(&ledctl->update_lock); + + if( !accton_getLedReg(type, ®)) + { + dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type); + } + + reg_val = accton_as7327_56x_led_read_value(reg); + if (reg_val < 0) { + dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val); + goto exit; + } + + reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val); + accton_as7327_56x_led_write_value(reg, reg_val); + ledctl->valid = 0; + +exit: + mutex_unlock(&ledctl->update_lock); +} + + +static void accton_as7327_56x_led_sys_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7327_56x_led_set(led_cdev, led_light_mode, LED_TYPE_SYS); +} + +static enum led_brightness accton_as7327_56x_led_sys_get(struct led_classdev *cdev) +{ + accton_as7327_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_SYS, ledctl->reg_val[0]); +} + +static void accton_as7327_56x_led_bmc_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7327_56x_led_set(led_cdev, led_light_mode, LED_TYPE_BMC); +} + +static enum led_brightness accton_as7327_56x_led_bmc_get(struct led_classdev *cdev) +{ + accton_as7327_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_BMC, ledctl->reg_val[0]); +} + +static void accton_as7327_56x_led_usb_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7327_56x_led_set(led_cdev, led_light_mode, LED_TYPE_USB); +} + +static enum led_brightness accton_as7327_56x_led_usb_get(struct led_classdev *cdev) +{ + accton_as7327_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_USB, ledctl->reg_val[0]); +} + +static void accton_as7327_56x_led_id_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ + accton_as7327_56x_led_set(led_cdev, led_light_mode, LED_TYPE_ID); +} + +static enum led_brightness accton_as7327_56x_led_id_get(struct led_classdev *cdev) +{ + accton_as7327_56x_led_update(); + return led_reg_val_to_light_mode(LED_TYPE_ID, ledctl->reg_val[1]); +} + +static void accton_as7327_56x_led_auto_set(struct led_classdev *led_cdev, + enum led_brightness led_light_mode) +{ +} + +static enum led_brightness accton_as7327_56x_led_auto_get(struct led_classdev *cdev) +{ + return LED_MODE_AUTO; +} + +static struct led_classdev accton_as7327_56x_leds[] = { + [LED_TYPE_SYS] = { + .name = "accton_as7327_56x_led::sys", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_sys_set, + .brightness_get = accton_as7327_56x_led_sys_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN_BLINK, + }, + [LED_TYPE_BMC] = { + .name = "accton_as7327_56x_led::bmc", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_bmc_set, + .brightness_get = accton_as7327_56x_led_bmc_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN_BLINK, + }, + [LED_TYPE_USB] = { + .name = "accton_as7327_56x_led::usb", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_usb_set, + .brightness_get = accton_as7327_56x_led_usb_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN_BLINK, + }, + [LED_TYPE_ID] = { + .name = "accton_as7327_56x_led::id", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_id_set, + .brightness_get = accton_as7327_56x_led_id_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_GREEN_BLINK, + }, + [LED_TYPE_FAN] = { + .name = "accton_as7327_56x_led::fan", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_auto_set, + .brightness_get = accton_as7327_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU1] = { + .name = "accton_as7327_56x_led::psu1", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_auto_set, + .brightness_get = accton_as7327_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, + [LED_TYPE_PSU2] = { + .name = "accton_as7327_56x_led::psu2", + .default_trigger = "unused", + .brightness_set = accton_as7327_56x_led_auto_set, + .brightness_get = accton_as7327_56x_led_auto_get, + .flags = LED_CORE_SUSPENDRESUME, + .max_brightness = LED_MODE_AUTO, + }, +}; + +static int accton_as7327_56x_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7327_56x_leds); i++) { + led_classdev_suspend(&accton_as7327_56x_leds[i]); + } + + return 0; +} + +static int accton_as7327_56x_led_resume(struct platform_device *dev) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(accton_as7327_56x_leds); i++) { + led_classdev_resume(&accton_as7327_56x_leds[i]); + } + + return 0; +} + +static int accton_as7327_56x_led_probe(struct platform_device *pdev) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(accton_as7327_56x_leds); i++) { + ret = led_classdev_register(&pdev->dev, &accton_as7327_56x_leds[i]); + + if (ret < 0) + break; + } + + /* Check if all LEDs were successfully registered */ + if (i != ARRAY_SIZE(accton_as7327_56x_leds)) { + int j; + + /* only unregister the LEDs that were successfully registered */ + for (j = 0; j < i; j++) { + led_classdev_unregister(&accton_as7327_56x_leds[i]); + } + } + + return ret; +} + +static void accton_as7327_56x_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(accton_as7327_56x_leds); i++) { + led_classdev_unregister(&accton_as7327_56x_leds[i]); + } +} + +static struct platform_driver accton_as7327_56x_led_driver = { + .probe = accton_as7327_56x_led_probe, + .remove = accton_as7327_56x_led_remove, + .suspend = accton_as7327_56x_led_suspend, + .resume = accton_as7327_56x_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init accton_as7327_56x_led_init(void) +{ + int ret; + + ret = platform_driver_register(&accton_as7327_56x_led_driver); + if (ret < 0) { + goto exit; + } + + ledctl = kzalloc(sizeof(struct accton_as7327_56x_led_data), GFP_KERNEL); + if (!ledctl) { + ret = -ENOMEM; + platform_driver_unregister(&accton_as7327_56x_led_driver); + goto exit; + } + + mutex_init(&ledctl->update_lock); + + ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(ledctl->pdev)) { + ret = PTR_ERR(ledctl->pdev); + platform_driver_unregister(&accton_as7327_56x_led_driver); + kfree(ledctl); + goto exit; + } + +exit: + return ret; +} + +static void __exit accton_as7327_56x_led_exit(void) +{ + platform_device_unregister(ledctl->pdev); + platform_driver_unregister(&accton_as7327_56x_led_driver); + kfree(ledctl); +} + +module_init(accton_as7327_56x_led_init); +module_exit(accton_as7327_56x_led_exit); + +MODULE_AUTHOR("Brandon Chuang "); +MODULE_DESCRIPTION("accton_as7327_56x_led driver"); +MODULE_LICENSE("GPL"); diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu.c new file mode 100644 index 000000000..839fdcbad --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu.c @@ -0,0 +1,737 @@ +/* + * Hardware monitoring driver for as7327_56x_psu + * + * Copyright (c) 2015 Accton Technology + * Copyright (c) 2015 Zhenling Yin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PSU_MAX_FAN_SPEED 23000 +#define PMBUS_MFR_MAX_LEN (31) +#define PMBUS_MFR_PSU_VIN_TYPE 0x85 //just for C1A-B0650 +#define PMBUS_READ_LED_STATUS 0xDA //just for C1A-B0650 +#define I2C_RW_RETRY_COUNT 10 +#define I2C_RW_RETRY_INTERVAL 60 /* ms */ + +#define IS_PRESENT(id, value) (!(value & BIT(7 - id))) + +/* + * Registers + */ +enum pmbus_regs { + PMBUS_VOUT_MODE = 0x20, + PMBUS_STATUS_WORD = 0x79, + PMBUS_STATUS_TEMPERATURE = 0x7D, + PMBUS_STATUS_FAN_12 = 0x81, + PMBUS_READ_VIN = 0x88, + PMBUS_READ_IIN = 0x89, + PMBUS_READ_VOUT = 0x8B, + PMBUS_READ_IOUT = 0x8C, + PMBUS_READ_TEMPERATURE_1 = 0x8D, + PMBUS_READ_TEMPERATURE_2 = 0x8E, + PMBUS_READ_TEMPERATURE_3 = 0x8F, + PMBUS_READ_FAN_SPEED_1 = 0x90, + PMBUS_READ_POUT = 0x96, + PMBUS_READ_PIN = 0x97, + PMBUS_MFR_ID = 0x99, + PMBUS_MFR_MODEL = 0x9A, + PMBUS_MFR_REVISION = 0x9B, + PMBUS_MFR_SERIAL = 0x9E, +}; + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE BIT(0) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_BUSY BIT(7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN BIT(8) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_FANS BIT(10) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_VOUT BIT(15) + +/* Each client has this additional data + */ +struct as7327_56x_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if registers are valid */ + unsigned long last_updated; /* In jiffies */ + u8 chip; /* chip id */ + u8 status; /* Status(present/power_good) register read from CPLD */ + u16 status_word; /* Register value */ + u8 fan_fault; /* Register value */ + u8 over_temp; /* Register value */ + u16 v_in; /* Register value */ + u16 i_in; /* Register value */ + u16 p_in; /* Register value */ + u16 v_out; /* Register value */ + u16 i_out; /* Register value */ + u16 p_out; /* Register value */ + u8 vout_mode; /* Register value */ + u16 temp[3]; /* Register value */ + u16 fan_speed; /* Register value */ + u16 fan_duty_cycle; /* Register value */ + u8 mfr_id[PMBUS_MFR_MAX_LEN]; /* Register value */ + u8 mfr_model[PMBUS_MFR_MAX_LEN]; /* Register value */ + u8 mfr_revsion[PMBUS_MFR_MAX_LEN]; /* Register value */ + u8 mfr_serial[PMBUS_MFR_MAX_LEN]; /* Register value */ + u8 mfr_vin_type; /* Register value */ + u8 led_status; /* Register value */ +}; + +static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_word(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_pout_max(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_vin_type(struct device *dev, struct device_attribute *da, char *buf); +static ssize_t show_led_status(struct device *dev, struct device_attribute *da, char *buf); +static struct as7327_56x_data *as7327_56x_update_device(struct device *dev); +extern int as7327_56x_cpld_read(unsigned short cpld_addr, u8 reg); + +enum as7327_56x_sysfs_attributes { + PSU_PRESENT = 0, + PSU_POWER_GOOD, + PSU_POWER_ON, + PSU_TEMP_FAULT, + PSU_FAN1_FAULT, + PSU_OVER_TEMP, + PSU_V_IN, + PSU_I_IN, + PSU_P_IN, + PSU_V_OUT, + PSU_I_OUT, + PSU_P_OUT, + PSU_TEMP1_INPUT, + PSU_TEMP2_INPUT, + PSU_TEMP3_INPUT, + PSU_FAN1_SPEED, + PSU_FAN1_DUTY_CYCLE, + PSU_FAN_DIRECTION, + PSU_MFR_ID, + PSU_MFR_MODEL, + PSU_MFR_REVISION, + PSU_MFR_SERIAL, + PSU_MFR_POUT_MAX, + PSU_MFR_VIN_TYPE, + PSU_LED_STATUS +}; + +/* sysfs attributes for hwmon + */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_present, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP); +static SENSOR_DEVICE_ATTR(psu_v_in, S_IRUGO, show_linear, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(psu_i_in, S_IRUGO, show_linear, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(psu_p_in, S_IRUGO, show_linear, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_vout, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp2_input, S_IRUGO, show_linear, NULL, PSU_TEMP2_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp3_input, S_IRUGO, show_linear, NULL, PSU_TEMP3_INPUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IRUGO, show_word, NULL, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_word, NULL, PSU_FAN_DIRECTION); +static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID); +static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL); +static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION); +static SENSOR_DEVICE_ATTR(psu_mfr_serial, S_IRUGO, show_ascii, NULL, PSU_MFR_SERIAL); +static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_pout_max, NULL, PSU_MFR_POUT_MAX); +static SENSOR_DEVICE_ATTR(psu_mfr_vin_type, S_IRUGO, show_vin_type, NULL, PSU_MFR_VIN_TYPE); +static SENSOR_DEVICE_ATTR(psu_led_status, S_IRUGO, show_led_status, NULL, PSU_LED_STATUS); + +static struct attribute *as7327_56x_attributes[] = { + &sensor_dev_attr_psu_present.dev_attr.attr, + &sensor_dev_attr_psu_power_on.dev_attr.attr, + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, + &sensor_dev_attr_psu_power_good.dev_attr.attr, + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, + &sensor_dev_attr_psu_over_temp.dev_attr.attr, + &sensor_dev_attr_psu_v_in.dev_attr.attr, + &sensor_dev_attr_psu_i_in.dev_attr.attr, + &sensor_dev_attr_psu_p_in.dev_attr.attr, + &sensor_dev_attr_psu_v_out.dev_attr.attr, + &sensor_dev_attr_psu_i_out.dev_attr.attr, + &sensor_dev_attr_psu_p_out.dev_attr.attr, + &sensor_dev_attr_psu_temp1_input.dev_attr.attr, + &sensor_dev_attr_psu_temp2_input.dev_attr.attr, + &sensor_dev_attr_psu_temp3_input.dev_attr.attr, + &sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr, + &sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr, + &sensor_dev_attr_psu_fan_dir.dev_attr.attr, + &sensor_dev_attr_psu_mfr_id.dev_attr.attr, + &sensor_dev_attr_psu_mfr_model.dev_attr.attr, + &sensor_dev_attr_psu_mfr_revision.dev_attr.attr, + &sensor_dev_attr_psu_mfr_serial.dev_attr.attr, + &sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr, + &sensor_dev_attr_psu_mfr_vin_type.dev_attr.attr, + &sensor_dev_attr_psu_led_status.dev_attr.attr, + NULL +}; + +static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_data *data = i2c_get_clientdata(client); + int status = 0; + + mutex_lock(&data->update_lock); + + status = as7327_56x_cpld_read(0x62, 0x1d); + if (status < 0) { + dev_dbg(&client->dev, "cpld reg 0x62 err %d\n", status); + } + else { + data->status = status; + } + mutex_unlock(&data->update_lock); + + status = IS_PRESENT(data->chip, data->status); + + return sprintf(buf, "%d\n", status); +} + +static ssize_t show_word(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7327_56x_data *data = as7327_56x_update_device(dev); + u16 status = 0; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */ + status = (data->status_word & PB_STATUS_OFF) ? 0 : 1; + break; + case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */ + status = !!(data->status_word & PB_STATUS_TEMPERATURE); + break; + case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */ + status = (data->status_word & PB_STATUS_POWER_GOOD_N) ? 0 : 1; + break; + case PSU_FAN1_DUTY_CYCLE: + status = (data->fan_speed * 100) / PSU_MAX_FAN_SPEED; + status = (status > 100) ? 100 : status; + break; + case PSU_FAN_DIRECTION: /* psu_fan_dir, 0=>F2B, 1=>B2F */ + if ((strncmp((data->mfr_model + 1), "C1A-B0650-C", strlen("C1A-B0650-C")) == 0) || + (strncmp((data->mfr_model + 1), "G1342-0800W", strlen("G1342-0800W")) == 0)){ + status = 0; + } + break; + default: + return 0; + } + + return sprintf(buf, "%d\n", status); +} + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static int pmbus_linear16_to_val(int data, int data_exponent, long *val) +{ + int exponent, mantissa; + int temp_value = 0; + int multiplier = 1000; + + if (data < 0) + { + return data; + } + if (data_exponent < 0) + { + return data_exponent; + } + exponent = two_complement_to_int(data_exponent, 5, 0x1f); + mantissa = data; + if (exponent >= 0) + { + temp_value = (mantissa << exponent) * multiplier; + } + else + { + temp_value = (mantissa * multiplier) / (1 << -exponent); + } + *val = temp_value; + return 0; +} + +static ssize_t show_linear(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7327_56x_data *data = as7327_56x_update_device(dev); + u8 *ptr = NULL; + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + ptr = data->mfr_model + 1; /* The first byte is the count byte of string. */ + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_V_IN: + value = data->v_in; + break; + case PSU_I_IN: + value = data->i_in; + break; + case PSU_P_IN: + value = data->p_in; + break; + case PSU_I_OUT: + value = data->i_out; + break; + case PSU_P_OUT: + value = data->p_out; + break; + case PSU_TEMP1_INPUT: + case PSU_TEMP2_INPUT: + case PSU_TEMP3_INPUT: + value = data->temp[attr->index-PSU_TEMP1_INPUT]; + break; + case PSU_FAN1_SPEED: + value = data->fan_speed; + multiplier = 1; + break; + default: + return 0; + } + + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + + return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7327_56x_data *data = as7327_56x_update_device(dev); + u8 shift; + + if (!data->valid) { + return 0; + } + + shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_fault >> shift); +} + +static ssize_t show_over_temp(struct device *dev, struct device_attribute *da, char *buf) +{ + struct as7327_56x_data *data = as7327_56x_update_device(dev); + + if (!data->valid) { + return 0; + } + + return sprintf(buf, "%d\n", data->over_temp >> 7); +} + +static ssize_t show_ascii(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct as7327_56x_data *data = as7327_56x_update_device(dev); + u8 *ptr = NULL; + + if (!data->valid) { + return 0; + } + + switch (attr->index) { + case PSU_MFR_ID: /* psu_mfr_id */ + ptr = data->mfr_id + 1; /* The first byte is the count byte of string. */; + break; + case PSU_MFR_MODEL: /* psu_mfr_model */ + ptr = data->mfr_model + 1; /* The first byte is the count byte of string. */; + break; + case PSU_MFR_REVISION: /* psu_mfr_revision */ + ptr = data->mfr_revsion + 1; /* The first byte is the count byte of string. */; + break; + case PSU_MFR_SERIAL: /* psu_mfr_serial */ + ptr = data->mfr_serial + 1; /* The first byte is the count byte of string. */; + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", ptr); +} + +static ssize_t show_vout(struct device *dev, struct device_attribute *da, char *buf) +{ + struct as7327_56x_data *data = as7327_56x_update_device(dev); + int status = 0; + long val = 0; + + if (!data->valid) { + return 0; + } + + status = pmbus_linear16_to_val(data->v_out, data->vout_mode, &val); + if (status < 0) { + return 0; + } + + return sprintf(buf, "%ld\n", val); +} + +static ssize_t show_pout_max(struct device *dev, struct device_attribute *da, char *buf) +{ + int val = 650 * 1000; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t show_vin_type(struct device *dev, struct device_attribute *da, char *buf) +{ + struct as7327_56x_data *data = as7327_56x_update_device(dev); + + if (!data->valid) { + return 0; + } + + return sprintf(buf, "%d\n", data->mfr_vin_type); +} + +static ssize_t show_led_status(struct device *dev, struct device_attribute *da, char *buf) +{ + struct as7327_56x_data *data = as7327_56x_update_device(dev); + + if (!data->valid) { + return 0; + } + + return sprintf(buf, "%d\n", data->led_status); +} + +static int as7327_56x_read_byte(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_byte_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7327_56x_read_word(struct i2c_client *client, u8 reg) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + + while (retry) { + status = i2c_smbus_read_word_data(client, reg); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + + break; + } + + return status; +} + +static int as7327_56x_read_block(struct i2c_client *client, u8 command, u8 *data) +{ + int status = 0, retry = I2C_RW_RETRY_COUNT; + int read_value = 0; + + while (retry) { + read_value = i2c_smbus_read_byte_data(client, command); + if ((read_value >= (PMBUS_MFR_MAX_LEN-1)) || (read_value < 0)) + { + return -EINVAL; + } + status = i2c_smbus_read_i2c_block_data(client, command, read_value+1, data); + if (unlikely(status < 0)) { + msleep(I2C_RW_RETRY_INTERVAL); + retry--; + continue; + } + data[read_value+1] = '\0'; + break; + } + + return status; +} + +struct reg_data_byte { + u8 reg; + u8 *value; +}; + +struct reg_data_word { + u8 reg; + u16 *value; +}; + +static struct as7327_56x_data *as7327_56x_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct as7327_56x_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i, status; + struct reg_data_byte regs_byte[] = { {PMBUS_VOUT_MODE, &data->vout_mode}, + {PMBUS_STATUS_TEMPERATURE, &data->over_temp}, + {PMBUS_STATUS_FAN_12, &data->fan_fault}, + {PMBUS_READ_LED_STATUS, &data->led_status}, + }; + struct reg_data_word regs_word[] = { {PMBUS_STATUS_WORD, &data->status_word}, + {PMBUS_READ_VIN, &data->v_in}, + {PMBUS_READ_VOUT, &data->v_out}, + {PMBUS_READ_IIN, &data->i_in}, + {PMBUS_READ_IOUT, &data->i_out}, + {PMBUS_READ_POUT, &data->p_out}, + {PMBUS_READ_PIN, &data->p_in}, + {PMBUS_READ_TEMPERATURE_1, &(data->temp[0])}, + {PMBUS_READ_TEMPERATURE_2, &(data->temp[1])}, + {PMBUS_READ_TEMPERATURE_3, &(data->temp[2])}, + {PMBUS_READ_FAN_SPEED_1, &(data->fan_speed)}, + }; + + dev_dbg(&client->dev, "Starting as7327_56x_psu update\n"); + + /* Read byte data */ + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = as7327_56x_read_byte(client, regs_byte[i].reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_byte[i].reg, status); + } + else { + *(regs_byte[i].value) = status; + } + } + + /* Read word data */ + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = as7327_56x_read_word(client, regs_word[i].reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_word[i].reg, status); + } + else { + *(regs_word[i].value) = status; + } + + } + /* Read mfr_id */ + status = as7327_56x_read_block(client, PMBUS_MFR_ID, data->mfr_id); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", PMBUS_MFR_ID, status); + goto exit; + } + /* Read mfr_model */ + status = as7327_56x_read_block(client, PMBUS_MFR_MODEL, data->mfr_model); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", PMBUS_MFR_MODEL, status); + goto exit; + } + /* Read mfr_revsion */ + status = as7327_56x_read_block(client, PMBUS_MFR_REVISION, data->mfr_revsion); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", PMBUS_MFR_REVISION, status); + goto exit; + } + /* Read mfr_serial */ + status = as7327_56x_read_block(client, PMBUS_MFR_SERIAL, data->mfr_serial); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", PMBUS_MFR_SERIAL, status); + goto exit; + } + + /* mfr_vin_type */ + if ((strncmp((data->mfr_model + 1), "C1A-B0650-C", strlen("C1A-B0650-C")) == 0) || + (strncmp((data->mfr_model + 1), "G1342-0800W", strlen("G1342-0800W")) == 0)){ + data->mfr_vin_type = 1; + } + + data->last_updated = jiffies; + data->valid = 1; + } + +exit: + mutex_unlock(&data->update_lock); + + return data; +} + +enum psu_index { + as7327_56x_psu1, + as7327_56x_psu2 +}; + +static const struct i2c_device_id as7327_56x_psu_id[] = { + { "as7327_56x_psu1", as7327_56x_psu1 }, + { "as7327_56x_psu2", as7327_56x_psu2 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as7327_56x_psu_id); + +static const struct attribute_group as7327_56x_group = { + .attrs = as7327_56x_attributes, +}; + +static umode_t as7327_56x_psu_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0; +} + +static const struct hwmon_channel_info *as7327_56x_psu_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + NULL, +}; + +static const struct hwmon_ops as7327_56x_psu_hwmon_ops = { + .is_visible = as7327_56x_psu_is_visible, +}; + +static const struct hwmon_chip_info as7327_56x_psu_chip_info = { + .ops = &as7327_56x_psu_hwmon_ops, + .info = as7327_56x_psu_info, +}; + +static int as7327_56x_psu_probe(struct i2c_client *client) +{ + const struct i2c_device_id *dev_id = i2c_client_get_device_id(client); + struct as7327_56x_data *data; + int status = 0; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + data = kzalloc(sizeof(struct as7327_56x_data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->chip = dev_id->driver_data; + dev_info(&client->dev, "chip found\n"); + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &as7327_56x_group); + if (status) { + goto exit_free; + } + + data->hwmon_dev = hwmon_device_register_with_info(&client->dev, "as7327_56x", + NULL, &as7327_56x_psu_chip_info, NULL); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "%s: psu '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &as7327_56x_group); +exit_free: + kfree(data); +exit: + return status; +} + +void as7327_56x_psu_remove(struct i2c_client *client) +{ + struct as7327_56x_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &as7327_56x_group); + kfree(data); +} + +static struct i2c_driver as7327_56x_psu_driver = { + .driver = { + .name = "as7327_56x_psu", + }, + .probe = as7327_56x_psu_probe, + .remove = as7327_56x_psu_remove, + .id_table = as7327_56x_psu_id, +}; + +module_i2c_driver(as7327_56x_psu_driver); + +MODULE_AUTHOR("vincent_chiang@edge-core.com"); +MODULE_DESCRIPTION("as7327_56x_psu driver"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu_bmc.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu_bmc.c new file mode 100644 index 000000000..3a5bbdfea --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-psu_bmc.c @@ -0,0 +1,972 @@ +/* + * Copyright (C) Ray Huang + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7327_56x_psu_bmc" +#define IPMI_NETFN 0x36 +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 +#define IPMI_MFR_LEN 32 + +#define IPMI_GET_PSU_ALARM_DATA_CMD 0x18 +#define IPMI_GET_PSU_ATTRIBUTE_DATA_CMD 0x1A +#define IPMI_PSU_GET_EEPROM_CMD 0xA1 + +#define PSU_MAX_FAN_SPEED 23000 + +#define IS_PRESENT(id, value) (!(value & BIT(7 - id))) +#define VALIDATE_PRESENT_RETURN(id) \ +do { \ + if (data->psu_data[id].present == 0) { \ + mutex_unlock(&data->update_lock); \ + return -ENXIO; \ + } \ +} while (0) + +#define VALIDATE_IPMI_REQUEST_RV(status, addr, cmd) \ +do { \ + if (unlikely(status != 0)) \ + dev_err(&data->pdev[pid]->dev, "ipmi request (0x%x) err (0x%x)", addr, cmd); \ + if (unlikely(data->ipmi.rx_result != 0)) \ + dev_err(&data->pdev[pid]->dev, "ipmi request (0x%x) failed (0x%x)", addr, cmd); \ +} while (0) + + +enum psu_eeprom_read_type +{ + eeprom_psu_product_number = 0x01, + eeprom_psu_serial_number = 0x02, + eeprom_psu_vendor = 0x03, + eeprom_psu_model_name = 0x04, + eeprom_psu_hardware_revision = 0x05, + eeprom_psu_date = 0x06, +}; + +enum ipmi_app_power_attribute_type +{ + POWER_GOOD = 0x02, + POWER_STATUS = 0x03, + POWER_CURRENT_LIMIT = 0x04, + POWER_TEMP_LIMIT = 0x05, + POWER_INPUT_VOLTAGE = 0x0E, + POWER_OUTPUT_VOLTAGE = 0x0F, + POWER_OUTPUT_CURRENT = 0x10, + POWER_TEMPERATURE = 0x11, + POWER_FAN_SPEED = 0x12, + POWER_OUTPUT_POWER = 0x13, + POWER_INPUT_POWER = 0x14, + POWER_HARDWARE_VER = 0x21, + POWER_INPUT_CURRENT = 0x23, + POWER_TEMPERATURE_1 = 0x24, + POWER_TEMPERATURE_2 = 0x25, + POWER_TEMPERATURE_3 = 0x26, + POWER_POUT_MAX = 0x27, + POWER_VIN_TYPE = 0x28, + POWER_LED_STATUS = 0x29, +}; + +enum ipmi_app_power_alarm_type +{ + POWER_ALARM_CMD = 0x1, + POWER_ALARM_TEMPERATURE = 0x2, + POWER_ALARM_VIN_UV_FAULT = 0x3, + POWER_ALARM_IOUT_OC_FAULT = 0x4, + POWER_ALARM_VOUT_OV_FAULT = 0x5, + POWER_ALARM_VIN_OV_DETECT = 0x6, + POWER_ALARM_VIN_UNIT_OFF = 0x7, + POWER_ALARM_OTHER = 0x9, + POWER_ALARM_FAN = 0xA, + POWER_ALARM_INPUT = 0xD, + POWER_ALARM_IOUT_POUT = 0xE, + POWER_ALARM_VOUT = 0xF, +}; + +#define PSU_STATUS_NONE_ABOVE BIT(0) +#define PSU_STATUS_CML BIT(1) +#define PSU_STATUS_TEMPERATURE BIT(2) +#define PSU_STATUS_VIN_UV BIT(3) +#define PSU_STATUS_IOUT_OC BIT(4) +#define PSU_STATUS_VOUT_OV BIT(5) +#define PSU_STATUS_OFF BIT(6) +#define PSU_STATUS_BUSY BIT(7) +#define PSU_STATUS_UNKNOWN BIT(8) +#define PSU_STATUS_OTHER BIT(9) +#define PSU_STATUS_FANS BIT(10) +#define PSU_STATUS_POWER_GOOD_N BIT(11) +#define PSU_STATUS_WORD_MFR BIT(12) +#define PSU_STATUS_INPUT BIT(13) +#define PSU_STATUS_IOUT_POUT BIT(14) +#define PSU_STATUS_VOUT BIT(15) + +extern int as7327_56x_cpld_read(unsigned short cpld_addr, u8 reg); + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_psu_alarm(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_psu_fan(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_psu_status(struct device *dev, struct device_attribute *da, + char *buf); +static ssize_t show_psu_mfr(struct device *dev, struct device_attribute *da, + char *buf); +static int as7327_56x_psu_probe(struct platform_device *pdev); +static void as7327_56x_psu_remove(struct platform_device *pdev); +static int as7327_56x_psu_update_device(struct device *dev, unsigned char pid); + +enum psu_id { + PSU_1, + PSU_2, + NUM_OF_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 psu_data_s { + char mfr_pn[IPMI_MFR_LEN+1]; + char mfr_sn[IPMI_MFR_LEN+1]; + char mfr_id[IPMI_MFR_LEN+1]; + char mfr_model[IPMI_MFR_LEN+1]; + char mfr_hwrev[IPMI_MFR_LEN+1]; + char mfr_date[IPMI_MFR_LEN+1]; + unsigned char status[4]; + unsigned char vin[4]; + unsigned char vout[4]; + unsigned char iin[4]; + unsigned char iout[4]; + unsigned char pin[4]; + unsigned char pout[4]; + unsigned char fan_speed[4]; + unsigned char temp[3][4]; /* 3 temp sensors */ + unsigned char present; +}; + +struct as7327_56x_psu_data { + struct platform_device *pdev[2]; + struct device *hwmon_dev[2]; + struct mutex update_lock; + char valid[2]; /* != 0 if registers are valid, 0: PSU1, 1: PSU2 */ + unsigned long last_updated[2]; /* In jiffies, 0: PSU1, 1: PSU2 */ + struct ipmi_data ipmi; + char ipmi_resp[30]; + unsigned char ipmi_tx_data[3]; + struct psu_data_s psu_data[2]; +}; + +struct as7327_56x_psu_data *data = NULL; + +static struct platform_driver as7327_56x_psu_driver = { + .probe = as7327_56x_psu_probe, + .remove = as7327_56x_psu_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum as7327_56x_psu_sysfs_attrs { + /* psu attributes */ + PSU_PRESENT, + PSU_POWER_GOOD, + PSU_POWER_ON, + PSU_TEMP_FAULT, + PSU_FAN_FAULT, + PSU_OVR_TEMP, + PSU_PN, + PSU_SN, + PSU_ID, + PSU_MODEL, + PSU_REVISION, + PSU_DATE, + PSU_VIN, + PSU_VOUT, + PSU_IIN, + PSU_IOUT, + PSU_PIN, + PSU_POUT, + PSU_TEMP1_INPUT, + PSU_TEMP2_INPUT, + PSU_TEMP3_INPUT, + PSU_FAN_SPEED, + PSU_FAN_DUTY_CYCLE, + PSU_FAN_DIRECTION, + PSU_POUT_MAX, + PSU_VIN_TYPE, + NUM_OF_PSU_ATTR +}; + +/* psu attributes */ +static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_present, NULL, PSU_PRESENT); +static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_psu_alarm, NULL, PSU_POWER_GOOD); +static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_psu_alarm, NULL, PSU_POWER_ON); +static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_psu_alarm, NULL, PSU_TEMP_FAULT); +static SENSOR_DEVICE_ATTR(psu_ovr_temp, S_IRUGO, show_psu_alarm, NULL, PSU_OVR_TEMP); +static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_psu_alarm, NULL, PSU_FAN_FAULT); +static SENSOR_DEVICE_ATTR(psu_product_number, S_IRUGO, show_psu_mfr, NULL, PSU_PN); +static SENSOR_DEVICE_ATTR(psu_serial_number, S_IRUGO, show_psu_mfr, NULL, PSU_SN); +static SENSOR_DEVICE_ATTR(psu_id, S_IRUGO, show_psu_mfr, NULL, PSU_ID); +static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_psu_mfr, NULL, PSU_MODEL); +static SENSOR_DEVICE_ATTR(psu_revision, S_IRUGO, show_psu_mfr, NULL, PSU_REVISION); +static SENSOR_DEVICE_ATTR(psu_date, S_IRUGO, show_psu_mfr, NULL, PSU_DATE); +static SENSOR_DEVICE_ATTR(psu_vin_type, S_IRUGO, show_psu_mfr, NULL, PSU_VIN_TYPE); +static SENSOR_DEVICE_ATTR(psu_vin, S_IRUGO, show_psu_status, NULL, PSU_VIN); +static SENSOR_DEVICE_ATTR(psu_vout, S_IRUGO, show_psu_status, NULL, PSU_VOUT); +static SENSOR_DEVICE_ATTR(psu_iin, S_IRUGO, show_psu_status, NULL, PSU_IIN); +static SENSOR_DEVICE_ATTR(psu_iout, S_IRUGO, show_psu_status, NULL, PSU_IOUT); +static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_psu_status, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp2_input, S_IRUGO, show_psu_status, NULL, PSU_TEMP2_INPUT); +static SENSOR_DEVICE_ATTR(psu_temp3_input, S_IRUGO, show_psu_status, NULL, PSU_TEMP3_INPUT); +static SENSOR_DEVICE_ATTR(psu_pout_max, S_IRUGO, show_psu_status, NULL, PSU_POUT_MAX); +static SENSOR_DEVICE_ATTR(psu_pin, S_IRUGO, show_psu_status, NULL, PSU_PIN); +static SENSOR_DEVICE_ATTR(psu_pout, S_IRUGO, show_psu_status, NULL, PSU_POUT); +static SENSOR_DEVICE_ATTR(psu_fan1_speed, S_IRUGO, show_psu_fan, NULL, PSU_FAN_SPEED); +static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle, S_IRUGO, show_psu_fan, NULL, PSU_FAN_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(psu_fan1_dir, S_IRUGO, show_psu_fan, NULL, PSU_FAN_DIRECTION); + +#define DECLARE_PSU_ATTR() \ + &sensor_dev_attr_psu_present.dev_attr.attr, \ + &sensor_dev_attr_psu_power_good.dev_attr.attr, \ + &sensor_dev_attr_psu_power_on.dev_attr.attr, \ + &sensor_dev_attr_psu_temp_fault.dev_attr.attr, \ + &sensor_dev_attr_psu_ovr_temp.dev_attr.attr, \ + &sensor_dev_attr_psu_fan1_fault.dev_attr.attr, \ + &sensor_dev_attr_psu_product_number.dev_attr.attr, \ + &sensor_dev_attr_psu_serial_number.dev_attr.attr, \ + &sensor_dev_attr_psu_id.dev_attr.attr, \ + &sensor_dev_attr_psu_model_name.dev_attr.attr, \ + &sensor_dev_attr_psu_revision.dev_attr.attr, \ + &sensor_dev_attr_psu_date.dev_attr.attr, \ + &sensor_dev_attr_psu_vin_type.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_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_pout_max.dev_attr.attr, \ + &sensor_dev_attr_psu_fan1_speed.dev_attr.attr, \ + &sensor_dev_attr_psu_fan1_duty_cycle.dev_attr.attr, \ + &sensor_dev_attr_psu_fan1_dir.dev_attr.attr + +static struct attribute *as7327_56x_psu_attrs[] = { + /* psu attributes */ + DECLARE_PSU_ATTR(), + NULL +}; +static struct attribute_group as7327_56x_psu_group = { + .attrs = as7327_56x_psu_attrs, +}; + +const struct attribute_group *as7327_56x_psu_groups[][2] = { + {&as7327_56x_psu_group, NULL}, + {&as7327_56x_psu_group, NULL} +}; + +/* Functions to talk to the IPMI layer */ + +/* Initialize IPMI address, message buffers and user data */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface) +{ + int err; + + init_completion(&ipmi->read_complete); + + /* Initialize IPMI address */ + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + + /* Initialize message buffers */ + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = IPMI_NETFN; + + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + /* Create IPMI messaging interface user */ + 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", ipmi->interface); + return -EACCES; + } + + return 0; +} + +/* Send an IPMI command */ +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) + goto addr_err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + goto ipmi_req_err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + goto ipmi_timeout_err; + + return 0; + +ipmi_timeout_err: + err = -ETIMEDOUT; + pr_err("request_timeout=%x\n", err); + return err; +ipmi_req_err: + pr_err("request_settime=%x\n", err); + return err; +addr_err: + pr_err("validate_addr=%x\n", err); + return err; +} + +/* Send an IPMI command with retry */ +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)) { + pr_err("ipmi_send_message_%d err status(%d)\r\n", retry, status); + continue; + } + + if (unlikely(ipmi->rx_result != 0)) { + pr_err("ipmi_send_message_%d err result(%d)\r\n", retry, ipmi->rx_result); + continue; + } + + break; + } + + return status; +} + +/* Dispatch IPMI messages to callers */ +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) { + pr_notice("Mismatch between received msgid " + "(%02x) and transmitted msgid (%02x)!\n", + (int)msg->msgid, + (int)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 as7327_56x_psu_update_present(struct device *dev, unsigned char pid) +{ + int status = 0; + static unsigned long last_updated = 0; + + if (time_before(jiffies, last_updated + HZ * 5)) + return status; + + status = as7327_56x_cpld_read(0x62, 0x1d); + + last_updated = jiffies; + + if (status < 0) { + dev_warn(&data->pdev[pid]->dev, "cpld reg 0x62 err %d\n", status); + } + else { + data->psu_data[PSU_1].present = IS_PRESENT(PSU_1, status); + data->psu_data[PSU_2].present = IS_PRESENT(PSU_2, status); + } + return status; +} + +static int as7327_56x_psu_update_device(struct device *dev, unsigned char pid) +{ + int status = 0; + + if (time_before(jiffies, data->last_updated[pid] + HZ + HZ / 2)) + goto exit; + + status = as7327_56x_psu_update_present(dev, pid); + + if (unlikely(status < 0)) { + status = -EIO; + goto exit; + } + + if (data->psu_data[pid].present == 0) { + status = -ENXIO; + goto exit; + } + + data->ipmi_tx_data[0] = pid + 1; /* PSU ID base id for ipmi start from 1 */ + + /* get psu alarm */ + data->ipmi_tx_data[2] = POWER_STATUS; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].status, + sizeof(data->psu_data[pid].status)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu vin */ + data->ipmi_tx_data[2] = POWER_INPUT_VOLTAGE; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].vin, + sizeof(data->psu_data[pid].vin)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu vout */ + data->ipmi_tx_data[2] = POWER_OUTPUT_VOLTAGE; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].vout, + sizeof(data->psu_data[pid].vout)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu iin */ + data->ipmi_tx_data[2] = POWER_INPUT_CURRENT; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].iin, + sizeof(data->psu_data[pid].iin)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu iout */ + data->ipmi_tx_data[2] = POWER_OUTPUT_CURRENT; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].iout, + sizeof(data->psu_data[pid].iout)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu pin */ + data->ipmi_tx_data[2] = POWER_INPUT_POWER; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].pin, + sizeof(data->psu_data[pid].pin)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu pout */ + data->ipmi_tx_data[2] = POWER_OUTPUT_POWER; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].pout, + sizeof(data->psu_data[pid].pout)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu temp1 */ + data->ipmi_tx_data[2] = POWER_TEMPERATURE_1; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].temp[0], + sizeof(data->psu_data[pid].temp[0])); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu temp2 */ + data->ipmi_tx_data[2] = POWER_TEMPERATURE_2; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].temp[1], + sizeof(data->psu_data[pid].temp[1])); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu temp3 */ + data->ipmi_tx_data[2] = POWER_TEMPERATURE_3; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].temp[2], + sizeof(data->psu_data[pid].temp[2])); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get fan speed */ + data->ipmi_tx_data[2] = POWER_FAN_SPEED; + status = ipmi_send_message(&data->ipmi, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].fan_speed, + sizeof(data->psu_data[pid].fan_speed)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_GET_PSU_ATTRIBUTE_DATA_CMD, data->ipmi_tx_data[2]); + + /* get psu mfr */ + data->ipmi_tx_data[2] = eeprom_psu_product_number; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_pn, + sizeof(data->psu_data[pid].mfr_pn)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->ipmi_tx_data[2] = eeprom_psu_serial_number; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_sn, + sizeof(data->psu_data[pid].mfr_sn)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->ipmi_tx_data[2] = eeprom_psu_vendor; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_id, + sizeof(data->psu_data[pid].mfr_id)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->ipmi_tx_data[2] = eeprom_psu_model_name; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_model, + sizeof(data->psu_data[pid].mfr_model)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->ipmi_tx_data[2] = eeprom_psu_hardware_revision; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_hwrev, + sizeof(data->psu_data[pid].mfr_hwrev)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->ipmi_tx_data[2] = eeprom_psu_date; + status = ipmi_send_message(&data->ipmi, IPMI_PSU_GET_EEPROM_CMD, + data->ipmi_tx_data, 3, + data->psu_data[pid].mfr_date, + sizeof(data->psu_data[pid].mfr_date)); + VALIDATE_IPMI_REQUEST_RV(status, IPMI_PSU_GET_EEPROM_CMD, data->ipmi_tx_data[2]); + + data->last_updated[pid] = jiffies; +exit: + return status; +} + +static ssize_t show_present(struct device *dev, struct device_attribute *da, + char *buf) +{ + int pid = ((struct platform_device *)dev_get_drvdata(dev))->id; + int status = 0; + + mutex_lock(&data->update_lock); + + status = as7327_56x_psu_update_present(dev, pid); + + if (status < 0) { + status = -EIO; + goto exit; + } + + status = sprintf(buf, "%d\n", data->psu_data[pid].present); + +exit: + mutex_unlock(&data->update_lock); + return status ; +} + +static ssize_t show_psu_alarm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int pid = ((struct platform_device *)dev_get_drvdata(dev))->id; + unsigned char value = 0; + unsigned int psu_status = 0; + int status; + + mutex_lock(&data->update_lock); + + status = as7327_56x_psu_update_device(dev, pid); + + if (unlikely(status != 0)) /* IO ERR */ + goto exit; + + VALIDATE_PRESENT_RETURN(pid); + + psu_status = (u32)data->psu_data[pid].status[0] << 24 | + (u32)data->psu_data[pid].status[1] << 16 | + (u32)data->psu_data[pid].status[2] << 8 | + (u32)data->psu_data[pid].status[3]; + + switch(attr->index) { + case PSU_POWER_GOOD: + value = !(psu_status & PSU_STATUS_POWER_GOOD_N); + break; + case PSU_POWER_ON: + value = !(psu_status & PSU_STATUS_OFF); + break; + case PSU_OVR_TEMP: + case PSU_TEMP_FAULT: + value = !!(psu_status & PSU_STATUS_TEMPERATURE); + break; + case PSU_FAN_FAULT: + value = !!(psu_status & PSU_STATUS_FANS); + break; + default: + dev_warn(&data->pdev[pid]->dev, "unknown attr id %d\n", attr->index); + status = sprintf(buf, "NA\n"); + goto exit; + }; + + status = sprintf(buf, "%d\n", value); +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_psu_fan(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int pid = ((struct platform_device *)dev_get_drvdata(dev))->id; + unsigned int value = 0; + unsigned char *ptr = NULL; + int status = 0; + + mutex_lock(&data->update_lock); + + status = as7327_56x_psu_update_device(dev, pid); + + if (unlikely(status != 0)) /* IO ERR */ + goto exit; + + VALIDATE_PRESENT_RETURN(pid); + + ptr = data->psu_data[pid].fan_speed; + value = (u32)ptr[0] << 24 | (u32)ptr[1] << 16 | (u32)ptr[2] << 8 | (u32)ptr[3]; + + switch(attr->index) { + case PSU_FAN_SPEED: + status = sprintf(buf, "%d\n", value); + break; + case PSU_FAN_DUTY_CYCLE: + value = value * 100 / PSU_MAX_FAN_SPEED; + value = (value > 100)? 100: value; + status = sprintf(buf, "%d\n", value); + break; + case PSU_FAN_DIRECTION: /* psu_fan_dir, 0=>F2B, 1=>B2F */ + if ((strncmp((data->psu_data[pid].mfr_model), "C1A-B0650-C", strlen("C1A-B0650-C")) == 0) || + (strncmp((data->psu_data[pid].mfr_model), "G1342-0800W", strlen("G1342-0800W")) == 0)) + status = sprintf(buf, "0\n"); + else + /* Unknown direction */ + status = sprintf(buf, "2\n"); + break; + default: + dev_err(&data->pdev[pid]->dev, "unknown attr id %d\n", attr->index); + status = -EINVAL;; + }; + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static ssize_t show_psu_status(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int pid = ((struct platform_device *)dev_get_drvdata(dev))->id; + unsigned int value = 0; + unsigned int multiplier = 1; + unsigned char *ptr = NULL; + int status; + mutex_lock(&data->update_lock); + + status = as7327_56x_psu_update_device(dev, pid); + + if (unlikely(status != 0)) /* IO ERR */ + goto exit; + + VALIDATE_PRESENT_RETURN(pid); + + switch(attr->index) { + case PSU_VIN: + ptr = data->psu_data[pid].vin; + break; + case PSU_VOUT: + ptr = data->psu_data[pid].vout; + break; + case PSU_IIN: + ptr = data->psu_data[pid].iin; + break; + case PSU_IOUT: + ptr = data->psu_data[pid].iout; + break; + case PSU_TEMP1_INPUT: + ptr = data->psu_data[pid].temp[0]; + break; + case PSU_TEMP2_INPUT: + ptr = data->psu_data[pid].temp[1]; + break; + case PSU_TEMP3_INPUT: + ptr = data->psu_data[pid].temp[2]; + break; + case PSU_PIN: + multiplier = 1000; + ptr = data->psu_data[pid].pin; + break; + case PSU_POUT: + multiplier = 1000; + ptr = data->psu_data[pid].pout; + break; + case PSU_POUT_MAX: + status = sprintf(buf, "650000\n"); + goto exit; + case PSU_FAN_SPEED: + ptr = data->psu_data[pid].fan_speed; + value = (u32)ptr[0] << 24 | (u32)ptr[1] << 16 | (u32)ptr[2] << 8 | (u32)ptr[3]; + status = sprintf(buf, "%d\n", value); + goto exit; + case PSU_FAN_DUTY_CYCLE: + ptr = data->psu_data[pid].fan_speed; + value = (u32)ptr[0] << 24 | (u32)ptr[1] << 16 | (u32)ptr[2] << 8 | (u32)ptr[3]; + value = value * 100 / PSU_MAX_FAN_SPEED; + value = (value > 100)? 100: value; + status = sprintf(buf, "%d\n", value); + goto exit; + default: + dev_err(&data->pdev[pid]->dev, "unknown attr id %d\n", attr->index); + status = sprintf(buf, "NA\n"); + goto exit; + }; + value = (u32)ptr[0] << 24 | (u32)ptr[1] << 16 | (u32)ptr[2] << 8 | (u32)ptr[3]; + + status = sprintf(buf, "%d\n", value/multiplier); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + + +static ssize_t show_psu_mfr(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int pid = ((struct platform_device *)dev_get_drvdata(dev))->id; + int status; + mutex_lock(&data->update_lock); + + status = as7327_56x_psu_update_device(dev, pid); + + if (unlikely(status != 0)) /* IO ERR */ + goto exit; + + VALIDATE_PRESENT_RETURN(pid); + + switch(attr->index) { + case PSU_PN: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_pn); + break; + case PSU_SN: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_sn); + break; + case PSU_ID: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_id); + break; + case PSU_MODEL: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_model); + break; + case PSU_REVISION: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_hwrev); + break; + case PSU_DATE: + status = sprintf(buf, "%s\n", data->psu_data[pid].mfr_date); + break; + case PSU_VIN_TYPE: + /* 0:Unknown 1:AC 2:DC*/ + if ((strncmp((data->psu_data[pid].mfr_model), "C1A-B0650-C", strlen("C1A-B0650-C")) == 0) || + (strncmp((data->psu_data[pid].mfr_model), "G1342-0800W", strlen("G1342-0800W")) == 0)) { + status = sprintf(buf, "1\n"); + } else { + status = sprintf(buf, "0\n"); + } + + break; + default: + dev_err(&data->pdev[pid]->dev, "unknown attr id %d\n", attr->index); + status = sprintf(buf, "NA\n"); + break; + } +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7327_56x_psu_probe(struct platform_device *pdev) +{ + int status = 0; + struct device *hwmon_dev = NULL; + + hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + pdev, as7327_56x_psu_groups[pdev->id]); + if (IS_ERR(hwmon_dev)) { + status = PTR_ERR(hwmon_dev); + return status; + } + + mutex_lock(&data->update_lock); + data->hwmon_dev[pdev->id] = hwmon_dev; + mutex_unlock(&data->update_lock); + + dev_info(&pdev->dev, "PSU%d device created\n", pdev->id + 1); + + return 0; +} + +static void as7327_56x_psu_remove(struct platform_device *pdev) +{ + mutex_lock(&data->update_lock); + if (data->hwmon_dev[pdev->id]) { + hwmon_device_unregister(data->hwmon_dev[pdev->id]); + data->hwmon_dev[pdev->id] = NULL; + } + mutex_unlock(&data->update_lock); +} + +static int __init as7327_56x_psu_init(void) +{ + int ret; + int i; + + data = kzalloc(sizeof(struct as7327_56x_psu_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7327_56x_psu_driver); + if (ret < 0) + goto dri_reg_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; + } + } + + /* Set up IPMI interface */ + ret = init_ipmi_data(&data->ipmi, 0); + if (ret) { + goto ipmi_err; + } + + return 0; + +ipmi_err: + while (i > 0) { + i--; + platform_device_unregister(data->pdev[i]); + } +dev_reg_err: + platform_driver_unregister(&as7327_56x_psu_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7327_56x_psu_exit(void) +{ + int i; + + ipmi_destroy_user(data->ipmi.user); + for (i = 0; i < NUM_OF_PSU; i++) { + platform_device_unregister(data->pdev[i]); + } + platform_driver_unregister(&as7327_56x_psu_driver); + kfree(data); +} + +MODULE_AUTHOR("Ray Huang "); +MODULE_DESCRIPTION("as7327_56x_psu_bmc driver"); +MODULE_LICENSE("GPL"); + +module_init(as7327_56x_psu_init); +module_exit(as7327_56x_psu_exit); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-thermal_bmc.c b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-thermal_bmc.c new file mode 100644 index 000000000..b7f051b9a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/src/x86-64-accton-as7327-56x-thermal_bmc.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) Ray Huang + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "as7327_56x_thermal_bmc" +#define IPMI_NETFN 0x36 +#define IPMI_THERMAL_READ_CMD 0x12 + +#define THERMAL_COUNT 3 +#define THERMAL_DATA_LEN 1 + +#define IPMI_TIMEOUT (5 * HZ) +#define IPMI_ERR_RETRY_TIMES 1 + +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 *attr, + char *buf); +static int as7327_56x_thermal_probe(struct platform_device *pdev); +static void as7327_56x_thermal_remove(struct platform_device *pdev); + +enum temp_data_index { + TEMP_ADDR, + TEMP_FAULT, + TEMP_INPUT, + TEMP_DATA_COUNT +}; + +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 as7327_56x_thermal_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex update_lock; + char valid[THERMAL_COUNT]; /* != 0 if registers are valid */ + unsigned long last_updated[THERMAL_COUNT]; /* In jiffies */ + char ipmi_resp[THERMAL_DATA_LEN]; + struct ipmi_data ipmi; + unsigned char ipmi_tx_data[2]; /* 0: thermal id, 1: dummy */ + unsigned char cached_temp[THERMAL_COUNT]; /* 0: temp1, 1: temp2. 2: temp3*/ +}; + +struct as7327_56x_thermal_data *data = NULL; + +static struct platform_driver as7327_56x_thermal_driver = { + .probe = as7327_56x_thermal_probe, + .remove = as7327_56x_thermal_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +enum as7327_56x_thermal_sysfs_attrs { + TEMP1_INPUT, // 0x4A + TEMP2_INPUT, // 0x4B + TEMP3_INPUT, // 0x4C +}; + +#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); + +static struct attribute *as7327_56x_thermal_attrs[] = { + DECLARE_THERMAL_ATTR(1), + DECLARE_THERMAL_ATTR(2), + DECLARE_THERMAL_ATTR(3), + NULL +}; +ATTRIBUTE_GROUPS(as7327_56x_thermal); + +/* Functions to talk to the IPMI layer */ + +/* Initialize IPMI address, message buffers and user data */ +static int init_ipmi_data(struct ipmi_data *ipmi, int iface, + struct device *dev) +{ + int err; + + init_completion(&ipmi->read_complete); + + /* Initialize IPMI address */ + ipmi->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + ipmi->address.channel = IPMI_BMC_CHANNEL; + ipmi->address.data[0] = 0; + ipmi->interface = iface; + + /* Initialize message buffers */ + ipmi->tx_msgid = 0; + ipmi->tx_message.netfn = IPMI_NETFN; + + ipmi->ipmi_hndlrs.ipmi_recv_hndl = ipmi_msg_handler; + + /* Create IPMI messaging interface user */ + 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; +} + +/* Send an IPMI command */ +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) + goto addr_err; + + ipmi->tx_msgid++; + err = ipmi_request_settime(ipmi->user, &ipmi->address, ipmi->tx_msgid, + &ipmi->tx_message, ipmi, 0, 0, 0); + if (err) + goto ipmi_req_err; + + err = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); + if (!err) + goto ipmi_timeout_err; + + return 0; + +ipmi_timeout_err: + err = -ETIMEDOUT; + dev_err(&data->pdev->dev, "request_timeout=%x\n", err); + return err; +ipmi_req_err: + dev_err(&data->pdev->dev, "request_settime=%x\n", err); + return err; +addr_err: + dev_err(&data->pdev->dev, "validate_addr=%x\n", err); + return err; +} + +/* Send an IPMI command with retry */ +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)) { + dev_err(&data->pdev->dev, "ipmi cmd(%x) err status(%d)\r\n", + cmd, status); + continue; + } + + if (unlikely(ipmi->rx_result != 0)) { + dev_err(&data->pdev->dev, "ipmi cmd(%x) err result(%d)\r\n", + cmd, ipmi->rx_result); + continue; + } + + break; + } + + return status; +} + +/* Dispatch IPMI messages to callers */ +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) { + dev_notice(&data->pdev->dev, "Mismatch between received msgid " + "(%02x) and transmitted msgid (%02x)!\n", + (int)msg->msgid, + (int)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 ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int status = 0; + int cel; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated[attr->index] + HZ * 5) || !data->valid[attr->index]) { + data->ipmi_tx_data[0] = attr->index + 1; // thermal index starts from 1 + + status = ipmi_send_message(&data->ipmi, IPMI_THERMAL_READ_CMD, + data->ipmi_tx_data, 1, + data->ipmi_resp, sizeof(data->ipmi_resp)); + if (unlikely(status != 0)) { + dev_err(&data->pdev->dev, "get temp idx %d failed. status=%d\n", data->ipmi_tx_data[0], status); + goto exit; + } + + if (unlikely(data->ipmi.rx_result != 0)) { + dev_err(&data->pdev->dev, "get temp idx %d failed. result=%d\n", data->ipmi_tx_data[0], data->ipmi.rx_result); + goto exit; + } + + data->cached_temp[attr->index] = data->ipmi_resp[0]; + data->last_updated[attr->index] = jiffies; + data->valid[attr->index] = 1; + } + + cel = data->cached_temp[attr->index] * 1000; + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", cel); + +exit: + mutex_unlock(&data->update_lock); + return status; +} + +static int as7327_56x_thermal_probe(struct platform_device *pdev) +{ + int status = 0; + + + mutex_lock(&data->update_lock); + + data->hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + NULL, as7327_56x_thermal_groups); + + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + return status; + } + + mutex_unlock(&data->update_lock); + + dev_info(&pdev->dev, "Device Created\n"); + + return status; +} + +static void as7327_56x_thermal_remove(struct platform_device *pdev) +{ + mutex_lock(&data->update_lock); + + if (data->hwmon_dev) { + hwmon_device_unregister(data->hwmon_dev); + data->hwmon_dev = NULL; + } + + mutex_unlock(&data->update_lock); +} + +static int __init as7327_56x_thermal_init(void) +{ + int ret; + + data = kzalloc(sizeof(struct as7327_56x_thermal_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto alloc_err; + } + + mutex_init(&data->update_lock); + + ret = platform_driver_register(&as7327_56x_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; + } + + /* Set up IPMI interface */ + 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(&as7327_56x_thermal_driver); +dri_reg_err: + kfree(data); +alloc_err: + return ret; +} + +static void __exit as7327_56x_thermal_exit(void) +{ + if (data) { + ipmi_destroy_user(data->ipmi.user); + platform_device_unregister(data->pdev); + platform_driver_unregister(&as7327_56x_thermal_driver); + kfree(data); + } +} + +MODULE_AUTHOR("Ray Huang "); +MODULE_DESCRIPTION("as7327_56x_thermal_bmc driver"); +MODULE_LICENSE("GPL"); + +module_init(as7327_56x_thermal_init); +module_exit(as7327_56x_thermal_exit); diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_monitor.py b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_monitor.py new file mode 100755 index 000000000..49fc2e2b3 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_monitor.py @@ -0,0 +1,959 @@ +#!/usr/bin/env python + +try: + import os + import sys + import syslog + import signal + import time + import glob + #import logging + #import logging.config +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + +############################################################################### + +FUNCTION_NAME = 'fan-control' +PRODUCT_NAME = 'SYSTEM' + +THERMAL_CONFIG_FILE = 'thermal_sensor_pid.config' +FAN_CONFIG_FILE = 'fan_sysfs.config' + +SLEEP_TIME = 10 # Loop interval in second. +RETRY_COUNT_MAX = 3 +TEMP_HISTORY_MAX = RETRY_COUNT_MAX + +TEST_MODE = False +DEBUG_MODE = False +SHOW_MODE = False + +NONE_VALUE = 'N/A' + +############################################################################### +# Thermal sensor. +THERMAL_NUM_MAX = 0 + +THERMAL_INIT_VAL = None +THERMAL_INVALID_VAL = -10000 + +THERMAL_VAL_MIN = -40 +THERMAL_VAL_MAX = 125 +THERMAL_BURST_MAX = 30 +THERMAL_HYSTERESIS = 3 + +THERMAL_FATAL_MAX = 2 + +THERMAL_CONFIG = {} +############################################################################### +# Fan sensor. +FAN_NUM_MAX = 2 + +FAN_PWM_MAX = 100 +FAN_PWM_MIN = 30 +FAN_PWM_DEFAULT = 50 + +FAN_RPM_LOW = 2500 # 10% +FAN_RPM_OFFSET_MAX = 10 + +FAN_ABNORMAL_MAX = 2 + +FAN_SPEED_CTRL_PATH = "/sys/bus/i2c/devices/157-0062/fan_duty_cycle_percentage" +FAN_WDT_CONFIG_PATH = "/sys/bus/i2c/devices/157-0062/fan_wdt_count" +FAN_WDT_CLEAR_PATH = "/sys/bus/i2c/devices/157-0062/fan_wdt_clear" + +FAN_CONFIG = {} + +FAN_NOT_PRESENT = 0 + +############################################################################### +# Alarm. +ALARM_STATE = {} + +# Thermal alarm. +THERMAL_ALARM_INACCESS = 'INACCESS' +THERMAL_ALARM_INVALID = 'INVALID' +THERMAL_ALARM_BURST = 'BURST' +THERMAL_ALARM_FATAL = 'FATAL' +THERMAL_ALARM_MAJOR = 'MAJOR' + +THERMAL_ALARM = [THERMAL_ALARM_INACCESS, THERMAL_ALARM_INVALID, THERMAL_ALARM_BURST, THERMAL_ALARM_FATAL, THERMAL_ALARM_MAJOR] + +# Fan alarm. +FAN_ALARM_ABSENT = 'ABSENT' +FAN_ALARM_LOW = 'LOW' +FAN_ALARM_OFFSET = 'OFFSET' + +FAN_ALARM = [FAN_ALARM_ABSENT, FAN_ALARM_LOW, FAN_ALARM_OFFSET] + +############################################################################### +# LOG. +# priorities (these are ordered) + +LOG_EMERG = 0 # system is unusable +LOG_ALERT = 1 # action must be taken immediately +LOG_CRIT = 2 # critical conditions +LOG_ERR = 3 # error conditions +LOG_WARNING = 4 # warning conditions +LOG_NOTICE = 5 # normal but significant condition +LOG_INFO = 6 # informational +LOG_DEBUG = 7 # debug-level messages + +''' +priority_names = { + "alert": LOG_ALERT, + "crit": LOG_CRIT, + "critical": LOG_CRIT, + "debug": LOG_DEBUG, + "emerg": LOG_EMERG, + "err": LOG_ERR, + "error": LOG_ERR, # DEPRECATED + "info": LOG_INFO, + "notice": LOG_NOTICE, + "panic": LOG_EMERG, # DEPRECATED + "warn": LOG_WARNING, # DEPRECATED + "warning": LOG_WARNING, +} +''' +priority_name = { + LOG_CRIT : "critical", + LOG_DEBUG : "debug", + LOG_WARNING : "warning", + LOG_INFO : "info", +} + + +def SYS_LOG(level, msg): + if TEST_MODE: + time_str = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + print('%s %s: %-12s %s' %(time_str, FUNCTION_NAME, priority_name[level].upper(), msg)) + else: + syslog.syslog(level, msg) + +def DBG_LOG(msg): + if DEBUG_MODE: + level = syslog.LOG_DEBUG + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_INFO(msg): + level = syslog.LOG_INFO + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_WARN(msg): + level = syslog.LOG_WARNING + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_CRITICAL(msg): + level = syslog.LOG_CRIT + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +############################################################################### + +def check_debug_flag(): + path_prefix = '/run/' + if TEST_MODE: + path_prefix = './test/' + return os.path.isfile(path_prefix + "fan-control.debug") + +def check_show_flag(): + path_prefix = '/run/' + if TEST_MODE: + path_prefix = './test/' + return os.path.isfile(path_prefix + "fan-control.show") + +############################################################################### + +def getstatusoutput(cmd): + if sys.version_info.major == 2: + # python2 + import commands + return commands.getstatusoutput( cmd ) + else: + # python3 + import subprocess + return subprocess.getstatusoutput( cmd ) + +############################################################################### + +def init_thermal_config(file_name): + # Exist ? + if not os.path.isfile(file_name): + SYS_LOG_CRITICAL('Not find the thermal config file "%s", exiting...' %(file_name)) + return -1, None + + # Open the file. + try: + file = open(file_name, 'r') + except ImportError as e: + SYS_LOG_CRITICAL('Can not read the thermal config file "%s", exiting...' %(file_name)) + return -1, None + + # Read the file data. + config_data = {} + line_num = 0 + for line in file.readlines(): + line = line.strip() + #SYS_LOG_INFO('config_data line: %s' %(line)) + + # Empty? + if len(line) == 0: + continue + # Comment? + if line[0] == '#': + continue + + # Config. + line_val_list = line.split() + line_val = {} + # name + name = line_val_list[0] + line_val[ 'name' ] = name + # sysfs node ? + sysfs = (line_val_list[1] != '0') + line_val[ 'sysfs' ] = sysfs + # set-point + line_val[ 'setpoint' ] = int(line_val_list[2]) + # MAJOR + line_val[ 'MAJOR' ] = int(line_val_list[3]) + # FATAL + line_val[ 'FATAL' ] = int(line_val_list[4]) + # Kp + line_val[ 'kp' ] = float(line_val_list[5]) + # Ki + line_val[ 'ki' ] = float(line_val_list[6]) + # Kd + line_val[ 'kd' ] = float(line_val_list[7]) + # sensor path, or command to get the sensor value. + if sysfs: + sensor_path = line_val_list[8] + line_val['sensor_path'] = sensor_path + # if TEST_MODE: + # line_val['sensor_path'] = './test' + sensor_path + SYS_LOG_INFO('thermal sensor "%s" sysfs path = "%s".' %(name, line_val[ 'sensor_path' ])) + else: + if 0: #TEST_MODE + line_val['command'] = 'echo 50.0' + else: + cmd = line.partition('"')[2] + line_val['command'] = cmd.strip('"') + SYS_LOG_INFO('thermal sensor "%s" command = "%s".' %(name, line_val[ 'command' ])) + + # last PWM + line_val[ 'last_pwm' ] = FAN_PWM_DEFAULT + # Temperature history. + line_val[ 'temp' ] = [] + line_val[ 'history' ] = [] + # Temperature change-point + line_val[ 'change_point' ] = None + + config_data[line_num] = line_val + line_num += 1 + + file.close() + return 0, config_data + + + +def init_fan_config(file_name): + # Exist ? + if not os.path.isfile(file_name): + SYS_LOG_CRITICAL('Not find the fan config file "%s", exiting...' %(file_name)) + return -1, None + + # Open the file. + try: + file = open(file_name, 'r') + except ImportError as e: + SYS_LOG_CRITICAL('Can not read the fan config file "%s", exiting...' %(file_name)) + return -1, None + + + fan_config = {} + prefix = '' + # if TEST_MODE: + # prefix = './test' + prefix + + + # Read the file data. + config_data = {} + line_num = 0 + for line in file.readlines(): + line = line.strip() + #SYS_LOG_INFO('config_data line: %s' %(line)) + + # Empty? + if len(line) == 0: + continue + # Comment? + if line[0] == '#': + continue + + # Config. + line_val_list = line.split() + line_val = {} + # name + line_val[ 'name' ] = line_val_list[0] + # sysfs node + line_val[ 'sysfs_path' ] = prefix + line_val_list[1] + sysfs_path = line_val[ 'sysfs_path' ] + # present + line_val[ 'sysfs_path_present' ] = sysfs_path + line_val_list[2] if line_val_list[2] != NONE_VALUE else None + + line_val[ 'present' ] = True + + config_data[line_num] = line_val + line_num += 1 + + config_data['present_count'] = 0 + config_data['abnormal_count'] = 0 + config_data['abnormal_list'] = [] + + file.close() + return 0, config_data + + + +def init_data(): + global THERMAL_NUM_MAX + global THERMAL_CONFIG, FAN_CONFIG + + prefix = '/lib/platform-config/current/onl/bin/' + if TEST_MODE: + prefix = './' + + # Init thermal config. + thermal_config_file = prefix + THERMAL_CONFIG_FILE + ret, THERMAL_CONFIG = init_thermal_config(thermal_config_file) + if ret != 0: + return ret + THERMAL_NUM_MAX = len(THERMAL_CONFIG) + SYS_LOG_INFO('THERMAL_CONFIG:') + for index in range(THERMAL_NUM_MAX): + SYS_LOG_INFO('%s' %(THERMAL_CONFIG[index])) + + fan_config_file = prefix + FAN_CONFIG_FILE + ret, FAN_CONFIG = init_fan_config(fan_config_file) + if ret != 0: + return ret + SYS_LOG_INFO('FAN_CONFIG:') + SYS_LOG_INFO('%s' %(FAN_CONFIG)) + + return 0 + + +def raising_alarm(sensor, alarm, value = None): + sensor_alarm = {} + if ALARM_STATE.get(sensor) == None: + ALARM_STATE[sensor] = { alarm : { 'raise' : 0, 'clear' : 0, 'value' : value }} + else: + if ALARM_STATE[sensor].get(alarm) == None: + ALARM_STATE[sensor][alarm] = { 'raise' : 0, 'clear': 0, 'value' : value } + ALARM_STATE[sensor][alarm]['raise'] += 1 + ALARM_STATE[sensor][alarm]['value'] = value + ALARM_STATE[sensor][alarm]['clear'] = 0 + + # clear other alarm. + alarm_raise_count = ALARM_STATE[sensor][alarm]['raise'] + if alarm_raise_count >= RETRY_COUNT_MAX: + for every_alarm in ALARM_STATE[sensor].keys(): + if every_alarm != alarm: + ALARM_STATE[sensor].pop(every_alarm) + + return alarm_raise_count + + +def clearing_alarm(sensor, alarm, value = None): + if ALARM_STATE.get(sensor) == None: + return 0 + if ALARM_STATE[sensor].get(alarm) == None: + return 0 + if ALARM_STATE[sensor][alarm]['raise'] <= 0: + return 0 + + ALARM_STATE[sensor][alarm]['clear'] += 1 + ALARM_STATE[sensor][alarm]['value'] = None + if ALARM_STATE[sensor][alarm]['raise'] < RETRY_COUNT_MAX: + ALARM_STATE[sensor][alarm]['raise'] = 0 + else: + if ALARM_STATE[sensor][alarm]['clear'] >= RETRY_COUNT_MAX: + ALARM_STATE[sensor][alarm]['raise'] = 0 + return ALARM_STATE[sensor][alarm]['clear'] + + +def get_alarm_count(sensor, alarm): + if ALARM_STATE.get(sensor) == None: + return 0 + if ALARM_STATE[sensor].get(alarm) == None: + return 0 + return ALARM_STATE[sensor][alarm]['raise'] + +def get_sensor_alarm(sensor): + alarm_list = [] + if ALARM_STATE.get(sensor) != None: + for alarm in ALARM_STATE[sensor].keys(): + if ALARM_STATE[sensor][alarm]['raise'] >= RETRY_COUNT_MAX: + alarm_list.append(alarm) + return alarm_list + +def check_thermal_sensor_alarm(index, temp, alarm, raising, RETRY_LIMIT = RETRY_COUNT_MAX, PWM_PROPOSAL = FAN_PWM_MAX): + pwm = None + old_pwm = FAN_PWM_MIN + + sensor = THERMAL_CONFIG[index] + sensor_name = sensor['name'] + # Old PWM. + last_pwm = sensor['last_pwm'] + + if raising: + count = raising_alarm(sensor_name, alarm, temp) + if count > 0 and count <= RETRY_LIMIT: + SYS_LOG_INFO('sensor "%s" is going to raise "%s"(%s), retrying count %d' %(sensor_name, alarm, str(temp), count)) + # Need more retry, so not to change PWM. + pwm = last_pwm + if count >= RETRY_LIMIT: + if count == RETRY_LIMIT: + ''' + # Customer log specification. + if alarm == THERMAL_ALARM_FATAL: + SYS_LOG_CRITICAL('TempRisingCriticalAlarm Report:The temperature of [%s] exceeded the critical limit.(currentValue(%s), thresholdValue(%s))' \ + %(sensor_name, str(temp), str(sensor[alarm]))) + elif alarm == THERMAL_ALARM_MAJOR: + SYS_LOG_WARN('TempRisingWarningAlarm Report:The temperature of [%s] exceeded the upper limit.(currentValue(%s), thresholdValue(%s))' + %(sensor_name, str(temp), str(sensor[alarm]))) + ''' + SYS_LOG_WARN('sensor "%s" raise alarm "%s"' %(sensor_name, alarm)) + if PWM_PROPOSAL == FAN_PWM_MAX: + SYS_LOG_CRITICAL('propose to set MAX PWM') + pwm = PWM_PROPOSAL + else: + count = clearing_alarm(sensor_name, alarm, temp) + if count > 0 and count <= RETRY_LIMIT: + pwm = last_pwm + SYS_LOG_INFO('sensor "%s" is going to clear "%s"(%s), retrying count %d' %(sensor_name, alarm, str(temp), count)) + if count >= RETRY_LIMIT: + if count == RETRY_LIMIT: + ''' + # Customer log specification. + if alarm == THERMAL_ALARM_FATAL: + SYS_LOG_WARN('TempRisingCriticalAlarm Cancel:The temperature of [%s] exceeded the critical limit.(currentValue(%s), thresholdValue(%s))' \ + %(sensor_name, str(temp), str(sensor[alarm]))) + elif alarm == THERMAL_ALARM_MAJOR: + SYS_LOG_WARN('TempRisingWarningAlarm Cancel:The temperature of [%s] exceeded the upper limit.(currentValue(%s), thresholdValue(%s))' + %(sensor_name, str(temp), str(sensor[alarm]))) + ''' + SYS_LOG_WARN('sensor "%s" clear alarm "%s"(%s)' %(sensor_name, alarm, str(temp))) + pwm = None + return pwm + + +def check_thermal_sensor_value(index, temp): + sensor = THERMAL_CONFIG[index] + sensor_name = sensor['name'] + + pwm = None + pwm_all = [] + + # INACCESS ? + alarm = THERMAL_ALARM_INACCESS + raise_alarm = (temp == None) + if raise_alarm and sensor_name == 'SFP': + # SFP may be inaccessible when system is booting up. + return FAN_PWM_MIN + pwm = check_thermal_sensor_alarm(index, temp, alarm, raise_alarm) + if raise_alarm: + DBG_LOG('sensor "%s" hit alarm "%s", value is "%s", propose PWM "%s"' %(sensor_name, alarm, str(temp), str(pwm))) + return pwm + if pwm != None: + pwm_all.append(pwm) + + # INVALID ? + alarm = THERMAL_ALARM_INVALID + raise_alarm = (temp < THERMAL_VAL_MIN or temp > THERMAL_VAL_MAX) + pwm = check_thermal_sensor_alarm(index, temp, alarm, raise_alarm) + if raise_alarm: + DBG_LOG('sensor "%s" hit alarm "%s", value is "%s", propose PWM "%s"' %(sensor_name, alarm, str(temp), str(pwm))) + return pwm + if pwm != None: + pwm_all.append(pwm) + + # BURST ? + alarm = THERMAL_ALARM_BURST + history = sensor['temp'] + #DBG_LOG('sensor "%s" history value %s' %(sensor_name, history)) + if len(history) >= TEMP_HISTORY_MAX: + old_temp_max = max(history) + old_temp_min = min(history) + raise_alarm = (old_temp_max > old_temp_min + THERMAL_BURST_MAX) + pwm = check_thermal_sensor_alarm(index, temp, alarm, raise_alarm) + if raise_alarm: + DBG_LOG('sensor "%s" hit alarm "%s", value is "%s", propose PWM "%s"' %(sensor_name, alarm, str(temp), str(pwm))) + return pwm + if pwm != None: + pwm_all.append(pwm) + + # FATAL + alarm_fatal = THERMAL_ALARM_FATAL + raise_alarm = (temp >= sensor[alarm_fatal]) + check_thermal_sensor_alarm(index, temp, alarm_fatal, raise_alarm, PWM_PROPOSAL = None) + if raise_alarm: + DBG_LOG('sensor "%s" hit alarm "%s", value is "%s"' %(sensor_name, alarm_fatal, str(temp))) + else: + # MAJOR + alarm_major = THERMAL_ALARM_MAJOR + raise_alarm = (temp >= sensor[alarm_major] and temp < sensor[alarm_fatal]) + check_thermal_sensor_alarm(index, temp, alarm_major, raise_alarm, PWM_PROPOSAL = None) + if raise_alarm: + DBG_LOG('sensor "%s" hit alarm "%s", value is "%s"' %(sensor_name, alarm_major, str(temp))) + + # Return the MAX PWM proposal. + if len(pwm_all) > 0: + pwm = max(pwm_all) + else: + pwm = None + + return pwm + + +def check_thermal_sensor_fatal(): + fatal_thermal = [] + alarm = 'FATAL' + for index in range(THERMAL_NUM_MAX): + thermal = THERMAL_CONFIG[index] + thermal_name = thermal['name'] + count = get_alarm_count(thermal_name, alarm) + if count >= RETRY_COUNT_MAX: + fatal_thermal.append(thermal_name) + DBG_LOG('sensor "%s" hit alarm "%s" count %d' %(thermal_name, alarm, count)) + if len(fatal_thermal) >= THERMAL_FATAL_MAX: + while True: + SYS_LOG_CRITICAL('resetting board since thermal sensor %s hit "%s" alarm!' %(fatal_thermal, alarm)) + if TEST_MODE: + break + time.sleep(3) + # reset board... + ret, output = getstatusoutput('sudo reboot') + SYS_LOG_CRITICAL('%d, "%s"' %(ret, output)) + time.sleep(300) + + +def get_thermal_sensor_value(index): + temp = None + name = THERMAL_CONFIG[index]['name'] + + # Get the thermal sensor. + cmd = 'timeout 3s ' + if THERMAL_CONFIG[index]['sysfs']: + sys_path = THERMAL_CONFIG[index]['sensor_path'] + cmd = cmd + 'cat %s' %(sys_path) + else: + cmd = cmd + THERMAL_CONFIG[index]['command'] + + ret, output = getstatusoutput(cmd) + DBG_LOG('read sensor "%s" output: "%s".' %(name, output)) + temp = None + if ret != 0 or len(output) == 0: + DBG_LOG('fail to read sensor "%s".' %(name)) + else: + try: + temp = int(float(output)) + except: + temp = 0 + + temp /= 1000 + + # History, but may be invalid. + THERMAL_CONFIG[index][ 'temp' ].insert(0, temp) + if len(THERMAL_CONFIG[index][ 'temp' ]) > TEMP_HISTORY_MAX: + old_temp = THERMAL_CONFIG[index][ 'temp' ].pop() + DBG_LOG('sensor "%s" history value = %s' %(name, THERMAL_CONFIG[index][ 'temp' ])) + return temp + + +CUSTOMER_FAN_ALARM_LOG = { + FAN_ALARM_ABSENT : ['0x10002001', 'Report:The fan module was pluged in/out.(Status=0 0-out 1-in.)', + 'Cancel:The fan module was pluged in/out.(Status=1 0-out 1-in.)'], + FAN_ALARM_LOW : ['0x10002002', 'Report:The fan module failed.(ErrCode=1, Reason=The Fan module status abnormal.)', + 'Cancel:The fan module failed.(ErrCode=0, Reason=The Fan module status abnormal.)'], + FAN_ALARM_OFFSET : ['0x10002002', 'Report:The fan module failed.(ErrCode=2, Reason=The Fan module status abnormal.)', + 'Cancel:The fan module failed.(ErrCode=0, Reason=The Fan module status abnormal.)'], + 'FAN_ABNORMAL_LIST' : ['Report:The fan module failed(Name={}, Reason= Two or more fans are faulty.)', + 'Cancel:The fan module failed(Name={}, Reason= Two or more fans are faulty.)' ], +} + +def check_fan_sensor_alarm(index, alarm, raising): + sensor = FAN_CONFIG[index] + sensor_name = sensor['name'] + + if raising: + count = raising_alarm(sensor_name, alarm) + if count <= RETRY_COUNT_MAX: + SYS_LOG_INFO('sensor "%s" is going to raise "%s", retrying count %d' %(sensor_name, alarm, count)) + if count >= RETRY_COUNT_MAX: + if count == RETRY_COUNT_MAX: + SYS_LOG_WARN('sensor "%s" raise alarm "%s"' %(sensor_name, alarm)) + + ''' + # Customer log specification. + SYS_LOG_WARN('%s|%s %s' %(CUSTOMER_FAN_ALARM_LOG[alarm][0], sensor_name, CUSTOMER_FAN_ALARM_LOG[alarm][1])) + ''' + else: + count = clearing_alarm(sensor_name, alarm) + if count > 0 and count <= RETRY_COUNT_MAX: + SYS_LOG_INFO('sensor "%s" is going to clear "%s", retrying count %d' %(sensor_name, alarm, count)) + if count >= RETRY_COUNT_MAX: + if count == RETRY_COUNT_MAX: + SYS_LOG_WARN('sensor "%s" clear alarm "%s"' %(sensor_name, alarm)) + + ''' + # Customer log specification. + SYS_LOG_WARN('%s|%s %s' %(CUSTOMER_FAN_ALARM_LOG[alarm][0], sensor_name, CUSTOMER_FAN_ALARM_LOG[alarm][2])) + ''' + raised = (get_alarm_count(sensor_name, alarm) >= RETRY_COUNT_MAX) + return raised + + +def check_fan_state(index, alarm , raising): + abnormal = False + if raising: + if check_fan_sensor_alarm(index, alarm, True): + # alarm raised. + abnormal = True + else: + abnormal = check_fan_sensor_alarm(index, alarm, False) + return abnormal + + +def check_all_fan_state(): + abnormal_fan = [] + for index in range(FAN_NUM_MAX): + name = FAN_CONFIG[index]['name'] + present = FAN_CONFIG[index]['present'] + + raise_alarm = False + abnormal = False + # ABSENT + if not raise_alarm: + alarm = FAN_ALARM_ABSENT + raise_alarm = not present + if check_fan_state(index, alarm, raise_alarm): + abnormal = True + + if abnormal: + abnormal_fan.append(name) + + return abnormal_fan + + +def get_fan_alarm_count(alarm): + total_count = 0 + for index in range(FAN_NUM_MAX): + name = FAN_CONFIG[index]['name'] + if get_alarm_count(name, alarm) >= RETRY_COUNT_MAX: + total_count += 1 + return total_count + + +def get_fan_pwm(): + val = None + cmd = "cat %s" %(FAN_SPEED_CTRL_PATH) + ret, output = getstatusoutput(cmd) + if (ret != 0) or (len(output) == 0): + val = 0 + else: + val = int(output) + return val + + +def set_fan_pwm(pwm): + cmd = 'echo %d > %s' %(pwm, FAN_SPEED_CTRL_PATH) + #SYS_LOG_INFO('setting %s PWM to %d by command "%s".' %(name, pwm, cmd)) + ret, output = getstatusoutput(cmd) + if ret != 0: + SYS_LOG_WARN('fail to set PWM to %d, %s.' %(pwm, output)) + return ret + + +def check_fan_pwm(pwm_expected): + match = True + pwm = get_fan_pwm() + if pwm != pwm_expected: + match = False + return match + + +def get_fan_present(index): + present = True + fan = FAN_CONFIG[index] + + val = None + cmd = "cat %s" %(fan['sysfs_path_present']) + ret, output = getstatusoutput(cmd) + if (ret != 0) or (len(output) == 0): + val = 0 + else: + val = int(output) + + if val == 0: + present = False + + if present == False: + # Not present. + fan['present'] = False + DBG_LOG('FAN%d is absent' %(index + 1)) + + return present + + +def check_fan_sensor(): + global FAN_CONFIG + pwm_proposal = FAN_PWM_MIN + present_count = 0 + + for index in range(FAN_NUM_MAX): + fan = FAN_CONFIG[index] + # present ? + present = get_fan_present(index) + if present == False: + # Not present. + fan['present'] = False + DBG_LOG('FAN%d is absent' %(index + 1)) + continue + # present. + fan['present'] = True + present_count += 1 + + FAN_CONFIG['present_count'] = present_count + DBG_LOG('FAN present count = [%d]' %(present_count)) + + abnormal_fan_list = check_all_fan_state() + abnormal_count = len(abnormal_fan_list) + DBG_LOG('FAN abnormal count = [%d]' %(abnormal_count)) + + + FAN_CONFIG['abnormal_list'] = abnormal_fan_list + FAN_CONFIG['abnormal_count'] = abnormal_count + + return pwm_proposal + + +def calc_pid_pwm(thermal_index, temp): + global THERMAL_CONFIG + sensor = THERMAL_CONFIG[thermal_index] + name = sensor['name'] + + Kp = sensor['kp'] + Ki = sensor['ki'] + Kd = sensor['kd'] + setpoint = sensor['setpoint'] + last_pwm = sensor['last_pwm'] + change_point_temp = sensor['change_point'] + + # Record real valid temperature history. + THERMAL_CONFIG[thermal_index][ 'history' ].insert(0, temp) + if len(THERMAL_CONFIG[thermal_index][ 'history' ]) > TEMP_HISTORY_MAX: + old_temp = THERMAL_CONFIG[thermal_index][ 'history' ].pop() + DBG_LOG('sensor "%s" valid history value = %s' %(name, THERMAL_CONFIG[thermal_index][ 'history' ])) + + last_temp1 = temp + if len(sensor['history']) > 1: + last_temp1 = sensor['history'][1] + last_temp2 = temp + if len(sensor['history']) > 2: + last_temp2 = sensor['history'][2] + + P = Kp * (temp - last_temp1) + I = Ki * (temp - setpoint) + D = Kd * (temp + last_temp2 - 2 * last_temp1) + pwm = int(last_pwm + P + I + D) + + if temp < setpoint: + # Force the thermal sensor not to affect PWM as long as its temperature is below "setpoint". + pwm = FAN_PWM_MIN + + # Hysteresis operation. + hyst_action = 'hyst' + if change_point_temp == None: + # init state. + change_point_temp = temp + THERMAL_HYSTERESIS + 1 + if (pwm <= last_pwm) and ((change_point_temp - THERMAL_HYSTERESIS) <= temp <= change_point_temp): + # Hyst state + DBG_LOG('%s: pwm = [%d] last_pwm = [%d] change_point = [%d] P = [%d] I = [%d] D = [%d]' \ + %(hyst_action, pwm, last_pwm, change_point_temp, P, I, D)) + # Not change PWM under Hyst state. + pwm = last_pwm + else: + # Non-Hyst state + hyst_action = 'calc' + THERMAL_CONFIG[thermal_index]['change_point'] = temp + DBG_LOG('%s: pwm = [%d] last_pwm = [%d] change_point = [%d] P = [%d] I = [%d] D = [%d]' \ + %(hyst_action, pwm, last_pwm, change_point_temp, P, I, D)) + + pwm = min(pwm, FAN_PWM_MAX) + pwm = max(pwm, FAN_PWM_MIN) + THERMAL_CONFIG[thermal_index]['last_pwm'] = pwm + return pwm + + +def set_all_fan_pwm(pwm, sensor): + if check_fan_pwm(pwm): + # No need to set the same PWM again. + return + SYS_LOG_INFO('setting ALL FAN PWM to %d, thermal sensor %s.' %(pwm, sensor)) + set_fan_pwm(pwm) + + +def show_raised_alarm(): + if not SHOW_MODE: + return + + SYS_LOG_INFO( "----------------------------------------------------------------------" ) + SYS_LOG_INFO("") + SYS_LOG_INFO( "%-16s %-16s %-24s %-12s" %('Raised alarm:', 'MODULE', 'ALARM', 'VALUE') ) + + print_sensor = None + for sensor in ALARM_STATE.keys(): + for alarm in ALARM_STATE[sensor].keys(): + raised_count = ALARM_STATE[sensor][alarm]['raise'] + if raised_count < RETRY_COUNT_MAX: + continue + if( print_sensor != sensor ): + SYS_LOG_INFO("%-16s %-16s %-24s [%s]" %('', sensor, alarm, raised_count)) + else: + SYS_LOG_INFO("%-16s %-16s %-24s [%s]" %('', '', alarm, raised_count)) + print_sensor = sensor + SYS_LOG_INFO("") + SYS_LOG_INFO( "----------------------------------------------------------------------" ) + + +def thermal_pid_fan_policy(): + pwm_sensor = [] + max_thermal_pwm = FAN_PWM_MIN + for index in range(THERMAL_NUM_MAX): + # Read the thermal sensor value. + temp = get_thermal_sensor_value(index) + + # Check thermal sensor value. + thermal_pwm = check_thermal_sensor_value(index, temp) + # Check FATAL thermal sensor. + + pwm = FAN_PWM_MIN + if thermal_pwm != None: + # No need to calculate the PID value. + pwm = thermal_pwm + else: + # PID + pwm = calc_pid_pwm(index, temp) + + # Record the sensor with the max PWM. + if pwm > max_thermal_pwm: + max_thermal_pwm = pwm + pwm_sensor = [THERMAL_CONFIG[index]['name'], str(temp)] + elif pwm == max_thermal_pwm: + pwm_sensor.append([THERMAL_CONFIG[index]['name'], str(temp)]) + + # Reset the board once two(or more) thermal sensors hit alarm 'FATAL'. + check_thermal_sensor_fatal() + + # Check the FAN sensor + fan_sensor_pwm = check_fan_sensor() + + # Set FMEA status. + #set_all_fmea_fan_status() + + # Choose the highest value. + pwm_proposal = max(max_thermal_pwm, fan_sensor_pwm) + DBG_LOG('propose NEW PWM = [%d]' %(pwm_proposal)) + + pwm_proposal = min(FAN_PWM_MAX, pwm_proposal) + pwm_proposal = max(FAN_PWM_MIN, pwm_proposal) + + # Set PWM. + set_all_fan_pwm(pwm_proposal, pwm_sensor) + + # Show all alarms by now. + show_raised_alarm() + +# time_out 0-42 sec +def set_fan_timeout(time_out): + if(time_out > 42): + time_out = 42 + timeout_count = int(float(time_out)/0.67) & 0x3f + cmd = "echo {} > {}".format(timeout_count, FAN_WDT_CONFIG_PATH) + status, output = getstatusoutput(cmd) + if status: + print(output) + +def clear_fan_count(): + clear_cmd = "echo 1 > {}".format(FAN_WDT_CLEAR_PATH) + status, output = getstatusoutput(clear_cmd) + if status: + print(output) + +def power_off(): + cmd = "sync;sync;sync;i2cset -f -y 157 0x62 0x1f 0x3" + status, output = getstatusoutput(cmd) + if status: + print(output) + +def power_down_strategy(): + present_count = 0 + retry_time = 3 + + while retry_time: + present_count = 0 + for index in range(FAN_NUM_MAX): + present = get_fan_present(index) + if(present == False): + present_count += 1 + if present_count >= FAN_NUM_MAX: + SYS_LOG_INFO("The fans are all pull out, check again, the check count{}".format(3-retry_time)) + retry_time -= 1 + else: + break + if retry_time == 0: + SYS_LOG_INFO("The fans are all pull out, system will power cycle") + power_off() + + +def main(argv): + global TEST_MODE, DEBUG_MODE, SHOW_MODE + # Check test mode flag. + if len(argv) > 1 : + if argv[1] == 'test': + TEST_MODE = True + + # Wait 60 seconds for system to finish the initialization. + if not TEST_MODE: + SYS_LOG_INFO('Waiting for system finish init...') + time.sleep(60) + SYS_LOG_INFO('Waiting for system finish init...done.') + + # Init + ret = init_data() + if ret != 0: + SYS_LOG_CRITICAL('Fail to init program data, %d, exiting...' %(ret)) + exit(ret) + + # Main loop. + set_fan_timeout(15) + while True: + # Check debug flag. + DEBUG_MODE = check_debug_flag() + SHOW_MODE = check_show_flag() + + thermal_pid_fan_policy() + clear_fan_count() + power_down_strategy() + # Sleep + DBG_LOG("fan monitor alive") + time.sleep(SLEEP_TIME) + + +if __name__ == '__main__': + main(sys.argv) + + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_sysfs.config b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_sysfs.config new file mode 100644 index 000000000..060612685 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/fan_sysfs.config @@ -0,0 +1,3 @@ +# name sysfs-path present +fan1 /sys/bus/i2c/devices/157-0062/ fan_present_1 +fan2 /sys/bus/i2c/devices/157-0062/ fan_present_2 diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/monitor_led.py b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/monitor_led.py new file mode 100755 index 000000000..029fbff3e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/monitor_led.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 Accton Technology Corporation +# +# 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 3 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, see . + +# ------------------------------------------------------------------ +# HISTORY: +# mm/dd/yyyy (A.D.) +# 7/2/2018: Jostar create for as7326-56x +# ------------------------------------------------------------------ + +try: + import os + import sys + import syslog + import signal + import time + import glob + #import logging + #import logging.config +except ImportError as e: + raise ImportError('%s - required module not found' % str(e)) + + +############################################################################### +# debug +FUNCTION_NAME = 'led-control' +PRODUCT_NAME = 'SYSTEM' + +SLEEP_TIME = 1 # Loop interval in second. +FAN_WARN_COUNT_MAX = 15 + +############################################################################### +# LOG. +# priorities (these are ordered) + +LOG_EMERG = 0 # system is unusable +LOG_ALERT = 1 # action must be taken immediately +LOG_CRIT = 2 # critical conditions +LOG_ERR = 3 # error conditions +LOG_WARNING = 4 # warning conditions +LOG_NOTICE = 5 # normal but significant condition +LOG_INFO = 6 # informational +LOG_DEBUG = 7 # debug-level messages + +''' +priority_names = { + "alert": LOG_ALERT, + "crit": LOG_CRIT, + "critical": LOG_CRIT, + "debug": LOG_DEBUG, + "emerg": LOG_EMERG, + "err": LOG_ERR, + "error": LOG_ERR, # DEPRECATED + "info": LOG_INFO, + "notice": LOG_NOTICE, + "panic": LOG_EMERG, # DEPRECATED + "warn": LOG_WARNING, # DEPRECATED + "warning": LOG_WARNING, +} +''' +priority_name = { + LOG_CRIT : "critical", + LOG_DEBUG : "debug", + LOG_WARNING : "warning", + LOG_INFO : "info", +} + + +def SYS_LOG(level, msg): + syslog.syslog(level, msg) + +def SYS_LOG_INFO(msg): + level = syslog.LOG_INFO + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_WARN(msg): + level = syslog.LOG_WARNING + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +def SYS_LOG_CRITICAL(msg): + level = syslog.LOG_CRIT + x = PRODUCT_NAME + ' ' + priority_name[level].upper() + ' : ' + msg + SYS_LOG(level, x) + +############################################################################### + +def getstatusoutput(cmd): + if sys.version_info.major == 2: + # python2 + import commands + return commands.getstatusoutput( cmd ) + else: + # python3 + import subprocess + return subprocess.getstatusoutput( cmd ) + +############################################################################### + +# Define +LED_DARK = 0 +LED_GREEN_ON = 1 +LED_AMBER_ON = 2 +LED_RED_ON = 3 +LED_GREEN_BLINK = 5 + +SYS_LED_PATH = '/sys/class/leds/accton_as7327_56x_led::sys/brightness' +SYS_LED_WDT_CLEAR_PATH = '/sys/bus/i2c/devices/157-0062/sysled_wdt_clear' + +PSU1_PRESENCE_PATH = '/sys/bus/i2c/devices/1-005a/psu_present' +PSU2_PRESENCE_PATH = '/sys/bus/i2c/devices/2-0059/psu_present' +PSU1_STATUS_PATH = '/sys/bus/i2c/devices/1-005a/psu_power_good' +PSU2_STATUS_PATH = '/sys/bus/i2c/devices/2-0059/psu_power_good' +PSU_PRESENT = 1 +PSU_OUT_OK = 1 +FAN_PRESENT = 1 + +FAN_NUM_MAX = 2 +FAN_STATUS_PATH = "/sys/bus/i2c/devices/157-0062/fan_present_{}" + +def get_psu_status(): + try: + psu1_cmd = 'cat '+PSU1_PRESENCE_PATH + psu2_cmd = 'cat '+PSU2_PRESENCE_PATH + ret, output = getstatusoutput(psu1_cmd) + psu1_present = int(output) + ret, output = getstatusoutput(psu2_cmd) + psu2_present = int(output) + if(psu1_present != PSU_PRESENT) or (psu1_present != PSU_PRESENT): + return -1 + psu1_sta_cmd = 'cat '+PSU1_STATUS_PATH + psu2_sta_cmd = 'cat '+PSU2_STATUS_PATH + ret, output = getstatusoutput(psu1_sta_cmd) + psu1_sta = int(output) + ret, output = getstatusoutput(psu2_sta_cmd) + psu2_sta = int(output) + if(psu1_sta != PSU_OUT_OK) or (psu2_sta != PSU_OUT_OK): + return -1 + except: + return -1 + return 0 + +def get_fan_status(): + try: + for index in range(1, FAN_NUM_MAX+1): + cmd = 'cat '+FAN_STATUS_PATH.format(index) + ret, output = getstatusoutput(cmd) + if(ret): + SYS_LOG_WARN(output) + fan_status = int(output) + if(fan_status != FAN_PRESENT): + return -1 + except: + return -1 + return 0 + +def get_sys_led(): + cmd = "cat "+SYS_LED_PATH + ret, output = getstatusoutput(cmd) + return int(output) + +def set_sys_led(led): + cmd = "echo {:d} > {}".format(led, SYS_LED_PATH) + ret, output = getstatusoutput(cmd) + return ret + +def clear_sys_led_wdt(): + cmd = "echo 1 > {}".format(SYS_LED_WDT_CLEAR_PATH) + ret, output = getstatusoutput(cmd) + return ret + +fan_warn_count = 0 +def sys_led_ctrl(): + global fan_warn_count + clear_sys_led_wdt() + psu_status = get_psu_status() + fan_status = get_fan_status() + cur_led = get_sys_led() + #SYS_LOG_INFO("psu_status = {}".format(psu_status)) + #SYS_LOG_INFO("fan_status = {}".format(fan_status)) + #SYS_LOG_INFO("cur_led = {}".format(cur_led)) + if(fan_status) : + if (fan_warn_count < FAN_WARN_COUNT_MAX): + fan_warn_count = fan_warn_count + 1 + else: + fan_warn_count = 0 + if(psu_status == 0) and (fan_warn_count < FAN_WARN_COUNT_MAX): + if(cur_led != LED_GREEN_ON): + set_sys_led(LED_GREEN_ON) + SYS_LOG_INFO("set sys led green") + else: + if(cur_led != LED_RED_ON): + set_sys_led(LED_RED_ON) + SYS_LOG_INFO("set sys led red on") + return 0 + +def main(argv): + # Wait 60 seconds for system to finish the initialization. + SYS_LOG_INFO('Waiting for system finish init...') + time.sleep(60) + SYS_LOG_INFO('Waiting for system finish init...done.') + SYS_LOG_INFO('Starting led monitor') + # Loop forever, doing something useful hopefully: + while True: + sys_led_ctrl() + time.sleep(SLEEP_TIME) + +if __name__ == '__main__': + main(sys.argv[1:]) + diff --git a/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/thermal_sensor_pid.config b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/thermal_sensor_pid.config new file mode 100644 index 000000000..043ae5f13 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/modules/builds/utils/thermal_sensor_pid.config @@ -0,0 +1,12 @@ +# name sysfs set-point MAJOR FATAL Kp Ki Kd sysfs/command + +COMe-bottom 1 39 70 75 4.0 0.25 0.1 /sys/bus/i2c/devices/7-004b/hwmon/hwmon2/temp1_input + +MAC-Around 1 62 70 75 4.0 0.3 0.1 /sys/bus/i2c/devices/7-004c/hwmon/hwmon3/temp1_input + +Air-Outlet 1 43 70 75 4.0 0.25 0.1 /sys/bus/i2c/devices/7-004a/hwmon/hwmon1/temp1_input + +MAC 0 80 105 115 4.0 0.3 0.2 "awk '{print ($1 * 1.25) + 10000}' /sys/bus/i2c/devices/7-004c/hwmon/hwmon3/temp1_input" + +COMe 1 78 95 102 4.0 0.3 0.15 /sys/class/thermal/thermal_zone0/temp + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/Makefile @@ -0,0 +1 @@ +include $(ONL)/make/pkg.mk \ No newline at end of file diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/PKG.yml b/packages/platforms/accton/x86-64/as7327-56x/onlp/PKG.yml new file mode 100644 index 000000000..5b6930e7e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/onlp-platform-any.yml PLATFORM=x86-64-accton-as7327-56x ARCH=amd64 TOOLCHAIN=x86_64-linux-gnu diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/Makefile new file mode 100644 index 000000000..e7437cb23 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/Makefile @@ -0,0 +1,2 @@ +FILTER=src +include $(ONL)/make/subdirs.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/lib/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/lib/Makefile new file mode 100644 index 000000000..3558f566e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/lib/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as7327-56x +include $(ONL)/packages/base/any/onlp/builds/platform/libonlp-platform.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/onlpdump/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/onlpdump/Makefile new file mode 100644 index 000000000..81055d9d4 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/onlpdump/Makefile @@ -0,0 +1,2 @@ +PLATFORM := x86-64-accton-as7327-56x +include $(ONL)/packages/base/any/onlp/builds/platform/onlps.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/.module b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/.module new file mode 100644 index 000000000..595773b1e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/.module @@ -0,0 +1 @@ +name: x86_64_accton_as7327_56x diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/Makefile new file mode 100644 index 000000000..7e28d0c98 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### +include $(ONL)/make/config.mk +MODULE := x86_64_accton_as7327_56x +AUTOMODULE := x86_64_accton_as7327_56x +include $(BUILDER)/definemodule.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/README b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/README new file mode 100644 index 000000000..5b4510b89 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/README @@ -0,0 +1,6 @@ +############################################################################### +# +# x86_64_accton_as7327_56x README +# +############################################################################### + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/make.mk b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/make.mk new file mode 100644 index 000000000..2e50e6129 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# x86_64_accton_as7327_56x Autogeneration +# +############################################################################### +x86_64_accton_as7327_56x_AUTO_DEFS := module/auto/x86_64_accton_as7327_56x.yml +x86_64_accton_as7327_56x_AUTO_DIRS := module/inc/x86_64_accton_as7327_56x module/src +include $(BUILDER)/auto.mk + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/x86_64_accton_as7327_56x.yml b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/x86_64_accton_as7327_56x.yml new file mode 100644 index 000000000..987e92639 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/auto/x86_64_accton_as7327_56x.yml @@ -0,0 +1,50 @@ +############################################################################### +# +# X86_64_ACCTON_AS7327_56X Autogeneration Definitions. +# +############################################################################### + +cdefs: &cdefs +- X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING: + doc: "Include or exclude logging." + default: 1 +- X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT: + doc: "Default enabled log options." + default: AIM_LOG_OPTIONS_DEFAULT +- X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT: + doc: "Default enabled log bits." + default: AIM_LOG_BITS_DEFAULT +- X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT: + doc: "Default enabled custom log bits." + default: 0 +- X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB: + doc: "Default all porting macros to use the C standard libraries." + default: 1 +- X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS: + doc: "Include standard library headers for stdlib porting macros." + default: X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB +- X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI: + doc: "Include generic uCli support." + default: 0 +- X86_64_ACCTON_AS7327_56X_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_AS7327_56X_CONFIG_HEADER: + defs: *cdefs + basename: X86_64_ACCTON_AS7327_56X_CONFIG + + portingmacro: + X86_64_ACCTON_AS7327_56X: + macros: + - malloc + - free + - memset + - memcpy + + - vsnprintf + - snprintf + - strlen diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x.x b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x.x new file mode 100644 index 000000000..efa8a5b7a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x.x @@ -0,0 +1,14 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.xmacro(ALL).define> */ +/* */ + +/* <--auto.start.xenum(ALL).define> */ +/* */ + + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_config.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_config.h new file mode 100644 index 000000000..4a3aec01b --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_config.h @@ -0,0 +1,137 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as7327_56x Configuration Header + * + * @addtogroup x86_64_accton_as7327_56x-config + * @{ + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7327_56x_CONFIG_H__ +#define __x86_64_accton_as7327_56x_CONFIG_H__ + +#ifdef GLOBAL_INCLUDE_CUSTOM_CONFIG +#include +#endif +#ifdef X86_64_ACCTON_AS7327_56X_INCLUDE_CUSTOM_CONFIG +#include +#endif + +/* */ +#include +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING + * + * Include or exclude logging. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING +#define X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING 1 +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT + * + * Default enabled log options. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT +#define X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT AIM_LOG_OPTIONS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT + * + * Default enabled log bits. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT +#define X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT AIM_LOG_BITS_DEFAULT +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + * + * Default enabled custom log bits. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT +#define X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT 0 +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB + * + * Default all porting macros to use the C standard libraries. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB +#define X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB 1 +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + * + * Include standard library headers for stdlib porting macros. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS +#define X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI + * + * Include generic uCli support. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI +#define X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI 0 +#endif + +/** + * X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + * + * Assume chassis fan direction is the same as the PSU fan direction. */ + + +#ifndef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION +#define X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION 0 +#endif + + + +/** + * All compile time options can be queried or displayed + */ + +/** Configuration settings structure. */ +typedef struct X86_64_ACCTON_AS7327_56X_CONFIG_settings_s { + /** name */ + const char* name; + /** value */ + const char* value; +} X86_64_ACCTON_AS7327_56X_CONFIG_settings_t; + +/** Configuration settings table. */ +/** X86_64_ACCTON_AS7327_56X_CONFIG_settings table. */ +extern X86_64_ACCTON_AS7327_56X_CONFIG_settings_t X86_64_ACCTON_AS7327_56X_CONFIG_settings[]; + +/** + * @brief Lookup a configuration setting. + * @param setting The name of the configuration option to lookup. + */ +const char* _x86_64__a_c_c_t_o_n__as7327_56x__c_o_n_f_i_g_lookup(const char* setting); + +/** + * @brief Show the compile-time configuration. + * @param pvs The output stream. + */ +int _x86_64__a_c_c_t_o_n__as7327_56x__c_o_n_f_i_g_show(struct aim_pvs_s* pvs); + +/* */ + +#include "x86_64_accton_as7327_56x_porting.h" + +#endif /* __X86_64_ACCTON_AS7327_56X_CONFIG_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_dox.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_dox.h new file mode 100644 index 000000000..d7347b45b --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_dox.h @@ -0,0 +1,26 @@ +/**************************************************************************//** + * + * x86_64_accton_as7327_56x Doxygen Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7327_56x_DOX_H__ +#define __x86_64_accton_as7327_56x_DOX_H__ + +/** + * @defgroup x86_64_accton_as7327_56x x86_64_accton_as7327_56x - x86_64_accton_as7327_56x Description + * + +The documentation overview for this module should go here. + + * + * @{ + * + * @defgroup x86_64_accton_as7327_56x-x86_64_accton_as7327_56x Public Interface + * @defgroup x86_64_accton_as7327_56x-config Compile Time Configuration + * @defgroup x86_64_accton_as7327_56x-porting Porting Macros + * + * @} + * + */ + +#endif /* __x86_64_accton_as7327_56x_DOX_H__ */ diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_porting.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_porting.h new file mode 100644 index 000000000..430e00b9e --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/inc/x86_64_accton_as7327_56x/x86_64_accton_as7327_56x_porting.h @@ -0,0 +1,97 @@ +/**************************************************************************//** + * + * @file + * @brief x86_64_accton_as7327_56x Porting Macros. + * + * @addtogroup x86_64_accton_as7327_56x-porting + * @{ + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7327_56x_PORTING_H__ +#define __x86_64_accton_as7327_56x_PORTING_H__ + + +/* */ +#if X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS == 1 +#include +#include +#include +#include +#include +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_MALLOC + #if defined(GLOBAL_MALLOC) + #define X86_64_ACCTON_AS7327_56X_MALLOC GLOBAL_MALLOC + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_MALLOC malloc + #else + #error The macro X86_64_ACCTON_AS7327_56X_MALLOC is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_FREE + #if defined(GLOBAL_FREE) + #define X86_64_ACCTON_AS7327_56X_FREE GLOBAL_FREE + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_FREE free + #else + #error The macro X86_64_ACCTON_AS7327_56X_FREE is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_MEMSET + #if defined(GLOBAL_MEMSET) + #define X86_64_ACCTON_AS7327_56X_MEMSET GLOBAL_MEMSET + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_MEMSET memset + #else + #error The macro X86_64_ACCTON_AS7327_56X_MEMSET is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_MEMCPY + #if defined(GLOBAL_MEMCPY) + #define X86_64_ACCTON_AS7327_56X_MEMCPY GLOBAL_MEMCPY + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_MEMCPY memcpy + #else + #error The macro X86_64_ACCTON_AS7327_56X_MEMCPY is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_VSNPRINTF + #if defined(GLOBAL_VSNPRINTF) + #define X86_64_ACCTON_AS7327_56X_VSNPRINTF GLOBAL_VSNPRINTF + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_VSNPRINTF vsnprintf + #else + #error The macro X86_64_ACCTON_AS7327_56X_VSNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_SNPRINTF + #if defined(GLOBAL_SNPRINTF) + #define X86_64_ACCTON_AS7327_56X_SNPRINTF GLOBAL_SNPRINTF + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_SNPRINTF snprintf + #else + #error The macro X86_64_ACCTON_AS7327_56X_SNPRINTF is required but cannot be defined. + #endif +#endif + +#ifndef X86_64_ACCTON_AS7327_56X_STRLEN + #if defined(GLOBAL_STRLEN) + #define X86_64_ACCTON_AS7327_56X_STRLEN GLOBAL_STRLEN + #elif X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB == 1 + #define X86_64_ACCTON_AS7327_56X_STRLEN strlen + #else + #error The macro X86_64_ACCTON_AS7327_56X_STRLEN is required but cannot be defined. + #endif +#endif + +/* */ + + +#endif /* __x86_64_accton_as7327_56x_PORTING_H__ */ +/* @} */ diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/make.mk b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/make.mk new file mode 100644 index 000000000..cf493ea95 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/make.mk @@ -0,0 +1,10 @@ +############################################################################### +# +# +# +############################################################################### +THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +x86_64_accton_as7327_56x_INCLUDES := -I $(THIS_DIR)inc +x86_64_accton_as7327_56x_INTERNAL_INCLUDES := -I $(THIS_DIR)src +x86_64_accton_as7327_56x_DEPENDMODULE_ENTRIES := init:x86_64_accton_as7327_56x ucli:x86_64_accton_as7327_56x + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/Makefile b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/Makefile new file mode 100644 index 000000000..2e8d81941 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/Makefile @@ -0,0 +1,9 @@ +############################################################################### +# +# Local source generation targets. +# +############################################################################### + +ucli: + @../../../../tools/uclihandlers.py x86_64_accton_as7327_56x_ucli.c + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/debug.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/debug.c new file mode 100644 index 000000000..37d78ee90 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/debug.c @@ -0,0 +1,45 @@ +#include "x86_64_accton_as7327_56x_int.h" + +#if x86_64_accton_as7327_56x_CONFIG_INCLUDE_DEBUG == 1 + +#include + +static char help__[] = + "Usage: debug [options]\n" + " -c CPLD Versions\n" + " -h Help\n" + ; + +int +x86_64_accton_as7327_56x_debug_main(int argc, char* argv[]) +{ + int c = 0; + int help = 0; + int rv = 0; + + while( (c = getopt(argc, argv, "ch")) != -1) { + switch(c) + { + case 'c': c = 1; break; + case 'h': help = 1; rv = 0; break; + default: help = 1; rv = 1; break; + } + + } + + if(help || argc == 1) { + printf("%s", help__); + return rv; + } + + if(c) { + printf("Not implemented.\n"); + } + + + return 0; +} + +#endif + + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/fani.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/fani.c new file mode 100644 index 000000000..370adc55b --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/fani.c @@ -0,0 +1,463 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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 Defaults. + * + ***********************************************************/ +#include +#include +#include "platform_lib.h" + +#define PSU_PREFIX_PATH "/sys/bus/i2c/devices/" + +/* +Although in HW spec. + Offset 0x43 FAN1_FRONT_MAX_SET_SPEED 0xDC = 220 + Offset 0x4B FAN1_REAR_MAX_SET_SPEED 0xB6 = 182 +But we refer to muxi's fani.c. +*/ +#define MAX_FRONT_FAN_SPEED 30000 +#define MAX_REAR_FAN_SPEED 25000 +#define MAX_PSU_FAN_SPEED 23000 + +#define XSTR(s) STR(s) +#define STR(s) #s + +enum fan_id { + FAN_BOX1_FRONT_1 = 1, + FAN_BOX1_FRONT_2, + FAN_BOX2_FRONT_3, + FAN_BOX2_FRONT_4, + FAN_BOX1_REAR_1, + FAN_BOX1_REAR_2, + FAN_BOX2_REAR_3, + FAN_BOX2_REAR_4, + FAN_1_ON_PSU_1, + FAN_1_ON_PSU_2 +}; + +#define CHASSIS_FAN_INFO(boxid, location, fid) \ +{ \ + { ONLP_FAN_ID_CREATE(FAN_BOX##boxid##_##location##_##fid), "Chassis Fan - FAN_BOX" XSTR(boxid) "_" XSTR(location) " " XSTR(fid), 0 }, 0x0,\ + ONLP_FAN_CAPS_SET_PERCENTAGE | ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ +} + +#define PSU_FAN_INFO(pid, fid) \ + { \ + { ONLP_FAN_ID_CREATE(FAN_##fid##_ON_PSU_##pid), "PSU "#pid" - Fan "#fid, 0 },\ + 0x0,\ + ONLP_FAN_CAPS_GET_RPM | ONLP_FAN_CAPS_GET_PERCENTAGE,\ + 0,\ + 0,\ + ONLP_FAN_MODE_INVALID,\ + } + +/* Static fan information */ +onlp_fan_info_t finfo[] = { + { }, /* Not used */ + CHASSIS_FAN_INFO(1,FRONT,1), + CHASSIS_FAN_INFO(1,FRONT,2), + CHASSIS_FAN_INFO(2,FRONT,3), + CHASSIS_FAN_INFO(2,FRONT,4), + CHASSIS_FAN_INFO(1,REAR,1), + CHASSIS_FAN_INFO(1,REAR,2), + CHASSIS_FAN_INFO(2,REAR,3), + CHASSIS_FAN_INFO(2,REAR,4), + PSU_FAN_INFO(1,1), + PSU_FAN_INFO(2,1) + }; + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_FAN(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +static int +_onlp_fani_info_get_fan_on_psu(int pid, onlp_fan_info_t* info) +{ + int val = 0; + char *basepath; + + info->status |= ONLP_FAN_STATUS_PRESENT; + + if (BMC_IS_ENABLED()) { + if (pid == PSU1_ID) + basepath = PSU1_BMC_BASE_PATH; + else if (pid == PSU2_ID) + basepath = PSU2_BMC_BASE_PATH; + else + return ONLP_STATUS_E_INVALID; + + /* Get power good status */ + if (psu_bmc_info_get(basepath, "psu_power_good", &val) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", pid); + } + + if(val == PSU_STATUS_POWER_GOOD) { + /* get fan direction */ + if (psu_bmc_info_get(basepath, "psu_fan1_dir", &val) == ONLP_STATUS_OK) { + if (val == 0) + info->status |= ONLP_FAN_STATUS_F2B; + else if (val == 1) + info->status |= ONLP_FAN_STATUS_B2F; + } + + /* get fan fault status */ + if (psu_bmc_info_get(basepath, "psu_fan1_fault", &val) == ONLP_STATUS_OK) { + info->status |= (val > 0) ? ONLP_FAN_STATUS_FAILED : 0; + } + + /* get fan speed */ + if (psu_bmc_info_get(basepath, "psu_fan1_speed", &val) == ONLP_STATUS_OK) { + info->rpm = val; + info->percentage = (info->rpm * 100) / MAX_PSU_FAN_SPEED; + } + } else { + info->status |= ONLP_FAN_STATUS_FAILED; + info->rpm = 0; + info->percentage = 0; + } + + } else { + /* Get power good status */ + if (psu_pmbus_info_get(pid, "psu_power_good", &val) != 0) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", pid); + } + + if (val == PSU_STATUS_POWER_GOOD) { + /* get fan direction */ + if (psu_pmbus_info_get(pid, "psu_fan_dir", &val) == ONLP_STATUS_OK) { + info->status |= val ? ONLP_FAN_STATUS_B2F : ONLP_FAN_STATUS_F2B; + } + + /* get fan fault status */ + if (psu_pmbus_info_get(pid, "psu_fan1_fault", &val) == ONLP_STATUS_OK) { + info->status |= (val > 0) ? ONLP_FAN_STATUS_FAILED : 0; + } + + /* get fan speed */ + if (psu_pmbus_info_get(pid, "psu_fan1_speed_rpm", &val) == ONLP_STATUS_OK) { + info->rpm = val; + info->percentage = (info->rpm * 100) / MAX_PSU_FAN_SPEED; + } + } else { + info->status |= ONLP_FAN_STATUS_FAILED; + info->rpm = 0; + info->percentage = 0; + } + } + return ONLP_STATUS_OK; +} + + static int + _onlp_fani_info_get_fan(int fid, onlp_fan_info_t* info) + { + int rc = ONLP_STATUS_OK; + int value; + char path[64] = {0}; + char path_front[64] = {0}; + char path_rear[64] = {0}; + + /* get fan present status */ + + switch (fid) + { + case FAN_BOX1_FRONT_1: + case FAN_BOX1_FRONT_2: + case FAN_BOX1_REAR_1: + case FAN_BOX1_REAR_2: + sprintf(path, "%s""fan_present_%d", CPLD_NODE_PATH, 1); + break; + case FAN_BOX2_FRONT_3: + case FAN_BOX2_FRONT_4: + case FAN_BOX2_REAR_3: + case FAN_BOX2_REAR_4: + sprintf(path, "%s""fan_present_%d", CPLD_NODE_PATH, 2); + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + DEBUG_PRINT("Fan(%d), present path = (%s)", fid, path); + + if (onlp_file_read_int(&value, path) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + info->status |= value ? ONLP_FAN_STATUS_PRESENT : 0; + + /* get fan direction (both : the same) + */ + switch (fid) + { + case FAN_BOX1_FRONT_1: + case FAN_BOX1_FRONT_2: + case FAN_BOX1_REAR_1: + case FAN_BOX1_REAR_2: + sprintf(path, "%s""fan_direction_%d", CPLD_NODE_PATH, 1); + break; + case FAN_BOX2_FRONT_3: + case FAN_BOX2_FRONT_4: + case FAN_BOX2_REAR_3: + case FAN_BOX2_REAR_4: + sprintf(path, "%s""fan_direction_%d", CPLD_NODE_PATH, 2); + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + DEBUG_PRINT("Fan(%d), direction path = (%s)", fid, path); + + if (onlp_file_read_int(&value, path) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + /* F2B has register value 0 as per x86-64-accton-as7327-56x-fan.c */ + info->status |= value ? ONLP_FAN_STATUS_B2F : ONLP_FAN_STATUS_F2B; + + /* get front fan speed + */ + switch (fid) + { + case FAN_BOX1_FRONT_1: + sprintf(path_front, "%s""fan_front_speed_rpm_%d", CPLD_NODE_PATH, 1); + break; + case FAN_BOX1_REAR_1: + sprintf(path_rear, "%s""fan_rear_speed_rpm_%d", CPLD_NODE_PATH, 1); + break; + case FAN_BOX1_FRONT_2: + sprintf(path_front, "%s""fan_front_speed_rpm_%d", CPLD_NODE_PATH, 2); + break; + case FAN_BOX1_REAR_2: + sprintf(path_rear, "%s""fan_rear_speed_rpm_%d", CPLD_NODE_PATH, 2); + break; + case FAN_BOX2_FRONT_3: + sprintf(path_front, "%s""fan_front_speed_rpm_%d", CPLD_NODE_PATH, 3); + break; + case FAN_BOX2_REAR_3: + sprintf(path_rear, "%s""fan_rear_speed_rpm_%d", CPLD_NODE_PATH, 3); + break; + case FAN_BOX2_FRONT_4: + sprintf(path_front, "%s""fan_front_speed_rpm_%d", CPLD_NODE_PATH, 4); + break; + case FAN_BOX2_REAR_4: + sprintf(path_rear, "%s""fan_rear_speed_rpm_%d", CPLD_NODE_PATH, 4); + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + switch (fid) + { + case FAN_BOX1_FRONT_1: + case FAN_BOX1_FRONT_2: + case FAN_BOX2_FRONT_3: + case FAN_BOX2_FRONT_4: + if (onlp_file_read_int(&value, path_front) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path_front); + return ONLP_STATUS_E_INTERNAL; + } + info->rpm = value; + info->percentage = (info->rpm * 100) / MAX_FRONT_FAN_SPEED; + break; + case FAN_BOX1_REAR_1: + case FAN_BOX1_REAR_2: + case FAN_BOX2_REAR_3: + case FAN_BOX2_REAR_4: + if (onlp_file_read_int(&value, path_rear) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path_rear); + return ONLP_STATUS_E_INTERNAL; + } + info->rpm = value; + info->percentage = (info->rpm * 100) / MAX_REAR_FAN_SPEED; + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + return rc; + } + + /* + * This function will be called prior to all of onlp_fani_* functions. + */ + int + onlp_fani_init(void) + { + return ONLP_STATUS_OK; + } + + int + onlp_fani_info_get(onlp_oid_t id, onlp_fan_info_t* info) + { + int rc = 0; + int fid; + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + *info = finfo[fid]; + + initialize_bmc_status(); + + info->status = 0; + + switch (fid) + { + case FAN_1_ON_PSU_1: + rc = _onlp_fani_info_get_fan_on_psu(PSU1_ID, info); + break; + case FAN_1_ON_PSU_2: + rc = _onlp_fani_info_get_fan_on_psu(PSU2_ID, info); + break; + case FAN_BOX1_FRONT_1: + case FAN_BOX1_FRONT_2: + case FAN_BOX1_REAR_1: + case FAN_BOX1_REAR_2: + case FAN_BOX2_FRONT_3: + case FAN_BOX2_FRONT_4: + case FAN_BOX2_REAR_3: + case FAN_BOX2_REAR_4: + if (BMC_IS_ENABLED()) { + info->caps &= ~ONLP_FAN_CAPS_SET_PERCENTAGE; + } + rc =_onlp_fani_info_get_fan(fid, info); + break; + default: + rc = ONLP_STATUS_E_INVALID; + break; + } + + return rc; + } + + /* + * This function sets the speed of the given fan in RPM. + * + * This function will only be called if the fan supprots the RPM_SET + * capability. + * + * It is optional if you have no fans at all with this feature. + */ + int + onlp_fani_rpm_set(onlp_oid_t id, int rpm) + { + return ONLP_STATUS_E_UNSUPPORTED; + } + + /* + * This function sets the fan speed of the given OID as a percentage. + * + * This will only be called if the OID has the PERCENTAGE_SET + * capability. + * + * It is optional if you have no fans at all with this feature. + */ + int + onlp_fani_percentage_set(onlp_oid_t id, int p) + { + int fid; + char *path = NULL; + + VALIDATE(id); + + fid = ONLP_OID_ID_GET(id); + + /* reject p=0 (p=0, stop fan) */ + if (p == 0){ + return ONLP_STATUS_E_INVALID; + } + + switch (fid) + { + case FAN_BOX1_FRONT_1: + case FAN_BOX1_FRONT_2: + case FAN_BOX1_REAR_1: + case FAN_BOX1_REAR_2: + case FAN_BOX2_FRONT_3: + case FAN_BOX2_FRONT_4: + case FAN_BOX2_REAR_3: + case FAN_BOX2_REAR_4: + path = FAN_NODE(fan_duty_cycle_percentage); + break; + default: + return ONLP_STATUS_E_INVALID; + } + + DEBUG_PRINT("Fan path = (%s)", path); + + if (onlp_file_write_integer(path, p) < 0) { + AIM_LOG_ERROR("Unable to write data to file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + AIM_LOG_MSG("Set fan_duty_cycle_percentage (%s)=(%d)\r\n", path, p); + + return ONLP_STATUS_OK; + } + + + /* + * This function sets the fan speed of the given OID as per + * the predefined ONLP fan speed modes: off, slow, normal, fast, max. + * + * Interpretation of these modes is up to the platform. + * + */ + int + onlp_fani_mode_set(onlp_oid_t id, onlp_fan_mode_t mode) + { + return ONLP_STATUS_E_UNSUPPORTED; + } + + /* + * This function sets the fan direction of the given OID. + * + * This function is only relevant if the fan OID supports both direction + * capabilities. + * + * This function is optional unless the functionality is available. + */ + int + onlp_fani_dir_set(onlp_oid_t id, onlp_fan_dir_t dir) + { + return ONLP_STATUS_E_UNSUPPORTED; + } + + /* + * Generic fan ioctl. Optional. + */ + int + onlp_fani_ioctl(onlp_oid_t id, va_list vargs) + { + return ONLP_STATUS_E_UNSUPPORTED; + } diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/ledi.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/ledi.c new file mode 100644 index 000000000..533ac4f69 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/ledi.c @@ -0,0 +1,281 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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 +#include +#include "platform_lib.h" + +#define prefix_path "/sys/class/leds/accton_as7327_56x_led::" +#define filename "brightness" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_LED(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +/* LED related data + */ +enum onlp_led_id +{ + LED_RESERVED = 0, + LED_SYS, + LED_BMC, + LED_ID, + LED_ACT, + LED_FAN, + LED_PSU1, + LED_PSU2 +}; + +enum led_light_mode { + LED_MODE_OFF = 0, + LED_MODE_GREEN, + LED_MODE_AMBER, + LED_MODE_RED, + LED_MODE_BLUE, + LED_MODE_GREEN_BLINK, + LED_MODE_AMBER_BLINK, + LED_MODE_RED_BLINK, + LED_MODE_BLUE_BLINK, + LED_MODE_AUTO, + LED_MODE_UNKNOWN +}; + +typedef struct led_light_mode_map { + enum onlp_led_id id; + enum led_light_mode driver_led_mode; + enum onlp_led_mode_e onlp_led_mode; +} led_light_mode_map_t; + +led_light_mode_map_t led_map[] = { +{LED_SYS, LED_MODE_GREEN, ONLP_LED_MODE_GREEN}, +{LED_SYS, LED_MODE_RED, ONLP_LED_MODE_RED}, +{LED_SYS, LED_MODE_GREEN_BLINK, ONLP_LED_MODE_GREEN_BLINKING}, +{LED_BMC, LED_MODE_AMBER, ONLP_LED_MODE_ORANGE}, +{LED_BMC, LED_MODE_GREEN_BLINK, ONLP_LED_MODE_GREEN_BLINKING}, +{LED_BMC, LED_MODE_GREEN, ONLP_LED_MODE_GREEN}, +{LED_BMC, LED_MODE_RED, ONLP_LED_MODE_RED}, +{LED_BMC, LED_MODE_OFF, ONLP_LED_MODE_OFF}, +{LED_ACT, LED_MODE_OFF, ONLP_LED_MODE_OFF}, +{LED_ACT, LED_MODE_GREEN, ONLP_LED_MODE_GREEN}, +{LED_ACT, LED_MODE_RED, ONLP_LED_MODE_RED}, +{LED_ACT, LED_MODE_GREEN_BLINK, ONLP_LED_MODE_GREEN_BLINKING}, +{LED_ID, LED_MODE_OFF, ONLP_LED_MODE_OFF}, +{LED_ID, LED_MODE_BLUE, ONLP_LED_MODE_BLUE}, +{LED_FAN, LED_MODE_AUTO, ONLP_LED_MODE_AUTO}, +{LED_PSU1, LED_MODE_AUTO, ONLP_LED_MODE_AUTO}, +{LED_PSU2, LED_MODE_AUTO, ONLP_LED_MODE_AUTO} +}; + +static char last_path[][10] = /* must map with onlp_led_id */ +{ + "reserved", + "sys", + "bmc", + "id", + "usb", + "fan", + "psu1", + "psu2" +}; + +/* + * Get the information for the given LED OID. + */ +static onlp_led_info_t linfo[] = +{ + { }, /* Not used */ + { + { ONLP_LED_ID_CREATE(LED_SYS), "Chassis LED 1 (SYS LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_GREEN | ONLP_LED_CAPS_RED | ONLP_LED_CAPS_GREEN_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_BMC), "Chassis LED 2 (BMC LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_GREEN | ONLP_LED_CAPS_RED | + ONLP_LED_CAPS_ORANGE | ONLP_LED_CAPS_GREEN_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_ID), "Chassis LED 3 (ID LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_BLUE, + }, + { + { ONLP_LED_ID_CREATE(LED_ACT), "Chassis LED 4 (ACT LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_ON_OFF | ONLP_LED_CAPS_GREEN | ONLP_LED_CAPS_RED | + ONLP_LED_CAPS_GREEN_BLINKING, + }, + { + { ONLP_LED_ID_CREATE(LED_FAN), "Chassis LED 5 (FAN LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU1), "Chassis LED 6 (PSU1 LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, + { + { ONLP_LED_ID_CREATE(LED_PSU2), "Chassis LED 7 (PSU2 LED)", 0 }, + ONLP_LED_STATUS_PRESENT, + ONLP_LED_CAPS_AUTO, + }, +}; + +static int driver_to_onlp_led_mode(enum onlp_led_id id, enum led_light_mode driver_led_mode) +{ + int i, nsize = sizeof(led_map)/sizeof(led_map[0]); + + for (i = 0; i < nsize; i++) + { + if (id == led_map[i].id && driver_led_mode == led_map[i].driver_led_mode) + { + return led_map[i].onlp_led_mode; + } + } + + return 0; +} + +static int onlp_to_driver_led_mode(enum onlp_led_id id, onlp_led_mode_t onlp_led_mode) +{ + int i, nsize = sizeof(led_map)/sizeof(led_map[0]); + + for(i = 0; i < nsize; i++) + { + if (id == led_map[i].id && onlp_led_mode == led_map[i].onlp_led_mode) + { + return led_map[i].driver_led_mode; + } + } + + return 0; +} + +/* + * This function will be called prior to any other onlp_ledi_* functions. + */ +int +onlp_ledi_init(void) +{ + return ONLP_STATUS_OK; +} + +int +onlp_ledi_info_get(onlp_oid_t id, onlp_led_info_t* info) +{ + int local_id; + char data[2] = {0}; + char fullpath[PATH_MAX] = {0}; + + VALIDATE(id); + + local_id = ONLP_OID_ID_GET(id); + + /* get fullpath */ + sprintf(fullpath, "%s%s/%s", prefix_path, last_path[local_id], filename); + + /* Set the onlp_oid_hdr_t and capabilities */ + *info = linfo[ONLP_OID_ID_GET(id)]; + + /* Set LED mode */ + if (onlp_file_read_string(fullpath, data, sizeof(data), 0) != 0) { + DEBUG_PRINT("%s(%d)\r\n", __FUNCTION__, __LINE__); + return ONLP_STATUS_E_INTERNAL; + } + + info->mode = driver_to_onlp_led_mode(local_id, atoi(data)); + + /* Set the on/off status */ + if (info->mode != ONLP_LED_MODE_OFF) { + info->status |= ONLP_LED_STATUS_ON; + } + + return ONLP_STATUS_OK; +} + +/* + * Turn an LED on or off. + * + * This function will only be called if the LED OID supports the ONOFF + * capability. + * + * What 'on' means in terms of colors or modes for multimode LEDs is + * up to the platform to decide. This is intended as baseline toggle mechanism. + */ +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; +} + +/* + * This function puts the LED into the given mode. It is a more functional + * interface for multimode LEDs. + * + * Only modes reported in the LED's capabilities will be attempted. + */ +int +onlp_ledi_mode_set(onlp_oid_t id, onlp_led_mode_t mode) +{ + int local_id; + char fullpath[PATH_MAX] = {0}; + + VALIDATE(id); + + local_id = ONLP_OID_ID_GET(id); + sprintf(fullpath, "%s%s/%s", prefix_path, last_path[local_id], filename); + + if (onlp_file_write_integer(fullpath, onlp_to_driver_led_mode(local_id, mode)) != 0) + { + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +/* + * Generic LED ioctl interface. + */ +int +onlp_ledi_ioctl(onlp_oid_t id, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/make.mk b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/make.mk new file mode 100644 index 000000000..30bc32b5f --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/make.mk @@ -0,0 +1,9 @@ +############################################################################### +# +# +# +############################################################################### + +LIBRARY := x86_64_accton_as7327_56x +$(LIBRARY)_SUBDIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(BUILDER)/lib.mk diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.c new file mode 100644 index 000000000..d5cd5de55 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.c @@ -0,0 +1,207 @@ +#include +#include +#include "platform_lib.h" +#include +#include "x86_64_accton_as7327_56x_log.h" + +int bmc_enable = -1; + +int onlp_file_write_integer(char *filename, int value) +{ + char buf[8] = {0}; + sprintf(buf, "%d", value); + + return onlp_file_write((uint8_t*)buf, strlen(buf), filename); +} + +int onlp_file_read_binary(char *filename, char *buffer, int buf_size, int data_len) +{ + int fd; + int len; + + if ((buffer == NULL) || (buf_size < 0)) { + return -1; + } + + if ((fd = open(filename, O_RDONLY)) == -1) { + return -1; + } + + if ((len = read(fd, buffer, buf_size)) < 0) { + close(fd); + return -1; + } + + if ((close(fd) == -1)) { + return -1; + } + + if ((len > buf_size) || (data_len != 0 && len != data_len)) { + return -1; + } + + return 0; +} + +int onlp_file_read_string(char *filename, char *buffer, int buf_size, int data_len) +{ + int ret; + + if (data_len >= buf_size) { + return -1; + } + + ret = onlp_file_read_binary(filename, buffer, buf_size-1, data_len); + + if (ret == 0) { + buffer[buf_size-1] = '\0'; + } + + return ret; +} + +int initialize_bmc_status(void) +{ + int rv = 0; + int len; + uint8_t data; + + if (bmc_enable >= 0) { + return ONLP_STATUS_OK; + } + + rv = onlp_file_read(&data, sizeof(data), &len, BMC_ENABLE_NODE); + if (rv == ONLP_STATUS_OK) { + if (data == '1') + bmc_enable = 1; + else + bmc_enable = 0; + } else { + AIM_LOG_ERROR("Unable to get bmc enable (%s)\r\n", BMC_ENABLE_NODE); + bmc_enable = 0; + } + return ONLP_STATUS_OK; + +} + +int psu_bmc_str_get(char *basepath, char *field, char *data, int size) +{ + int len; + int rv = 0; + char path[256] = {0}; + + sprintf(path, basepath, field); + + rv = onlp_file_read((uint8_t*)data, size, &len, "%s", path); + + if ((rv != ONLP_STATUS_OK) || !len) { + AIM_LOG_ERROR("Unable to read string from file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + return rv; +} + +int psu_bmc_info_get(char *basepath, char *field, int *val) +{ + char path[256] = {0}; + + sprintf(path, basepath, field); + + if (onlp_file_read_int(val, path) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + return ONLP_STATUS_OK; +} + +int psu_pmbus_info_get(int id, char *node, int *value) +{ + int ret = 0; + char path[PSU_NODE_MAX_PATH_LEN] = {0}; + + *value = 0; + + if (PSU1_ID == id) { + sprintf(path, "%s%s", PSU1_AC_PMBUS_PREFIX, node); + } + else { + sprintf(path, "%s%s", PSU2_AC_PMBUS_PREFIX, node); + } + + if (onlp_file_read_int(value, path) < 0) { + AIM_LOG_ERROR("Unable to read status from file(%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + return ret; +} + +int psu_pmbus_info_set(int id, char *node, int value) +{ + char path[PSU_NODE_MAX_PATH_LEN] = {0}; + + switch (id) { + case PSU1_ID: + sprintf(path, "%s%s", PSU1_AC_PMBUS_PREFIX, node); + break; + case PSU2_ID: + sprintf(path, "%s%s", PSU2_AC_PMBUS_PREFIX, node); + break; + default: + return ONLP_STATUS_E_UNSUPPORTED; + }; + + if (onlp_file_write_integer(path, value) < 0) { + AIM_LOG_ERROR("Unable to write data to file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + + +int psu_pmbus_model_name_get(int id, char *model, int model_len) +{ + int size = 0; + int ret = ONLP_STATUS_OK; + char *prefix = NULL; + + if (model == NULL || model_len < PSU_MODEL_NAME_LEN) { + return ONLP_STATUS_E_PARAM; + } + + prefix = (id == PSU1_ID) ? PSU1_AC_PMBUS_PREFIX : PSU2_AC_PMBUS_PREFIX; + + ret = onlp_file_read((uint8_t*)model, PSU_MODEL_NAME_LEN, &size, "%s%s", prefix, "psu_mfr_model"); + if (ret != ONLP_STATUS_OK || size != PSU_MODEL_NAME_LEN) { + return ONLP_STATUS_E_INTERNAL; + + } + + model[PSU_MODEL_NAME_LEN] = '\0'; + return ONLP_STATUS_OK; +} + + +int psu_pmbus_serial_number_get(int id, char *serial, int serial_len) +{ + int size = 0; + int ret = ONLP_STATUS_OK; + char *prefix = NULL; + + if (serial == NULL || serial_len < PSU_SERIAL_NUMBER_LEN) { + return ONLP_STATUS_E_PARAM; + } + + prefix = (id == PSU1_ID) ? PSU1_AC_PMBUS_PREFIX : PSU2_AC_PMBUS_PREFIX; + + ret = onlp_file_read((uint8_t*)serial, PSU_SERIAL_NUMBER_LEN, &size, "%s%s", prefix, "psu_mfr_serial"); + if (ret != ONLP_STATUS_OK || size != PSU_SERIAL_NUMBER_LEN) { + return ONLP_STATUS_E_INTERNAL; + + } + + serial[PSU_SERIAL_NUMBER_LEN] = '\0'; + return ONLP_STATUS_OK; +} + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.h new file mode 100644 index 000000000..05b685ca7 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/platform_lib.h @@ -0,0 +1,104 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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_as7327_56x_log.h" + +#define CHASSIS_FAN_COUNT 8 +#define CHASSIS_THERMAL_COUNT 4 +#define CHASSIS_PSU_COUNT 2 +#define CHASSIS_PSU_THERMAL_COUNT 3 +#define CHASSIS_LED_COUNT 4 + +#define PSU1_ID 1 +#define PSU2_ID 2 + +#define PSU_NODE_MAX_INT_LEN 8 +#define PSU_NODE_MAX_PATH_LEN 64 + +#define PSU1_AC_PMBUS_PREFIX "/sys/bus/i2c/devices/1-005a/" +#define PSU2_AC_PMBUS_PREFIX "/sys/bus/i2c/devices/2-0059/" + +#define PSU1_BMC_BASE_PATH "/sys/bus/platform/devices/as7327_56x_psu_bmc.0/hwmon/*%s" +#define PSU2_BMC_BASE_PATH "/sys/bus/platform/devices/as7327_56x_psu_bmc.1/hwmon/*%s" + +#define PSU1_AC_PMBUS_NODE(node) PSU1_AC_PMBUS_PREFIX#node +#define PSU2_AC_PMBUS_NODE(node) PSU2_AC_PMBUS_PREFIX#node + +#define CPLD_NODE_PATH "/sys/bus/i2c/devices/i2c-157/157-0062/" +#define BMC_ENABLE_NODE CPLD_NODE_PATH"bmc_enable" +#define FAN_NODE(node) CPLD_NODE_PATH#node +#define FAN_WDT_ENABLE 0x1 +#define FAN_WDT_DISABLE 0x0 +#define FAN_WDT_CLEAR 0x1 + + +#define IDPROM_PATH "/sys/bus/i2c/devices/0-0050/eeprom" + +#define PSU_STATUS_POWER_GOOD 1 +#define PSU_STATUS_PRESENT 1 + +#define PSU_MODEL_NAME_LEN 11 +#define PSU_SERIAL_NUMBER_LEN 14 + +int onlp_file_write_integer(char *filename, int value); +int onlp_file_read_binary(char *filename, char *buffer, int buf_size, int data_len); +int onlp_file_read_string(char *filename, char *buffer, int buf_size, int data_len); + + +int psu_pmbus_info_get(int id, char *node, int *value); +int psu_pmbus_info_set(int id, char *node, int value); + +typedef enum psu_type { + PSU_TYPE_UNKNOWN, + PSU_TYPE_AC, + PSU_TYPE_DC +} psu_type_t; + +int initialize_bmc_status(void); + +int psu_bmc_str_get(char *basepath, char *field, char *data, int size); +int psu_bmc_info_get(char *basepath, char *field, int *val); + +psu_type_t get_psu_type(int id, char* modelname, int modelname_len); +int psu_pmbus_model_name_get(int id, char *model, int model_len); +int psu_pmbus_serial_number_get(int id, char *serial, int serial_len); + +int bmc_enable; + +#define BMC_IS_ENABLED() bmc_enable +//#define DEBUG_MODE 1 + +#if (DEBUG_MODE == 1) + #define DEBUG_PRINT(format, ...) printf(format, __VA_ARGS__) +#else + #define DEBUG_PRINT(format, ...) +#endif + +#endif /* __PLATFORM_LIB_H__ */ + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/psui.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/psui.c new file mode 100644 index 000000000..8c9c7fea8 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/psui.c @@ -0,0 +1,324 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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 "platform_lib.h" + +#define VALIDATE(_id) \ + do { \ + if(!ONLP_OID_IS_PSU(_id)) { \ + return ONLP_STATUS_E_INVALID; \ + } \ + } while(0) + +int +onlp_psui_init(void) +{ + return ONLP_STATUS_OK; +} + +static int +psu_bmc_detail_info_get(onlp_psu_info_t* info, int index) +{ + int val; + char *basepath = NULL; + + if (index == PSU1_ID) + basepath = PSU1_BMC_BASE_PATH; + else if (index == PSU2_ID) + basepath = PSU2_BMC_BASE_PATH; + + /* Read voltage, current and power */ + if (psu_bmc_info_get(basepath, "psu_vout", &val) == ONLP_STATUS_OK) { + info->mvout = val; + info->caps |= ONLP_PSU_CAPS_VOUT; + } + + if (psu_bmc_info_get(basepath, "psu_vin", &val) == ONLP_STATUS_OK) { + info->mvin = val; + info->caps |= ONLP_PSU_CAPS_VIN; + } + + if (psu_bmc_info_get(basepath, "psu_iout", &val) == ONLP_STATUS_OK) { + info->miout = val; + info->caps |= ONLP_PSU_CAPS_IOUT; + } + + if (psu_bmc_info_get(basepath, "psu_iin", &val) == ONLP_STATUS_OK) { + info->miin = val; + info->caps |= ONLP_PSU_CAPS_IIN; + } + + if (psu_bmc_info_get(basepath, "psu_pout", &val) == ONLP_STATUS_OK) { + info->mpout = val; + info->caps |= ONLP_PSU_CAPS_POUT; + } + + if (psu_bmc_info_get(basepath, "psu_pin", &val) == ONLP_STATUS_OK) { + info->mpin = val; + info->caps |= ONLP_PSU_CAPS_PIN; + } + + return ONLP_STATUS_OK; +} + +static int +psu_pmbus_detail_info_get(onlp_psu_info_t* info, int index) +{ + int val; + /* Read voltage, current and power */ + if (psu_pmbus_info_get(index, "psu_v_out", &val) == 0) { + info->mvout = val; + info->caps |= ONLP_PSU_CAPS_VOUT; + } + + if (psu_pmbus_info_get(index, "psu_v_in", &val) == 0) { + info->mvin = val; + info->caps |= ONLP_PSU_CAPS_VIN; + } + + if (psu_pmbus_info_get(index, "psu_i_out", &val) == 0) { + info->miout = val; + info->caps |= ONLP_PSU_CAPS_IOUT; + } + + if (psu_pmbus_info_get(index, "psu_i_in", &val) == 0) { + info->miin = val; + info->caps |= ONLP_PSU_CAPS_IIN; + } + + if (psu_pmbus_info_get(index, "psu_p_out", &val) == 0) { + info->mpout = val; + info->caps |= ONLP_PSU_CAPS_POUT; + } + + if (psu_pmbus_info_get(index, "psu_p_in", &val) == 0) { + info->mpin = val; + info->caps |= ONLP_PSU_CAPS_PIN; + } + + psu_pmbus_model_name_get(index, info->model, sizeof(info->model)); + + psu_pmbus_serial_number_get(index, info->serial, sizeof(info->serial)); + + return ONLP_STATUS_OK; +} + +static int +psu_detail_info_get(onlp_psu_info_t* info) +{ + int index = ONLP_OID_ID_GET(info->hdr.id); + int temp_index = 0; + + /* Set the associated oid_table */ + info->hdr.coids[0] = ONLP_FAN_ID_CREATE(index + CHASSIS_FAN_COUNT); + for(temp_index = 1; temp_index <= CHASSIS_PSU_THERMAL_COUNT; temp_index++) + { + info->hdr.coids[temp_index] = ONLP_THERMAL_ID_CREATE((index-1)*CHASSIS_PSU_THERMAL_COUNT + CHASSIS_THERMAL_COUNT + temp_index); + } + + if (info->status & (ONLP_PSU_STATUS_FAILED | ONLP_PSU_STATUS_UNPLUGGED)) { + return ONLP_STATUS_OK; + } + + if(BMC_IS_ENABLED()) { + psu_bmc_detail_info_get(info, index); + } else { + psu_pmbus_detail_info_get(info, index); + } + + return ONLP_STATUS_OK; +} + +/* + * Get all information about the given PSU oid. + */ +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 }, + } +}; + +static void +onlp_psu_str_check(char *str, int buffer_size) +{ + str[buffer_size - 1] = '\0'; + + if(strlen(str) && isspace(str[strlen(str) - 1])) { + str[strlen(str) - 1] = 0; + } +} + +int +onlp_psui_info_get(onlp_oid_t id, onlp_psu_info_t* info) +{ + int val = 0; + int ret = ONLP_STATUS_OK; + int index = ONLP_OID_ID_GET(id); + int psu_type; + char *basepath; + + VALIDATE(id); + + memset(info, 0, sizeof(onlp_psu_info_t)); + *info = pinfo[index]; /* Set the onlp_oid_hdr_t */ + + initialize_bmc_status(); + + if(BMC_IS_ENABLED()) { + if (index == PSU1_ID) + basepath = PSU1_BMC_BASE_PATH; + else if (index == PSU2_ID) + basepath = PSU2_BMC_BASE_PATH; + else + return ONLP_STATUS_E_INVALID; + + if (psu_bmc_info_get(basepath, "psu_present", &val) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_present)\r\n", index); + return ONLP_STATUS_E_INTERNAL; + } + + /* Get the present state */ + if (val != PSU_STATUS_PRESENT) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_OK; + } + info->status |= ONLP_PSU_STATUS_PRESENT; + + /* Get model name */ + psu_bmc_str_get(basepath, "psu_model_name", info->model, sizeof(info->model)); + + onlp_psu_str_check(info->model, ONLP_CONFIG_INFO_STR_MAX); + + /* Get serial number */ + psu_bmc_str_get(basepath, "psu_serial_number", info->serial, sizeof(info->serial)); + + onlp_psu_str_check(info->serial, ONLP_CONFIG_INFO_STR_MAX); + + /* get psu type + */ + if (psu_bmc_info_get(basepath, "psu_vin_type", &psu_type) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("unable to read PSU(%d) node(psu_mfr_vin_type)\r\n", index); + } + switch (psu_type) { + case PSU_TYPE_DC: + info->caps = ONLP_PSU_CAPS_DC48; + case PSU_TYPE_AC: + info->caps = ONLP_PSU_CAPS_AC; + break; + case PSU_TYPE_UNKNOWN: /* user insert a unknown psu or unplugged.*/ + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + info->status &= ~ONLP_PSU_STATUS_FAILED; + ret = ONLP_STATUS_OK; + break; + default: + ret = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + /* Get power good status */ + if (psu_bmc_info_get(basepath, "psu_power_good", &val) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", index); + } + + if (val != PSU_STATUS_POWER_GOOD) { + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + info->caps = 0; + } + + } else { + /* Get the present state */ + if (psu_pmbus_info_get(index, "psu_present", &val) != 0) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_present)\r\n", index); + } + + if (val != PSU_STATUS_PRESENT) { + info->status &= ~ONLP_PSU_STATUS_PRESENT; + return ONLP_STATUS_OK; + } + info->status |= ONLP_PSU_STATUS_PRESENT; + + /* Get model name */ + psu_pmbus_model_name_get(index, info->model, sizeof(info->model)); + + onlp_psu_str_check(info->model, ONLP_CONFIG_INFO_STR_MAX); + + /* Get serial number */ + psu_pmbus_serial_number_get(index, info->serial, sizeof(info->serial)); + + onlp_psu_str_check(info->serial, ONLP_CONFIG_INFO_STR_MAX); + + /* get psu type + */ + if (psu_pmbus_info_get(index, "psu_mfr_vin_type", &psu_type) != 0) { + AIM_LOG_ERROR("unable to read psu(%d) node(psu_mfr_vin_type)\r\n", index); + } + switch (psu_type) { + case PSU_TYPE_DC: + info->caps = ONLP_PSU_CAPS_DC48; + case PSU_TYPE_AC: + info->caps = ONLP_PSU_CAPS_AC; + break; + case PSU_TYPE_UNKNOWN: /* user insert a unknown psu or unplugged.*/ + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + info->status &= ~ONLP_PSU_STATUS_FAILED; + ret = ONLP_STATUS_OK; + break; + default: + ret = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + /* Get power good status */ + if (psu_pmbus_info_get(index, "psu_power_good", &val) != 0) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", index); + } + + if (val != PSU_STATUS_POWER_GOOD) { + info->status |= ONLP_PSU_STATUS_UNPLUGGED; + info->caps = 0; + } + + } + + ret = psu_detail_info_get(info); + + return ret; +} + +int +onlp_psui_ioctl(onlp_oid_t pid, va_list vargs) +{ + return ONLP_STATUS_E_UNSUPPORTED; +} + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sfpi.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sfpi.c new file mode 100644 index 000000000..b834e87d3 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sfpi.c @@ -0,0 +1,549 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2013 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 "x86_64_accton_as7327_56x_int.h" +#include "x86_64_accton_as7327_56x_log.h" + +#define PORT_BUS_INDEX(port) sfp_map[port] + +#define PORT_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom" +#define MODULE_PRESENT_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_present_%d" +#define MODULE_RXLOS_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_rx_los_%d" +#define MODULE_TXFAULT_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_tx_fault_%d" +#define MODULE_TXDISABLE_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_tx_disable_%d" +#define MODULE_PRESENT_ALL_ATTR "/sys/bus/i2c/devices/%d-00%d/module_present_all" +#define MODULE_RESET_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_reset_%d" +#define MODULE_LPMODE_FORMAT "/sys/bus/i2c/devices/%d-00%d/module_lpmode_%d" +#define MODULE_RXLOS_ALL_ATTR_CPLD1 "/sys/bus/i2c/devices/157-0062/module_rx_los_all" +#define MODULE_RXLOS_ALL_ATTR_CPLD2 "/sys/bus/i2c/devices/158-0064/module_rx_los_all" +/* QSFP device address of eeprom */ +#define PORT_EEPROM_DEVADDR 0x50 +/* QSFP tx disable offset */ +#define QSFP_EEPROM_OFFSET_TXDIS 0x56 + +const int sfp_map[] = { + 101,102,103,104,105,106,107,108, + 109,110,111,112,113,114,115,116, + 117,118,119,120,121,122,123,124, + 125,126,127,128,129,130,131,132, + 133,134,135,136,137,138,139,140, + 141,142,143,144,145,146,147,148, + 149,150,151,152,153,154,155,156}; /*port 49~56 QSFP*/ + + +/************************************************************ + * + * SFPI Entry Points + * + ***********************************************************/ +int +onlp_sfpi_init(void) +{ + /* Called at initialization time */ + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_bitmap_get(onlp_sfp_bitmap_t* bmap) +{ + /* + * Ports {0, 56} + */ + int p; + + for(p = 0; p < 56; p++) { + AIM_BITMAP_SET(bmap, p); + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_is_present(int port) +{ + /* + * Return 1 if present. + * Return 0 if not present. + * Return < 0 if error. + */ + int present; + int bus, addr; + + addr = (port < 12) ? 62 : 64; + bus = (addr == 62) ? 157 : 158; + + if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return present; +} + +int +onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst) +{ + uint32_t bytes[8]; + FILE* fp; + int addr, i; + uint32_t *ptr = bytes; + unsigned long long presence_all = 0; + + for (addr = 62; addr <= 64; addr+=2) { + /* Read present status of port 0~53 */ + int length = 0; + int count = 0; + char file[64] = {0}; + int bus = (addr == 62) ? 157 : 158; + + sprintf(file, MODULE_PRESENT_ALL_ATTR, bus, addr); + fp = fopen(file, "r"); + if(fp == NULL) { + AIM_LOG_ERROR("Unable to open the module_present_all device file of CPLD3."); + return ONLP_STATUS_E_INTERNAL; + } + + length = (addr == 62) ? 2 : 6; + if (length == 2) { + count = fscanf(fp, "%x %x", ptr+0, ptr+1); + } + else { /* length == 6 */ + count = fscanf(fp, "%x %x %x %x %x %x", ptr+0, ptr+1, ptr+2, ptr+3, ptr+4, ptr+5); + } + fclose(fp); + + if(count != length) { + /* Likely a CPLD read timeout. */ + AIM_LOG_ERROR("Unable to read all fields the module_present_all device file of CPLD."); + return ONLP_STATUS_E_INTERNAL; + } + + ptr += count; + } + + /* Convert to 64 bit integer in port order */ + bytes[1] &= 0xF; + bytes[6] &= 0xF; + + presence_all |= bytes[7]; + + presence_all <<= 4; + presence_all |= bytes[6]; + + for(i = 5; i >= 2; i--) { + presence_all <<= 8; + presence_all |= bytes[i]; + } + + presence_all <<= 4; + presence_all |= bytes[1]; + + presence_all <<= 8; + presence_all |= bytes[0]; + + /* Populate bitmap */ + for(i = 0; presence_all; i++) { + AIM_BITMAP_MOD(dst, i, (presence_all & 1)); + presence_all >>= 1; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst) +{ + uint32_t bytes[7]; + uint32_t *ptr = bytes; + FILE* fp; + unsigned long long rx_los_all = 0 ; + + /* Read present status of port 0~23 */ + int addr, i = 0; + + for (addr = 62; addr <= 64; addr+=2) { + int count = 0; + + if (addr == 62) { + fp = fopen(MODULE_RXLOS_ALL_ATTR_CPLD1, "r"); + } + else { + fp = fopen(MODULE_RXLOS_ALL_ATTR_CPLD2, "r"); + } + + if(fp == NULL) { + AIM_LOG_ERROR("Unable to open the module_rx_los_all device file of CPLD(0x%d)", addr); + return ONLP_STATUS_E_INTERNAL; + } + + if (addr == 62) { + count = fscanf(fp, "%x %x", ptr+0, ptr+1); + fclose(fp); + if(count != 2) { + /* Likely a CPLD read timeout. */ + AIM_LOG_ERROR("Unable to read all fields from the module_rx_los_all device file of CPLD(0x%d)", addr); + return ONLP_STATUS_E_INTERNAL; + } + } + else { + count = fscanf(fp, "%x %x %x %x %x", ptr+0, ptr+1, ptr+2, ptr+3, ptr+4); + fclose(fp); + if(count != 5) { + /* Likely a CPLD read timeout. */ + AIM_LOG_ERROR("Unable to read all fields from the module_rx_los_all device file of CPLD(0x%d)", addr); + return ONLP_STATUS_E_INTERNAL; + } + } + + ptr += count; + } + + /* Convert to 64 bit integer in port order */ + bytes[1] &= 0xF; + bytes[6] &= 0xF; + for(i = 6; i >= 2; i--) { + rx_los_all <<= 8; + rx_los_all |= bytes[i]; + } + + rx_los_all <<= 4; + rx_los_all |= bytes[1]; + + rx_los_all <<= 8; + rx_los_all |= bytes[0]; + + /* Populate bitmap */ + for(i = 0; rx_los_all; i++) { + AIM_BITMAP_MOD(dst, i, (rx_los_all & 1)); + rx_los_all >>= 1; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_eeprom_read(int port, uint8_t data[256]) +{ + /* + * Read the SFP eeprom into data[] + * + * Return MISSING if SFP is missing. + * Return OK if eeprom is read + */ + int size = 0; + memset(data, 0, 256); + + if(onlp_file_read(data, 256, &size, PORT_EEPROM_FORMAT, PORT_BUS_INDEX(port)) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read eeprom from port(%d)\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + if (size != 256) { + AIM_LOG_ERROR("Unable to read eeprom from port(%d), size is different!\r\n", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_dom_read(int port, uint8_t data[256]) +{ + FILE* fp; + char file[64] = {0}; + + sprintf(file, PORT_EEPROM_FORMAT, PORT_BUS_INDEX(port)); + fp = fopen(file, "r"); + if(fp == NULL) { + AIM_LOG_ERROR("Unable to open the eeprom device file of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + if (fseek(fp, 256, SEEK_CUR) != 0) { + fclose(fp); + AIM_LOG_ERROR("Unable to set the file position indicator of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + int ret = fread(data, 1, 256, fp); + fclose(fp); + if (ret != 256) { + AIM_LOG_ERROR("Unable to read the module_eeprom device file of port(%d)", port); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + +int +onlp_sfpi_dev_readb(int port, uint8_t devaddr, uint8_t addr) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_readb(bus, devaddr, addr, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_writeb(int port, uint8_t devaddr, uint8_t addr, uint8_t value) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_writeb(bus, devaddr, addr, value, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_readw(bus, devaddr, addr, ONLP_I2C_F_FORCE); +} + +int +onlp_sfpi_dev_writew(int port, uint8_t devaddr, uint8_t addr, uint16_t value) +{ + int bus = PORT_BUS_INDEX(port); + return onlp_i2c_writew(bus, 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; + + if (port < 0 || port >= 56) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + int addr = (port < 12) ? 62 : 64; + int bus = (addr == 62) ? 157 : 158; + + switch(control) + { + case ONLP_SFP_CONTROL_TX_DISABLE: + { + if(port > 47 && port < 56) + { + present = onlp_sfpi_is_present(port); + /* write qsfp eeprom data of tx disable if qsfp present */ + if(present == 1) + { + /* txdis valid bit(bit0-bit3), xxxx 1111 */ + value = value&0xf; + + onlp_sfpi_dev_writeb(port, PORT_EEPROM_DEVADDR, QSFP_EEPROM_OFFSET_TXDIS, value); + + rv = ONLP_STATUS_OK; + + } + else + { + rv = ONLP_STATUS_E_INTERNAL; + } + + } + else + { + if (onlp_file_write_int(value, MODULE_TXDISABLE_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to set tx_disable status to port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + } + + break; + } + + case ONLP_SFP_CONTROL_RESET: + { + if (port < 48 || port > 55) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_write_int(value, MODULE_RESET_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to write reset status to port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + + break; + } + + case ONLP_SFP_CONTROL_LP_MODE: + { + if (port < 48 || port > 55) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_write_int(value, MODULE_LPMODE_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to write lpmode status to port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + + break; + } + default: + rv = ONLP_STATUS_E_UNSUPPORTED; + break; + } + + return rv; +} + +int +onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value) +{ + int rv; + int present = 0; + int tx_dis = 0; + + if (port < 0 || port >= 56) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + int addr = (port < 12) ? 62 : 64; + int bus = (addr == 62) ? 157 : 158; + + switch(control) + { + case ONLP_SFP_CONTROL_RX_LOS: + { + if (port < 0 || port >= 48) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_read_int(value, MODULE_RXLOS_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read rx_loss status from port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + break; + } + + case ONLP_SFP_CONTROL_TX_FAULT: + { + if (port < 0 || port >= 48) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_read_int(value, MODULE_TXFAULT_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read tx_fault status from port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + break; + } + + case ONLP_SFP_CONTROL_TX_DISABLE: + { + if(port > 47 && port < 56) + { + present = onlp_sfpi_is_present(port); + /* read qsfp eeprom offset of tx disable if qsfp on the port */ + if(present == 1) + { + tx_dis = onlp_sfpi_dev_readb(port, PORT_EEPROM_DEVADDR, QSFP_EEPROM_OFFSET_TXDIS); + + *value = tx_dis; + + rv = ONLP_STATUS_OK; + + } + else + { + rv = ONLP_STATUS_E_INTERNAL; + } + } + else + { + + if (onlp_file_read_int(value, MODULE_TXDISABLE_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read tx_disabled status from port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + } + break; + } + + case ONLP_SFP_CONTROL_RESET: + { + if (port < 48 || port > 55) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_read_int(value, MODULE_RESET_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read reset status from port(%d)\r\n", port); + rv = ONLP_STATUS_E_INTERNAL; + } + else { + rv = ONLP_STATUS_OK; + } + break; + } + + case ONLP_SFP_CONTROL_LP_MODE: + { + if (port < 48 || port > 55) { + return ONLP_STATUS_E_UNSUPPORTED; + } + + if (onlp_file_read_int(value, MODULE_LPMODE_FORMAT, bus, addr, (port+1)) < 0) { + AIM_LOG_ERROR("Unable to read lpmode status from port(%d)\r\n", 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/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sysi.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sysi.c new file mode 100644 index 000000000..89aec126b --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/sysi.c @@ -0,0 +1,175 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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 +#include +#include +#include "platform_lib.h" +#include "x86_64_accton_as7327_56x_int.h" +#include "x86_64_accton_as7327_56x_log.h" +#include + +#define NUM_OF_FAN_ON_MAIN_BROAD 8 + +#define NUM_OF_CPLD 3 +#define CPLD_VER_MAX_STR_LEN 20 +#define FAN_DUTY_CYCLE_MAX (100) +#define FAN_DUTY_CYCLE_DEFAULT (50) +#define FAN_DUTY_PLUS_FOR_DIR (13) +/* Note, all chassis fans share 1 single duty setting. + * Here use fan 1 to represent global fan duty value.*/ +#define FAN_ID_FOR_SET_FAN_DUTY (1) +#define CELSIUS_RECORD_NUMBER (2) /*Must >= 2*/ + + +const char* +onlp_sysi_platform_get(void) +{ + return "x86-64-accton-as7327-56x-r0"; +} + +int +onlp_sysi_onie_data_get(uint8_t** data, int* size) +{ + const int len = 256; + uint8_t* rdata = aim_zmalloc(len); + int ret = ONLP_STATUS_OK; + + ret = onlp_file_open(O_RDONLY, 0, IDPROM_PATH); + if (ret >= 0) { + close(ret); + if(onlp_file_read(rdata, len, size, IDPROM_PATH) == ONLP_STATUS_OK) { + if(*size == len) { + *data = rdata; + return ONLP_STATUS_OK; + } + } + } + + aim_free(rdata); + *size = 0; + return ONLP_STATUS_E_INTERNAL; +} + +void onlp_sysi_onie_data_free(uint8_t* data) +{ + if (data) + aim_free(data); +} + +int onlp_sysi_onie_info_get(onlp_onie_info_t* onie) +{ + int ret = ONLP_STATUS_OK; + + ret = onlp_onie_decode_file(onie, IDPROM_PATH); + + onie->_hdr_id_string = aim_fstrdup("TlvInfo"); + onie->_hdr_version = 0x1; + return ret; +} + +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)); + + /* 4 Thermal sensors on the chassis */ + for (i = 1; i <= CHASSIS_THERMAL_COUNT; i++) { + *e++ = ONLP_THERMAL_ID_CREATE(i); + } + + /* 3 LEDs on the chassis */ + for (i = 1; i <= CHASSIS_LED_COUNT; i++) { + *e++ = ONLP_LED_ID_CREATE(i); + } + + /* 2 PSUs on the chassis */ + for (i = 1; i <= CHASSIS_PSU_COUNT; i++) { + *e++ = ONLP_PSU_ID_CREATE(i); + } + + /* 8 Fans on the chassis */ + for (i = 1; i <= CHASSIS_FAN_COUNT; i++) { + *e++ = ONLP_FAN_ID_CREATE(i); + } + + return 0; +} + +static char* cpld_ver_path[NUM_OF_CPLD] = { + "/sys/class/fpga_class/fpga/version", /* FPGA */ + "/sys/bus/i2c/devices/157-0062/version", /* CPLD-1 */ + "/sys/bus/i2c/devices/158-0064/version" /* CPLD-2 */ +}; + +int +onlp_sysi_platform_info_get(onlp_platform_info_t* pi) +{ + + int i = 0; + int len = 0; + char *string = NULL; + char ver[NUM_OF_CPLD][CPLD_VER_MAX_STR_LEN]={{0}}; + + for (i = 0; i < NUM_OF_CPLD; i++) { + len = onlp_file_read_str(&string, cpld_ver_path[i]); + if (string && len) { + strncpy(ver[i], string, len); + aim_free(string); + } else { + return ONLP_STATUS_E_INTERNAL; + } + } + + pi->cpld_versions = aim_fstrdup("FPGA:%s CPLD-1:%s CPLD-2:%s", + ver[0], ver[1], ver[2]); + + return 0; +} + +void +onlp_sysi_platform_info_free(onlp_platform_info_t* pi) +{ + aim_free(pi->cpld_versions); +} + +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/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/thermali.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/thermali.c new file mode 100644 index 000000000..0d7ca5918 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/thermali.c @@ -0,0 +1,255 @@ +/************************************************************ + * + * + * Copyright 2014 Big Switch Networks, Inc. + * Copyright 2014 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 +#include "platform_lib.h" + +#define THERMAL_PATH_FORMAT "/sys/bus/i2c/devices/%s/*temp1_input" +#define THERMAL_BMC_BASE_PATH "/sys/bus/platform/devices/as7327_56x_thermal_bmc/hwmon/*temp%s_input" + +#define PSU_THERMAL_PATH_FORMAT "/sys/bus/i2c/devices/%s/*psu_temp%d_input" +#define PSU1_THERMAL_BMC_BASE_PATH "/sys/bus/platform/devices/as7327_56x_psu_bmc.0/hwmon/*psu_temp%d_input" +#define PSU2_THERMAL_BMC_BASE_PATH "/sys/bus/platform/devices/as7327_56x_psu_bmc.1/hwmon/*psu_temp%d_input" + +#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_MAIN_BOARD, + THERMAL_2_ON_MAIN_BOARD, + THERMAL_3_ON_MAIN_BOARD, + THERMAL_1_ON_PSU1, + THERMAL_2_ON_PSU1, + THERMAL_3_ON_PSU1, + THERMAL_1_ON_PSU2, + THERMAL_2_ON_PSU2, + THERMAL_3_ON_PSU2, +}; + +static char* directory[] = /* must map with onlp_thermal_id */ +{ + NULL, + NULL, /* CPU_CORE files */ + "7-004c", /* Thermal 1 on main board */ + "7-004b", /* Thermal 2 on main board */ + "7-004a", /* Thermal 3 on main board */ + "1-005a", /* Thermal 1 on PSU1 */ + "1-005a", /* Thermal 2 on PSU1 */ + "1-005a", /* Thermal 3 on PSU1 */ + "2-0059", /* Thermal 1 on PSU2 */ + "2-0059", /* Thermal 2 on PSU2 */ + "2-0059", /* Thermal 3 on PSU2 */ +}; + +static char* directory_bmc[] = /* must map with onlp_thermal_id */ +{ + NULL, + NULL, /* CPU_CORE files */ + "1", /* Thermal 1 on main board */ + "2", /* Thermal 2 on main board */ + "3", /* Thermal 3 on main board */ + NULL, /* Thermal 1 on PSU1 */ + NULL, /* Thermal 2 on PSU1 */ + NULL, /* Thermal 3 on PSU1 */ + NULL, /* Thermal 1 on PSU2 */ + NULL, /* Thermal 2 on PSU2 */ + NULL, /* Thermal 3 on PSU2 */ +}; + +static char* cpu_coretemp_files[] = + { + "/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 values */ +static onlp_thermal_info_t linfo[] = { + { }, /* Not used */ + { { ONLP_THERMAL_ID_CREATE(THERMAL_CPU_CORE), "CPU Core", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 82000, 104000, 104000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_MAIN_BOARD), "MAC Around(0x4C)", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 68000, 70000, 70000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_MAIN_BOARD), "COMe bottom(0x4B)", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 68000, 70000, 70000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_MAIN_BOARD), "Air Outlet(0x4A)", 0}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 68000, 70000, 70000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU1), "PSU-1 Thermal Sensor 1", ONLP_PSU_ID_CREATE(PSU1_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 60000, 70000, 70000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU1), "PSU-1 Thermal Sensor 2", ONLP_PSU_ID_CREATE(PSU1_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 100000, 120000, 120000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU1), "PSU-1 Thermal Sensor 3", ONLP_PSU_ID_CREATE(PSU1_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 100000, 120000, 120000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_1_ON_PSU2), "PSU-2 Thermal Sensor 1", ONLP_PSU_ID_CREATE(PSU2_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 60000, 70000, 70000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_2_ON_PSU2), "PSU-2 Thermal Sensor 2", ONLP_PSU_ID_CREATE(PSU2_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 100000, 120000, 120000 } + }, + { { ONLP_THERMAL_ID_CREATE(THERMAL_3_ON_PSU2), "PSU-2 Thermal Sensor 3", ONLP_PSU_ID_CREATE(PSU2_ID)}, + ONLP_THERMAL_STATUS_PRESENT, + ONLP_THERMAL_CAPS_ALL, 0, { 100000, 120000, 120000 } + } +}; + +/* + * This will be called to intiialize the thermali subsystem. + */ +int +onlp_thermali_init(void) +{ + return ONLP_STATUS_OK; +} + +/* + * Retrieve the information structure for the given thermal OID. + * + * If the OID is invalid, return ONLP_E_STATUS_INVALID. + * If an unexpected error occurs, return ONLP_E_STATUS_INTERNAL. + * Otherwise, return ONLP_STATUS_OK with the OID's information. + * + * Note -- it is expected that you fill out the information + * structure even if the sensor described by the OID is not present. + */ +int +onlp_thermali_info_get(onlp_oid_t id, onlp_thermal_info_t* info) +{ + int tid; + int pid; + int index = 0; /* thermal index in psu */ + int psu_power_good = PSU_STATUS_POWER_GOOD; + char path[64] = {0}; + char *basepath = NULL; + + VALIDATE(id); + + tid = ONLP_OID_ID_GET(id); + + /* Set the onlp_oid_hdr_t and capabilities */ + *info = linfo[tid]; + + initialize_bmc_status(); + + if(tid == THERMAL_CPU_CORE) { + return onlp_file_read_int_max(&info->mcelsius, cpu_coretemp_files); + } + + + switch (tid) { + case THERMAL_1_ON_MAIN_BOARD: + case THERMAL_2_ON_MAIN_BOARD: + case THERMAL_3_ON_MAIN_BOARD: + /* get path for each thermal sensor on the main board */ + if (BMC_IS_ENABLED()) { + /* 1 -> 0x4c 2 -> 0x4b 3 -> 0x4a */ + sprintf(path, THERMAL_BMC_BASE_PATH, directory_bmc[tid]); + } else { + sprintf(path, THERMAL_PATH_FORMAT, directory[tid]); + } + break; + case THERMAL_1_ON_PSU1: + case THERMAL_2_ON_PSU1: + case THERMAL_3_ON_PSU1: + case THERMAL_1_ON_PSU2: + case THERMAL_2_ON_PSU2: + case THERMAL_3_ON_PSU2: + pid = ONLP_OID_ID_GET(info->hdr.poid); + + /* each psu has 3 thermal sensors, which are indexed 1-3 */ + index = (tid - THERMAL_1_ON_PSU1) % 3 + 1; + + /* get path for each sensor of the psu */ + if (BMC_IS_ENABLED()) { + if (pid == PSU1_ID) { + basepath = PSU1_BMC_BASE_PATH; + sprintf(path, PSU1_THERMAL_BMC_BASE_PATH, index); + } else if (pid == PSU2_ID) { + basepath = PSU2_BMC_BASE_PATH; + sprintf(path, PSU2_THERMAL_BMC_BASE_PATH, index); + } else { + return ONLP_STATUS_E_INVALID; + } + + /* Get power good status */ + if (psu_bmc_info_get(basepath, "psu_power_good", &psu_power_good) != ONLP_STATUS_OK) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", pid); + } + + } else { + sprintf(path, PSU_THERMAL_PATH_FORMAT, directory[tid], index); + + /* Get power good status */ + if (psu_pmbus_info_get(pid, "psu_power_good", &psu_power_good) != 0) { + AIM_LOG_ERROR("Unable to read PSU(%d) node(psu_power_good)\r\n", pid); + } + + } + break; + default: + return ONLP_STATUS_E_INVALID; + }; + + if(psu_power_good != PSU_STATUS_POWER_GOOD) { + info->status |= ONLP_THERMAL_STATUS_FAILED; + info->mcelsius = 0; + return ONLP_STATUS_OK; + } + + if (onlp_file_read_int(&info->mcelsius, path) < 0) { + AIM_LOG_ERROR("Unable to read status from file (%s)\r\n", path); + return ONLP_STATUS_E_INTERNAL; + } + + return ONLP_STATUS_OK; +} + + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_config.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_config.c new file mode 100644 index 000000000..a56299951 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_config.c @@ -0,0 +1,81 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* */ +#define __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(_x) #_x +#define __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(_x) __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(_x) +X86_64_ACCTON_AS7327_56X_CONFIG_settings_t X86_64_ACCTON_AS7327_56X_CONFIG_settings[] = +{ +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_LOGGING(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_STDLIB(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_PORTING_INCLUDE_STDLIB_HEADERS(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_UCLI(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION + { __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION), __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE(X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION) }, +#else +{ X86_64_ACCTON_AS7327_56X_CONFIG_INCLUDE_DEFAULT_FAN_DIRECTION(__X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME), "__undefined__" }, +#endif + { NULL, NULL } +}; +#undef __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_VALUE +#undef __X86_64_ACCTON_AS7327_56X_CONFIG_STRINGIFY_NAME + +const char* +_x86_64__a_c_c_t_o_n__as7327_56x__c_o_n_f_i_g_lookup(const char* setting) +{ + int i; + for(i = 0; X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].name; i++) { + if(!strcmp(X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].name, setting)) { + return X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].value; + } + } + return NULL; +} + +int +_x86_64__a_c_c_t_o_n__as7327_56x__c_o_n_f_i_g_show(struct aim_pvs_s* pvs) +{ + int i; + for(i = 0; X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].name; i++) { + aim_printf(pvs, "%s = %s\n", X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].name, X86_64_ACCTON_AS7327_56X_CONFIG_settings[i].value); + } + return i; +} + +/* */ + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_enums.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_enums.c new file mode 100644 index 000000000..661966e5a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_enums.c @@ -0,0 +1,10 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +/* <--auto.start.enum(ALL).source> */ +/* */ + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_int.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_int.h new file mode 100644 index 000000000..6ac136250 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_int.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * x86_64_accton_as7327_56x Internal Header + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7327_56x_INT_H__ +#define __x86_64_accton_as7327_56x_INT_H__ + +#include + + +#endif /* __x86_64_accton_as7327_56x_INT_H__ */ diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.c new file mode 100644 index 000000000..0d9531cec --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.c @@ -0,0 +1,18 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as7327_56x_log.h" +/* + * x86_64_accton_as7327_56x log struct. + */ +AIM_LOG_STRUCT_DEFINE( + X86_64_ACCTON_AS7327_56X_CONFIG_LOG_OPTIONS_DEFAULT, + X86_64_ACCTON_AS7327_56X_CONFIG_LOG_BITS_DEFAULT, + NULL, /* Custom log map */ + X86_64_ACCTON_AS7327_56X_CONFIG_LOG_CUSTOM_BITS_DEFAULT + ); + diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.h b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.h new file mode 100644 index 000000000..894194f4a --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_log.h @@ -0,0 +1,12 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#ifndef __x86_64_accton_as7327_56x_LOG_H__ +#define __x86_64_accton_as7327_56x_LOG_H__ + +#define AIM_LOG_MODULE_NAME x86_64_accton_as7327_56x +#include + +#endif /* __x86_64_accton_as7327_56x_LOG_H__ */ diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_module.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_module.c new file mode 100644 index 000000000..58228c72d --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_module.c @@ -0,0 +1,24 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#include "x86_64_accton_as7327_56x_log.h" + +static int +datatypes_init__(void) +{ +#define x86_64_accton_as7327_56x_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_as7327_56x_module_init__(void) +{ + AIM_LOG_STRUCT_REGISTER(); + datatypes_init__(); +} + +int __onlp_platform_version__ = 1; diff --git a/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_ucli.c b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_ucli.c new file mode 100644 index 000000000..3158ec5a7 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/onlp/builds/x86_64_accton_as7327_56x/module/src/x86_64_accton_as7327_56x_ucli.c @@ -0,0 +1,50 @@ +/**************************************************************************//** + * + * + * + *****************************************************************************/ +#include + +#if x86_64_accton_as7327_56x_CONFIG_INCLUDE_UCLI == 1 + +#include +#include +#include + +static ucli_status_t +x86_64_accton_as7327_56x_ucli_ucli__config__(ucli_context_t* uc) +{ + UCLI_HANDLER_MACRO_MODULE_CONFIG(x86_64_accton_as7327_56x) +} + +/* */ +/* */ + +static ucli_module_t +x86_64_accton_as7327_56x_ucli_module__ = + { + "x86_64_accton_as7327_56x_ucli", + NULL, + x86_64_accton_as7327_56x_ucli_ucli_handlers__, + NULL, + NULL, + }; + +ucli_node_t* +x86_64_accton_as7327_56x_ucli_node_create(void) +{ + ucli_node_t* n; + ucli_module_init(&x86_64_accton_as7327_56x_ucli_module__); + n = ucli_node_create("x86_64_accton_as7327_56x", NULL, &x86_64_accton_as7327_56x_ucli_module__); + ucli_node_subnode_add(n, ucli_module_log_node_create("x86_64_accton_as7327_56x")); + return n; +} + +#else +void* +x86_64_accton_as7327_56x_ucli_node_create(void) +{ + return NULL; +} +#endif + diff --git a/packages/platforms/accton/x86-64/as7327-56x/platform-config/Makefile b/packages/platforms/accton/x86-64/as7327-56x/platform-config/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/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/as7327-56x/platform-config/r0/Makefile b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/Makefile new file mode 100644 index 000000000..003238cf6 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/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/as7327-56x/platform-config/r0/PKG.yml b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/PKG.yml new file mode 100644 index 000000000..9197425f8 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/PKG.yml @@ -0,0 +1 @@ +!include $ONL_TEMPLATES/platform-config-platform.yml ARCH=amd64 VENDOR=accton BASENAME=x86-64-accton-as7327-56x REVISION=r0 diff --git a/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/lib/x86-64-accton-as7327-56x-r0.yml b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/lib/x86-64-accton-as7327-56x-r0.yml new file mode 100644 index 000000000..bad5c92e5 --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/lib/x86-64-accton-as7327-56x-r0.yml @@ -0,0 +1,39 @@ +--- + +###################################################################### +# +# platform-config for AS7327 +# +###################################################################### + +x86-64-accton-as7327-56x-r0: + + grub: + + serial: >- + --port=0x3f8 + --speed=115200 + --word=8 + --parity=no + --stop=1 + + kernel: + <<: *kernel-6-12 + + args: >- + nopat + console=ttyS0,115200n8 + intel_iommu=off + pcie_aspm=off + + interfaces: + ma1: + name: "eth0" + eno1: + name: "eth1" + enp3s0f1: + name: "eth2" + enp4s0f0: + name: "eth3" + enp4s0f1: + name: "eth4" diff --git a/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/python/x86_64_accton_as7327_56x_r0/__init__.py b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/python/x86_64_accton_as7327_56x_r0/__init__.py new file mode 100755 index 000000000..a81ae8dae --- /dev/null +++ b/packages/platforms/accton/x86-64/as7327-56x/platform-config/r0/src/python/x86_64_accton_as7327_56x_r0/__init__.py @@ -0,0 +1,125 @@ +from onl.platform.base import * +from onl.platform.accton import * + +import os.path +import time + +BMC_ENABLE_NODE="/sys/bus/i2c/devices/i2c-157/157-0062/bmc_enable" + +class OnlPlatform_x86_64_accton_as7327_56x_r0(OnlPlatformAccton, + OnlPlatformPortConfig_48x25_8x100): + + PLATFORM='x86-64-accton-as7327-56x-r0' + MODEL="AS7327-56X" + SYS_OBJECT_ID=".7327.56" + + def add_path(self, bin_path): + path = os.environ['PATH'] + path_exist = False + for sub_path in path.split(":"): + if bin_path == sub_path: + path_exist = True + + if(not path_exist): + new_path = bin_path + ":" + path + os.environ['PATH'] = new_path + + def baseconfig(self): + bmc_enabled = False + + self.insmod('optoe') + self.insmod('at24_as7327_56x') + for m in [ 'fpga', 'cpld', 'leds' ]: + self.insmod("x86-64-accton-as7327-56x-%s.ko" % m) + + self.new_i2c_devices([ + # initialize CPLD + ('as7327_56x_cpld1', 0x62, 157), + ('as7327_56x_cpld2', 0x64, 158), + ]) + + for i in range(5): + time.sleep(1) + if os.path.exists(BMC_ENABLE_NODE): + break + else: + print("cannot access %s" % BMC_ENABLE_NODE) + + try: + # Access CPLD bmc enable + with open(BMC_ENABLE_NODE, 'r') as f: + contents = f.read().strip() + if contents == "1": + bmc_enabled = 1 + else: + bmc_enabled = 0 + except Exception as e: + print("An unexpected error occurred: %s" % e) + + if bmc_enabled: + self.insmod("x86-64-accton-as7327-56x-psu_bmc.ko") + self.insmod("x86-64-accton-as7327-56x-thermal_bmc.ko") + + else: + self.insmod("x86-64-accton-as7327-56x-psu.ko") + + self.new_i2c_device('pca9548', 0x70, 0) + + self.new_i2c_devices([ + # inititate LM75 + ('lm75', 0x4a, 7), + ('lm75', 0x4b, 7), + ('lm75', 0x4c, 7), + ]) + + self.new_i2c_devices([ + # initiate PSU-1 + ('as7327_56x_psu1', 0x5a, 1), + # initiate PSU-2 + ('as7327_56x_psu2', 0x59, 2), + ]) + + self.new_i2c_device('accton_24c64', 0x51, 6) + self.new_i2c_device('accton_24c64', 0x55, 6) + + # Wait for ready of drivers intialization + time.sleep(5) + + bin_path = "/lib/platform-config/current/onl/bin" + self.add_path(bin_path) + sbin_path = "/sbin" + self.add_path(sbin_path) + + # Thermal policy executes overhere + os.system("sudo /usr/bin/python -u {}/fan_monitor.py &".format(bin_path)) + os.system("sudo /usr/bin/python -u {}/monitor_led.py &".format(bin_path)) + + sfp_map = [ + 101,102,103,104, + 105,106,107,108, + 109,110,111,112, + 113,114,115,116, + 117,118,119,120, + 121,122,123,124, + 125,126,127,128, + 129,130,131,132, + 133,134,135,136, + 137,138,139,140, + 141,142,143,144, + 145,146,147,148, + 149,150,151,152, + 153,154,155,156 + ] + + for i in range(0, len(sfp_map)): + if i < 48: # initialize SFP+ port 1~48 + self.new_i2c_device('optoe2', 0x50, sfp_map[i]) + else: # initialize QSFP port 49~56 + self.new_i2c_device('optoe1', 0x50, sfp_map[i]) + + subprocess.call('echo port%d > /sys/bus/i2c/devices/%d-0050/port_name' % (i+1, sfp_map[i]), shell=True) + + self.new_i2c_device('accton_24c64', 0x50, 0) + + + return True