mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-24 16:57:02 +00:00
Merge pull request #514 from brandonchuang/as4610_54t_b
[as4610] Modify oom related part to support kernel 4.14
This commit is contained in:
@@ -8,11 +8,18 @@ class OnlPlatform_arm_accton_as4610_30_r0(OnlPlatformAccton,
|
||||
SYS_OBJECT_ID=".4610.30"
|
||||
|
||||
def baseconfig(self):
|
||||
self.insmod("accton_i2c_cpld")
|
||||
self.new_i2c_device("as4610_30_cpld", 0x30, 0)
|
||||
self.insmod("accton_as4610_sfp")
|
||||
self.insmod("accton_as4610_cpld")
|
||||
self.insmod("accton_as4610_psu")
|
||||
self.insmod("accton_as4610_fan")
|
||||
self.insmod("accton_as4610_leds")
|
||||
self.insmod("ym2651y")
|
||||
self.insmod("optoe")
|
||||
|
||||
subprocess.call('echo port25 > /sys/bus/i2c/devices/2-0050/port_name', shell=True)
|
||||
subprocess.call('echo port26 > /sys/bus/i2c/devices/3-0050/port_name', shell=True)
|
||||
subprocess.call('echo port27 > /sys/bus/i2c/devices/4-0050/port_name', shell=True)
|
||||
subprocess.call('echo port28 > /sys/bus/i2c/devices/5-0050/port_name', shell=True)
|
||||
subprocess.call('echo port29 > /sys/bus/i2c/devices/6-0050/port_name', shell=True)
|
||||
subprocess.call('echo port30 > /sys/bus/i2c/devices/7-0050/port_name', shell=True)
|
||||
|
||||
return True
|
||||
|
||||
@@ -8,13 +8,20 @@ class OnlPlatform_arm_accton_as4610_54_r0(OnlPlatformAccton,
|
||||
SYS_OBJECT_ID=".4610.54"
|
||||
|
||||
def baseconfig(self):
|
||||
self.insmod("accton_i2c_cpld")
|
||||
self.new_i2c_device("as4610_54_cpld", 0x30, 0)
|
||||
self.insmod("accton_as4610_sfp")
|
||||
self.insmod("accton_as4610_cpld")
|
||||
self.insmod("accton_as4610_psu")
|
||||
self.insmod("accton_as4610_fan")
|
||||
self.insmod("accton_as4610_leds")
|
||||
self.insmod("ym2651y")
|
||||
self.insmod("optoe")
|
||||
|
||||
subprocess.call('echo port49 > /sys/bus/i2c/devices/2-0050/port_name', shell=True)
|
||||
subprocess.call('echo port50 > /sys/bus/i2c/devices/3-0050/port_name', shell=True)
|
||||
subprocess.call('echo port51 > /sys/bus/i2c/devices/4-0050/port_name', shell=True)
|
||||
subprocess.call('echo port52 > /sys/bus/i2c/devices/5-0050/port_name', shell=True)
|
||||
subprocess.call('echo port53 > /sys/bus/i2c/devices/6-0050/port_name', shell=True)
|
||||
subprocess.call('echo port54 > /sys/bus/i2c/devices/7-0050/port_name', shell=True)
|
||||
|
||||
# self.new_i2c_devices(
|
||||
# [
|
||||
# ("pca9548", 0x70, 1),
|
||||
|
||||
@@ -8,11 +8,18 @@ class OnlPlatform_arm_accton_as4610_30_r0(OnlPlatformAccton,
|
||||
SYS_OBJECT_ID=".4610.30"
|
||||
|
||||
def baseconfig(self):
|
||||
self.insmod("accton_i2c_cpld")
|
||||
self.new_i2c_device("as4610_30_cpld", 0x30, 0)
|
||||
self.insmod("accton_as4610_sfp")
|
||||
self.insmod("accton_as4610_cpld")
|
||||
self.insmod("accton_as4610_psu")
|
||||
self.insmod("accton_as4610_fan")
|
||||
self.insmod("accton_as4610_leds")
|
||||
self.insmod("ym2651y")
|
||||
self.insmod("optoe")
|
||||
|
||||
subprocess.call('echo port25 > /sys/bus/i2c/devices/2-0050/port_name', shell=True)
|
||||
subprocess.call('echo port26 > /sys/bus/i2c/devices/3-0050/port_name', shell=True)
|
||||
subprocess.call('echo port27 > /sys/bus/i2c/devices/4-0050/port_name', shell=True)
|
||||
subprocess.call('echo port28 > /sys/bus/i2c/devices/5-0050/port_name', shell=True)
|
||||
subprocess.call('echo port29 > /sys/bus/i2c/devices/6-0050/port_name', shell=True)
|
||||
subprocess.call('echo port30 > /sys/bus/i2c/devices/7-0050/port_name', shell=True)
|
||||
|
||||
return True
|
||||
|
||||
@@ -8,13 +8,20 @@ class OnlPlatform_arm_accton_as4610_54_r0(OnlPlatformAccton,
|
||||
SYS_OBJECT_ID=".4610.54"
|
||||
|
||||
def baseconfig(self):
|
||||
self.insmod("accton_i2c_cpld")
|
||||
self.new_i2c_device("as4610_54_cpld", 0x30, 0)
|
||||
self.insmod("accton_as4610_sfp")
|
||||
self.insmod("accton_as4610_cpld")
|
||||
self.insmod("accton_as4610_psu")
|
||||
self.insmod("accton_as4610_fan")
|
||||
self.insmod("accton_as4610_leds")
|
||||
self.insmod("ym2651y")
|
||||
self.insmod("optoe")
|
||||
|
||||
subprocess.call('echo port49 > /sys/bus/i2c/devices/2-0050/port_name', shell=True)
|
||||
subprocess.call('echo port50 > /sys/bus/i2c/devices/3-0050/port_name', shell=True)
|
||||
subprocess.call('echo port51 > /sys/bus/i2c/devices/4-0050/port_name', shell=True)
|
||||
subprocess.call('echo port52 > /sys/bus/i2c/devices/5-0050/port_name', shell=True)
|
||||
subprocess.call('echo port53 > /sys/bus/i2c/devices/6-0050/port_name', shell=True)
|
||||
subprocess.call('echo port54 > /sys/bus/i2c/devices/7-0050/port_name', shell=True)
|
||||
|
||||
# self.new_i2c_devices(
|
||||
# [
|
||||
# ("pca9548", 0x70, 1),
|
||||
|
||||
@@ -87,8 +87,8 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp1";
|
||||
optoe@50 {
|
||||
compatible = "optoe2";
|
||||
reg = <0x50>;
|
||||
label = "port49";
|
||||
};
|
||||
@@ -99,8 +99,8 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <1>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp2";
|
||||
optoe@50 {
|
||||
compatible = "optoe2";
|
||||
reg = <0x50>;
|
||||
label = "port50";
|
||||
};
|
||||
@@ -111,8 +111,8 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <2>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp3";
|
||||
optoe@50 {
|
||||
compatible = "optoe2";
|
||||
reg = <0x50>;
|
||||
label = "port51";
|
||||
};
|
||||
@@ -123,8 +123,8 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <3>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp4";
|
||||
optoe@50 {
|
||||
compatible = "optoe2";
|
||||
reg = <0x50>;
|
||||
label = "port52";
|
||||
};
|
||||
@@ -135,9 +135,10 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <4>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp5";
|
||||
optoe@50 {
|
||||
compatible = "optoe1";
|
||||
reg = <0x50>;
|
||||
label = "port53";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -146,9 +147,10 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <5>;
|
||||
sfp_eeprom@50 {
|
||||
compatible = "accton,as4610_sfp6";
|
||||
optoe@50 {
|
||||
compatible = "optoe1";
|
||||
reg = <0x50>;
|
||||
label = "port54";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,50 +24,31 @@
|
||||
*
|
||||
***********************************************************/
|
||||
#include <onlp/platformi/sfpi.h>
|
||||
#include <onlplib/file.h>
|
||||
#include "platform_lib.h"
|
||||
|
||||
#include <arm_accton_as4610/arm_accton_as4610_config.h>
|
||||
#include "arm_accton_as4610_log.h"
|
||||
|
||||
#define MAX_SFP_PATH 64
|
||||
static char sfp_node_path[MAX_SFP_PATH] = {0};
|
||||
#define FRONT_PORT_MUX_INDEX(port) (port-46)
|
||||
#define PORT_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom"
|
||||
#define MODULE_PRESENT_FORMAT "/sys/bus/i2c/devices/0-0030/module_present_%d"
|
||||
#define MODULE_RXLOS_FORMAT "/sys/bus/i2c/devices/0-0030/module_rx_los_%d"
|
||||
#define MODULE_TXFAULT_FORMAT "/sys/bus/i2c/devices/0-0030/module_tx_fault_%d"
|
||||
#define MODULE_PRESENT_ALL_ATTR_CPLD "/sys/bus/i2c/devices/0-0030/module_present_all"
|
||||
#define MODULE_RXLOS_ALL_ATTR_CPLD "/sys/bus/i2c/devices/0-0030/module_rx_los_all"
|
||||
|
||||
static int
|
||||
sfp_node_read_int(char *node_path, int *value, int data_len)
|
||||
static int front_port_bus_index(int port)
|
||||
{
|
||||
int ret = 0;
|
||||
char buf[8];
|
||||
*value = 0;
|
||||
|
||||
ret = deviceNodeReadString(node_path, buf, sizeof(buf), data_len);
|
||||
|
||||
if (ret == 0) {
|
||||
*value = atoi(buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return (platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0) ?
|
||||
(port - 22) : /* PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0 */
|
||||
(port - 46) ; /* PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0 */
|
||||
}
|
||||
|
||||
static char*
|
||||
sfp_get_port_path_addr(int port, int addr, char *node_name)
|
||||
static int front_port_to_cpld_port(int port)
|
||||
{
|
||||
int front_port_mux_id;
|
||||
|
||||
if(platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0)
|
||||
front_port_mux_id = port - 22;
|
||||
else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/
|
||||
front_port_mux_id = port - 46;
|
||||
|
||||
sprintf(sfp_node_path, "/sys/bus/i2c/devices/%d-00%d/%s",
|
||||
front_port_mux_id, addr, node_name);
|
||||
return sfp_node_path;
|
||||
}
|
||||
|
||||
static char*
|
||||
sfp_get_port_path(int port, char *node_name)
|
||||
{
|
||||
return sfp_get_port_path_addr(port, 50, node_name);
|
||||
return (platform_id == PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0) ?
|
||||
(port - 23) : /* PLATFORM_ID_POWERPC_ACCTON_AS4610_30_R0 */
|
||||
(port - 47) ; /* PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0 */
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
@@ -115,9 +96,9 @@ onlp_sfpi_is_present(int port)
|
||||
* Return < 0 if error.
|
||||
*/
|
||||
int present;
|
||||
char* path = sfp_get_port_path(port, "sfp_is_present");
|
||||
|
||||
if (sfp_node_read_int(path, &present, 0) != 0) {
|
||||
int cpld_port = front_port_to_cpld_port(port);
|
||||
|
||||
if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, cpld_port) < 0) {
|
||||
AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", port);
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
@@ -129,7 +110,6 @@ int
|
||||
onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
{
|
||||
uint32_t byte;
|
||||
char* path;
|
||||
FILE* fp;
|
||||
int port;
|
||||
|
||||
@@ -140,18 +120,18 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/
|
||||
port = 48;
|
||||
|
||||
path = sfp_get_port_path(port, "sfp_is_present_all");
|
||||
fp = fopen(path, "r");
|
||||
|
||||
/* Read present status of each port */
|
||||
fp = fopen(MODULE_PRESENT_ALL_ATTR_CPLD, "r");
|
||||
if(fp == NULL) {
|
||||
AIM_LOG_ERROR("Unable to open the sfp_is_present_all device file.");
|
||||
AIM_LOG_ERROR("Unable to open the module_present_all device file of CPLD.");
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
int count = fscanf(fp, "%x", &byte);
|
||||
fclose(fp);
|
||||
if(count != 1) {
|
||||
/* Likely a CPLD read timeout. */
|
||||
AIM_LOG_ERROR("Unable to read all fields from the sfp_is_present_all device file.");
|
||||
AIM_LOG_ERROR("Unable to read all fields the module_present_all device file of CPLD.");
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
@@ -173,7 +153,6 @@ int
|
||||
onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
{
|
||||
uint32_t byte;
|
||||
char* path;
|
||||
FILE* fp;
|
||||
int port;
|
||||
|
||||
@@ -182,16 +161,18 @@ onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
else /*PLATFORM_ID_POWERPC_ACCTON_AS4610_54_R0*/
|
||||
port = 48;
|
||||
|
||||
path = sfp_get_port_path(port, "sfp_rx_los_all");
|
||||
fp = fopen(path, "r");
|
||||
/* Read present status of each port */
|
||||
fp = fopen(MODULE_RXLOS_ALL_ATTR_CPLD, "r");
|
||||
if(fp == NULL) {
|
||||
AIM_LOG_ERROR("Unable to open the sfp_rx_los_all device file.");
|
||||
AIM_LOG_ERROR("Unable to open the module_rx_los_all device file of CPLD.");
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
int count = fscanf(fp, "%x", &byte);
|
||||
fclose(fp);
|
||||
if(count != 1) {
|
||||
AIM_LOG_ERROR("Unable to read all fields from the sfp_rx_los_all device file.");
|
||||
/* Likely a CPLD read timeout. */
|
||||
AIM_LOG_ERROR("Unable to read all fields the module_rx_los_all device file of CPLD.");
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
@@ -212,32 +193,51 @@ onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
int
|
||||
onlp_sfpi_eeprom_read(int port, uint8_t data[256])
|
||||
{
|
||||
char* path = sfp_get_port_path(port, "sfp_eeprom");
|
||||
|
||||
/*
|
||||
* Read the SFP eeprom into data[]
|
||||
*
|
||||
* Return MISSING if SFP is missing.
|
||||
* Return OK if eeprom is read
|
||||
*/
|
||||
int size = 0;
|
||||
memset(data, 0, 256);
|
||||
|
||||
if (deviceNodeReadBinary(path, (char*)data, 256, 256) != 0) {
|
||||
if(onlp_file_read(data, 256, &size, PORT_EEPROM_FORMAT, front_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])
|
||||
{
|
||||
char* path = sfp_get_port_path_addr(port, 51, "sfp_eeprom");
|
||||
memset(data, 0, 256);
|
||||
FILE* fp;
|
||||
char file[64] = {0};
|
||||
|
||||
sprintf(file, PORT_EEPROM_FORMAT, front_port_bus_index(port));
|
||||
fp = fopen(file, "r");
|
||||
if(fp == NULL) {
|
||||
AIM_LOG_ERROR("Unable to open the eeprom device file of port(%d)", port);
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
if (deviceNodeReadBinary(path, (char*)data, 256, 256) != 0) {
|
||||
AIM_LOG_INFO("Unable to read eeprom from port(%d)\r\n", port);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -247,45 +247,24 @@ onlp_sfpi_dom_read(int port, uint8_t data[256])
|
||||
int
|
||||
onlp_sfpi_control_set(int port, onlp_sfp_control_t control, int value)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch(control)
|
||||
{
|
||||
case ONLP_SFP_CONTROL_TX_DISABLE:
|
||||
{
|
||||
char* path = sfp_get_port_path(port, "sfp_tx_disable");
|
||||
|
||||
if (deviceNodeWriteInt(path, value, 0) != 0) {
|
||||
AIM_LOG_ERROR("Unable to set tx_disable status to port(%d)\r\n", port);
|
||||
rv = ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
else {
|
||||
rv = ONLP_STATUS_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
rv = ONLP_STATUS_E_UNSUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
return ONLP_STATUS_E_UNSUPPORTED;
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value)
|
||||
{
|
||||
int rv;
|
||||
char* path = NULL;
|
||||
int cpld_port = front_port_to_cpld_port(port);
|
||||
|
||||
if (cpld_port > 4) {
|
||||
return ONLP_STATUS_E_UNSUPPORTED;
|
||||
}
|
||||
|
||||
switch(control)
|
||||
{
|
||||
case ONLP_SFP_CONTROL_RX_LOS:
|
||||
{
|
||||
path = sfp_get_port_path(port, "sfp_rx_los");
|
||||
|
||||
if (sfp_node_read_int(path, value, 0) != 0) {
|
||||
if (onlp_file_read_int(value, MODULE_RXLOS_FORMAT, cpld_port) < 0) {
|
||||
AIM_LOG_ERROR("Unable to read rx_los status from port(%d)\r\n", port);
|
||||
rv = ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
@@ -297,9 +276,7 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value)
|
||||
|
||||
case ONLP_SFP_CONTROL_TX_FAULT:
|
||||
{
|
||||
path = sfp_get_port_path(port, "sfp_tx_fault");
|
||||
|
||||
if (sfp_node_read_int(path, value, 0) != 0) {
|
||||
if (onlp_file_read_int(value, MODULE_TXFAULT_FORMAT, cpld_port) < 0) {
|
||||
AIM_LOG_ERROR("Unable to read tx_fault status from port(%d)\r\n", port);
|
||||
rv = ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
@@ -309,20 +286,6 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value)
|
||||
break;
|
||||
}
|
||||
|
||||
case ONLP_SFP_CONTROL_TX_DISABLE:
|
||||
{
|
||||
path = sfp_get_port_path(port, "sfp_tx_disable");
|
||||
|
||||
if (sfp_node_read_int(path, value, 0) != 0) {
|
||||
AIM_LOG_ERROR("Unable to read tx_disabled status from port(%d)\r\n", port);
|
||||
rv = ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
else {
|
||||
rv = ONLP_STATUS_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
rv = ONLP_STATUS_E_UNSUPPORTED;
|
||||
}
|
||||
@@ -330,9 +293,9 @@ onlp_sfpi_control_get(int port, onlp_sfp_control_t control, int* value)
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
onlp_sfpi_denit(void)
|
||||
{
|
||||
return ONLP_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
/*
|
||||
* Copyright (C) Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* This module supports the accton cpld that hold the channel select
|
||||
* mechanism for other i2c slave devices, such as SFP.
|
||||
* This includes the:
|
||||
* Accton as4610_54 CPLD
|
||||
*
|
||||
* Based on:
|
||||
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
|
||||
* 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 <bkuschak@yahoo.com>
|
||||
* and
|
||||
* pca9540.c from Jean Delvare <khali@linux-fr.org>.
|
||||
*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define I2C_RW_RETRY_COUNT 10
|
||||
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
|
||||
#define AS4610_CPLD_SLAVE_ADDR 0x30
|
||||
#define AS4610_CPLD_PID_OFFSET 0x01 /* Product ID offset */
|
||||
|
||||
static LIST_HEAD(cpld_client_list);
|
||||
static struct mutex list_lock;
|
||||
|
||||
struct cpld_client_node {
|
||||
struct i2c_client *client;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
enum cpld_type {
|
||||
as4610_54_cpld
|
||||
};
|
||||
|
||||
enum as4610_product_id_e {
|
||||
PID_AS4610_30T,
|
||||
PID_AS4610_30P,
|
||||
PID_AS4610_54T,
|
||||
PID_AS4610_54P,
|
||||
PID_RESERVED,
|
||||
PID_AS4610_54T_B,
|
||||
PID_UNKNOWN
|
||||
};
|
||||
|
||||
struct as4610_54_cpld_data {
|
||||
enum cpld_type type;
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
};
|
||||
|
||||
static const struct i2c_device_id as4610_54_cpld_id[] = {
|
||||
{ "as4610_54_cpld", as4610_54_cpld },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, as4610_54_cpld_id);
|
||||
|
||||
#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index
|
||||
#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index
|
||||
#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index
|
||||
#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index
|
||||
|
||||
enum as4610_54_cpld1_sysfs_attributes {
|
||||
CPLD_VERSION,
|
||||
PRODUCT_ID,
|
||||
ACCESS,
|
||||
MODULE_PRESENT_ALL,
|
||||
MODULE_RXLOS_ALL,
|
||||
/* transceiver attributes */
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(1),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(2),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(3),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(4),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(5),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(6),
|
||||
TRANSCEIVER_TXDISABLE_ATTR_ID(1),
|
||||
TRANSCEIVER_TXDISABLE_ATTR_ID(2),
|
||||
TRANSCEIVER_TXDISABLE_ATTR_ID(3),
|
||||
TRANSCEIVER_TXDISABLE_ATTR_ID(4),
|
||||
TRANSCEIVER_RXLOS_ATTR_ID(1),
|
||||
TRANSCEIVER_RXLOS_ATTR_ID(2),
|
||||
TRANSCEIVER_RXLOS_ATTR_ID(3),
|
||||
TRANSCEIVER_RXLOS_ATTR_ID(4),
|
||||
TRANSCEIVER_TXFAULT_ATTR_ID(1),
|
||||
TRANSCEIVER_TXFAULT_ATTR_ID(2),
|
||||
TRANSCEIVER_TXFAULT_ATTR_ID(3),
|
||||
TRANSCEIVER_TXFAULT_ATTR_ID(4),
|
||||
};
|
||||
|
||||
/* sysfs attributes for hwmon
|
||||
*/
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_present_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_version(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_product_id(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
static int as4610_54_cpld_read_internal(struct i2c_client *client, u8 reg);
|
||||
static int as4610_54_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value);
|
||||
|
||||
/* transceiver attributes */
|
||||
#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index)
|
||||
#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr
|
||||
|
||||
#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \
|
||||
static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_TXFAULT_##index)
|
||||
#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \
|
||||
&sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \
|
||||
&sensor_dev_attr_module_tx_fault_##index.dev_attr.attr
|
||||
|
||||
static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION);
|
||||
static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS);
|
||||
static SENSOR_DEVICE_ATTR(product_id, S_IRUGO, show_product_id, NULL, PRODUCT_ID);
|
||||
/* transceiver attributes */
|
||||
static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL);
|
||||
static SENSOR_DEVICE_ATTR(module_rx_los_all, S_IRUGO, show_rxlos_all, NULL, MODULE_RXLOS_ALL);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6);
|
||||
|
||||
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(1);
|
||||
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(2);
|
||||
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(3);
|
||||
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(4);
|
||||
|
||||
static struct attribute *as4610_54_cpld_attributes[] = {
|
||||
&sensor_dev_attr_version.dev_attr.attr,
|
||||
&sensor_dev_attr_access.dev_attr.attr,
|
||||
&sensor_dev_attr_product_id.dev_attr.attr,
|
||||
/* transceiver attributes */
|
||||
&sensor_dev_attr_module_present_all.dev_attr.attr,
|
||||
&sensor_dev_attr_module_rx_los_all.dev_attr.attr,
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(1),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(2),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(3),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(4),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(5),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(6),
|
||||
DECLARE_SFP_TRANSCEIVER_ATTR(1),
|
||||
DECLARE_SFP_TRANSCEIVER_ATTR(2),
|
||||
DECLARE_SFP_TRANSCEIVER_ATTR(3),
|
||||
DECLARE_SFP_TRANSCEIVER_ATTR(4),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group as4610_54_cpld_group = {
|
||||
.attrs = as4610_54_cpld_attributes,
|
||||
};
|
||||
|
||||
static ssize_t show_present_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
int i, status, present = 0;
|
||||
u8 regs[] = {0x2, 0x3, 0x21};
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as4610_54_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
status = as4610_54_cpld_read_internal(client, regs[i]);
|
||||
|
||||
if (status < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
present |= (status & BIT(6)) >> 6; /* port 25/49 */
|
||||
present |= (status & BIT(2)) >> 1; /* port 26/50 */
|
||||
break;
|
||||
case 1:
|
||||
present |= (status & BIT(6)) >> 4; /* port 27/51 */
|
||||
present |= (status & BIT(2)) << 1; /* port 28/52 */
|
||||
break;
|
||||
case 2:
|
||||
present |= (status & BIT(0)) << 4; /* port 29/53 */
|
||||
present |= (status & BIT(4)) << 1; /* port 30/54 */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%.2x\n", present);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t show_rxlos_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
int i, status, rx_los = 0;
|
||||
u8 regs[] = {0x2, 0x3};
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as4610_54_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
status = as4610_54_cpld_read_internal(client, regs[i]);
|
||||
|
||||
if (status < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
rx_los |= (status & BIT(4)) >> 4; /* port 25/49 rx_los */
|
||||
rx_los |= (status & BIT(0)) << 1; /* port 26/50 rx_los */
|
||||
break;
|
||||
case 1:
|
||||
rx_los |= (status & BIT(4)) >> 2; /* port 27/51 rx_los */
|
||||
rx_los |= (status & BIT(0)) << 3; /* port 28/52 rx_los */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
/* Return values 25/49 -> 28/52 in order */
|
||||
return sprintf(buf, "%.2x\n", rx_los);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as4610_54_cpld_data *data = i2c_get_clientdata(client);
|
||||
int status = 0;
|
||||
u8 reg = 0, mask = 0;
|
||||
|
||||
switch (attr->index) {
|
||||
case MODULE_PRESENT_1:
|
||||
reg = 0x2;
|
||||
mask = 0x40;
|
||||
break;
|
||||
case MODULE_PRESENT_2:
|
||||
reg = 0x2;
|
||||
mask = 0x4;
|
||||
break;
|
||||
case MODULE_PRESENT_3:
|
||||
reg = 0x3;
|
||||
mask = 0x40;
|
||||
break;
|
||||
case MODULE_PRESENT_4:
|
||||
reg = 0x3;
|
||||
mask = 0x4;
|
||||
break;
|
||||
case MODULE_PRESENT_5:
|
||||
reg = 0x21;
|
||||
mask = 0x1;
|
||||
break;
|
||||
case MODULE_PRESENT_6:
|
||||
reg = 0x21;
|
||||
mask = 0x10;
|
||||
break;
|
||||
case MODULE_TXFAULT_1:
|
||||
reg = 0x2;
|
||||
mask = 0x20;
|
||||
break;
|
||||
case MODULE_TXFAULT_2:
|
||||
reg = 0x2;
|
||||
mask = 0x2;
|
||||
break;
|
||||
case MODULE_TXFAULT_3:
|
||||
reg = 0x3;
|
||||
mask = 0x20;
|
||||
break;
|
||||
case MODULE_TXFAULT_4:
|
||||
reg = 0x3;
|
||||
mask = 0x2;
|
||||
break;
|
||||
case MODULE_RXLOS_1:
|
||||
reg = 0x2;
|
||||
mask = 0x10;
|
||||
break;
|
||||
case MODULE_RXLOS_2:
|
||||
reg = 0x2;
|
||||
mask = 0x1;
|
||||
break;
|
||||
case MODULE_RXLOS_3:
|
||||
reg = 0x3;
|
||||
mask = 0x10;
|
||||
break;
|
||||
case MODULE_RXLOS_4:
|
||||
reg = 0x3;
|
||||
mask = 0x1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = as4610_54_cpld_read_internal(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(status & mask));
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int status;
|
||||
u32 addr, val;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as4610_54_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr > 0xFF || val > 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = as4610_54_cpld_write_internal(client, addr, val);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void as4610_54_cpld_add_client(struct i2c_client *client)
|
||||
{
|
||||
struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
|
||||
|
||||
if (!node) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr);
|
||||
return;
|
||||
}
|
||||
|
||||
node->client = client;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_add(&node->list, &cpld_client_list);
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static void as4610_54_cpld_remove_client(struct i2c_client *client)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client == client) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_del(list_node);
|
||||
kfree(cpld_node);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static ssize_t show_product_id(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, 0x1);
|
||||
if (val < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", (val & 0xF));
|
||||
}
|
||||
|
||||
static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, 0xB);
|
||||
|
||||
if (val < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x%x) reg(0xB) err %d\n", client->addr, val);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d", val);
|
||||
}
|
||||
|
||||
/* I2C init/probing/exit functions */
|
||||
static int as4610_54_cpld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||
struct as4610_54_cpld_data *data;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||
goto exit;
|
||||
|
||||
data = kzalloc(sizeof(struct as4610_54_cpld_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->type = id->driver_data;
|
||||
/* Bring QSFPs out of reset */
|
||||
as4610_54_cpld_write_internal(client, 0x2A, 0);
|
||||
|
||||
ret = sysfs_create_group(&client->dev.kobj, &as4610_54_cpld_group);
|
||||
if (ret) {
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
as4610_54_cpld_add_client(client);
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int as4610_54_cpld_remove(struct i2c_client *client)
|
||||
{
|
||||
struct as4610_54_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
as4610_54_cpld_remove_client(client);
|
||||
|
||||
/* Remove sysfs hooks */
|
||||
sysfs_remove_group(&client->dev.kobj, &as4610_54_cpld_group);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int as4610_54_cpld_read_internal(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_read_byte_data(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int as4610_54_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_write_byte_data(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int as4610_54_cpld_read(unsigned short cpld_addr, u8 reg)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EPERM;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = as4610_54_cpld_read_internal(cpld_node->client, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(as4610_54_cpld_read);
|
||||
|
||||
int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EIO;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = as4610_54_cpld_write_internal(cpld_node->client, reg, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(as4610_54_cpld_write);
|
||||
|
||||
int as4610_product_id(void)
|
||||
{
|
||||
int pid = as4610_54_cpld_read(AS4610_CPLD_SLAVE_ADDR, AS4610_CPLD_PID_OFFSET);
|
||||
pid &= 0xF;
|
||||
|
||||
if (pid < PID_AS4610_30T || pid > PID_AS4610_54T_B || pid == PID_RESERVED) {
|
||||
return PID_UNKNOWN;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
EXPORT_SYMBOL(as4610_product_id);
|
||||
|
||||
int as4610_is_poe_system(void)
|
||||
{
|
||||
int pid = as4610_product_id();
|
||||
return (pid == PID_AS4610_30P || pid == PID_AS4610_54P);
|
||||
}
|
||||
EXPORT_SYMBOL(as4610_is_poe_system);
|
||||
|
||||
static struct i2c_driver as4610_54_cpld_driver = {
|
||||
.driver = {
|
||||
.name = "as4610_54_cpld",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = as4610_54_cpld_probe,
|
||||
.remove = as4610_54_cpld_remove,
|
||||
.id_table = as4610_54_cpld_id,
|
||||
};
|
||||
|
||||
static int __init as4610_54_cpld_init(void)
|
||||
{
|
||||
mutex_init(&list_lock);
|
||||
return i2c_add_driver(&as4610_54_cpld_driver);
|
||||
}
|
||||
|
||||
static void __exit as4610_54_cpld_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as4610_54_cpld_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("Accton I2C CPLD driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(as4610_54_cpld_init);
|
||||
module_exit(as4610_54_cpld_exit);
|
||||
|
||||
@@ -29,15 +29,16 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "accton_i2c_cpld.h"
|
||||
|
||||
#define DRVNAME "as4610_fan"
|
||||
|
||||
|
||||
static struct as4610_fan_data *as4610_fan_update_device(struct device *dev);
|
||||
static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
extern int as4610_54_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
extern int as4610_product_id(void);
|
||||
|
||||
/* fan related data, the index should match sysfs_fan_attributes
|
||||
*/
|
||||
@@ -74,6 +75,16 @@ enum sysfs_fan_attributes {
|
||||
FAN2_FAULT
|
||||
};
|
||||
|
||||
enum as4610_product_id_e {
|
||||
PID_AS4610_30T,
|
||||
PID_AS4610_30P,
|
||||
PID_AS4610_54T,
|
||||
PID_AS4610_54P,
|
||||
PID_RESERVED,
|
||||
PID_AS4610_54T_B,
|
||||
PID_UNKNOWN
|
||||
};
|
||||
|
||||
/* Define attributes
|
||||
*/
|
||||
#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \
|
||||
@@ -113,12 +124,12 @@ static struct attribute *as4610_fan_attributes[] = {
|
||||
|
||||
static int as4610_fan_read_value(u8 reg)
|
||||
{
|
||||
return accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, reg);
|
||||
return as4610_54_cpld_read(0x30, reg);
|
||||
}
|
||||
|
||||
static int as4610_fan_write_value(u8 reg, u8 value)
|
||||
{
|
||||
return accton_i2c_cpld_write(AS4610_CPLD_SLAVE_ADDR, reg, value);
|
||||
return as4610_54_cpld_write(0x30, reg, value);
|
||||
}
|
||||
|
||||
/* fan utility functions
|
||||
@@ -290,6 +301,27 @@ static struct platform_driver as4610_fan_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int as4610_number_of_system_fan(void)
|
||||
{
|
||||
int nFan = 0;
|
||||
int pid = as4610_product_id();
|
||||
|
||||
switch (pid) {
|
||||
case PID_AS4610_30P:
|
||||
case PID_AS4610_54P:
|
||||
nFan = 1;
|
||||
break;
|
||||
case PID_AS4610_54T_B:
|
||||
nFan = 2;
|
||||
break;
|
||||
default:
|
||||
nFan = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return nFan;
|
||||
}
|
||||
|
||||
static int __init as4610_fan_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -342,3 +374,4 @@ module_exit(as4610_fan_exit);
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("as4610_fan driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "accton_i2c_cpld.h"
|
||||
|
||||
//extern void led_classdev_unregister(struct led_classdev *led_cdev);
|
||||
//extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
|
||||
extern int as4610_54_cpld_read (unsigned short cpld_addr, u8 reg);
|
||||
extern int as4610_54_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
extern int as4610_product_id(void);
|
||||
|
||||
#define DRVNAME "as4610_led"
|
||||
|
||||
@@ -93,6 +93,16 @@ static const u8 led_reg[] = {
|
||||
0x33, /* STK1-2/Fan/PoE/Alarm LED */
|
||||
};
|
||||
|
||||
enum as4610_product_id_e {
|
||||
PID_AS4610_30T,
|
||||
PID_AS4610_30P,
|
||||
PID_AS4610_54T,
|
||||
PID_AS4610_54P,
|
||||
PID_RESERVED,
|
||||
PID_AS4610_54T_B,
|
||||
PID_UNKNOWN
|
||||
};
|
||||
|
||||
enum led_type {
|
||||
LED_TYPE_SYS,
|
||||
LED_TYPE_PRI,
|
||||
@@ -145,12 +155,12 @@ enum led_light_mode {
|
||||
|
||||
static int as4610_led_read_value(u8 reg)
|
||||
{
|
||||
return accton_i2c_cpld_read(0x30, reg);
|
||||
return as4610_54_cpld_read(0x30, reg);
|
||||
}
|
||||
|
||||
static int as4610_led_write_value(u8 reg, u8 value)
|
||||
{
|
||||
return accton_i2c_cpld_write(0x30, reg, value);
|
||||
return as4610_54_cpld_write(0x30, reg, value);
|
||||
}
|
||||
|
||||
static void as4610_led_update(void)
|
||||
@@ -672,3 +682,4 @@ module_exit(as4610_led_exit);
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("as4610_led driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static int as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data,int data_len);
|
||||
extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int as4610_54_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
|
||||
/* Addresses scanned
|
||||
*/
|
||||
@@ -227,7 +227,7 @@ static struct as4610_psu_data *as4610_psu_update_device(struct device *dev)
|
||||
dev_dbg(&client->dev, "Starting as4610 update\n");
|
||||
|
||||
/* Read psu status */
|
||||
status = accton_i2c_cpld_read(0x30, 0x11);
|
||||
status = as4610_54_cpld_read(0x30, 0x11);
|
||||
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "cpld reg 0x30 err %d\n", status);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* A hwmon driver for the accton_i2c_cpld
|
||||
*
|
||||
* Copyright (C) 2013 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/dmi.h>
|
||||
#include "accton_i2c_cpld.h"
|
||||
|
||||
static LIST_HEAD(cpld_client_list);
|
||||
static struct mutex list_lock;
|
||||
|
||||
enum cpld_device_id {
|
||||
as4610_30_cpld,
|
||||
as4610_54_cpld
|
||||
};
|
||||
|
||||
struct cpld_data {
|
||||
enum cpld_device_id id;
|
||||
};
|
||||
|
||||
struct cpld_client_node {
|
||||
struct i2c_client *client;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* Addresses scanned for accton_i2c_cpld
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END };
|
||||
|
||||
static u8 cpld_product_id_offset(enum cpld_device_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case as4610_30_cpld:
|
||||
case as4610_54_cpld:
|
||||
return 0x1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpld_has_product_id(const struct i2c_device_id *dev_id)
|
||||
{
|
||||
return (dev_id->driver_data == as4610_30_cpld) ||
|
||||
(dev_id->driver_data == as4610_54_cpld);
|
||||
}
|
||||
|
||||
static ssize_t show_cpld_product_id(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, cpld_product_id_offset(data->id));
|
||||
if (val < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", client->addr, cpld_product_id_offset(data->id), val);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", (val & 0xF));
|
||||
}
|
||||
|
||||
static u8 cpld_version_offset(enum cpld_device_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case as4610_30_cpld:
|
||||
case as4610_54_cpld:
|
||||
return 0xB;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_cpld_version(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, cpld_version_offset(data->id));
|
||||
if (val < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x%x) reg(0xB) err %d\n", client->addr, val);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static void accton_i2c_cpld_add_client(struct i2c_client *client, enum cpld_device_id id)
|
||||
{
|
||||
struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
|
||||
struct cpld_data *data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL);
|
||||
|
||||
if (!node) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_data (0x%x)\n", client->addr);
|
||||
return;
|
||||
}
|
||||
|
||||
data->id = id;
|
||||
i2c_set_clientdata(client, data);
|
||||
node->client = client;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_add(&node->list, &cpld_client_list);
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static void accton_i2c_cpld_remove_client(struct i2c_client *client)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client == client) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_del(list_node);
|
||||
kfree(cpld_node);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL);
|
||||
static struct device_attribute pid = __ATTR(product_id, 0600, show_cpld_product_id, NULL);
|
||||
|
||||
static int accton_i2c_cpld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr);
|
||||
status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
status = sysfs_create_file(&client->dev.kobj, &ver.attr);
|
||||
if (status) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (cpld_has_product_id(dev_id)) {
|
||||
status = sysfs_create_file(&client->dev.kobj, &pid.attr);
|
||||
if (status) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "chip found\n");
|
||||
accton_i2c_cpld_add_client(client, (enum cpld_device_id)dev_id->driver_data);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int accton_i2c_cpld_remove(struct i2c_client *client)
|
||||
{
|
||||
sysfs_remove_file(&client->dev.kobj, &ver.attr);
|
||||
accton_i2c_cpld_remove_client(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id accton_i2c_cpld_id[] = {
|
||||
{ "as4610_30_cpld", as4610_30_cpld },
|
||||
{ "as4610_54_cpld", as4610_54_cpld },
|
||||
{ /* LIST END */}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id);
|
||||
|
||||
static struct i2c_driver accton_i2c_cpld_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "accton_i2c_cpld",
|
||||
},
|
||||
.probe = accton_i2c_cpld_probe,
|
||||
.remove = accton_i2c_cpld_remove,
|
||||
.id_table = accton_i2c_cpld_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EPERM;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_read_byte_data(cpld_node->client, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(accton_i2c_cpld_read);
|
||||
|
||||
int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EIO;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(accton_i2c_cpld_write);
|
||||
|
||||
static int __init accton_i2c_cpld_init(void)
|
||||
{
|
||||
mutex_init(&list_lock);
|
||||
return i2c_add_driver(&accton_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
static void __exit accton_i2c_cpld_exit(void)
|
||||
{
|
||||
i2c_del_driver(&accton_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("accton_i2c_cpld driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(accton_i2c_cpld_init);
|
||||
module_exit(accton_i2c_cpld_exit);
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* A hwmon driver for the accton_i2c_cpld
|
||||
*
|
||||
* Copyright (C) 2016 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
#define AS4610_CPLD_SLAVE_ADDR 0x30
|
||||
#define AS4610_CPLD_PID_OFFSET 0x01 /* Product ID offset */
|
||||
|
||||
enum as4610_product_id_e {
|
||||
PID_AS4610_30T,
|
||||
PID_AS4610_30P,
|
||||
PID_AS4610_54T,
|
||||
PID_AS4610_54P,
|
||||
PID_RESERVED,
|
||||
PID_AS4610_54T_B,
|
||||
PID_UNKNOWN
|
||||
};
|
||||
|
||||
static inline int as4610_product_id(void)
|
||||
{
|
||||
int pid = accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, AS4610_CPLD_PID_OFFSET);
|
||||
pid &= 0xF;
|
||||
|
||||
if (pid < PID_AS4610_30T || pid > PID_AS4610_54T_B || pid == PID_RESERVED) {
|
||||
return PID_UNKNOWN;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static inline int as4610_is_poe_system(void)
|
||||
{
|
||||
int pid = as4610_product_id();
|
||||
return (pid == PID_AS4610_30P || pid == PID_AS4610_54P);
|
||||
}
|
||||
|
||||
static inline int as4610_number_of_system_fan(void)
|
||||
{
|
||||
int nFan = 0;
|
||||
int pid = as4610_product_id();
|
||||
|
||||
switch (pid) {
|
||||
case PID_AS4610_30P:
|
||||
case PID_AS4610_54P:
|
||||
nFan = 1;
|
||||
break;
|
||||
case PID_AS4610_54T_B:
|
||||
nFan = 2;
|
||||
break;
|
||||
default:
|
||||
nFan = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return nFan;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user