3289 lines
95 KiB
C
3289 lines
95 KiB
C
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/dmi.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include "inv_swps.h"
|
||
|
|
||
|
static int ctl_major;
|
||
|
static int port_major;
|
||
|
static int ioexp_total;
|
||
|
static int port_total;
|
||
|
static int block_polling;
|
||
|
static int auto_config;
|
||
|
static int flag_i2c_reset;
|
||
|
static int flag_mod_state;
|
||
|
static unsigned gpio_rest_mux;
|
||
|
static int gpio_base = 0;
|
||
|
static struct class *swp_class_p = NULL;
|
||
|
static struct inv_platform_s *platform_p = NULL;
|
||
|
static struct inv_ioexp_layout_s *ioexp_layout = NULL;
|
||
|
static struct inv_port_layout_s *port_layout = NULL;
|
||
|
int io_no_init = 0;
|
||
|
module_param(io_no_init, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||
|
static void swp_polling_worker(struct work_struct *work);
|
||
|
static DECLARE_DELAYED_WORK(swp_polling, swp_polling_worker);
|
||
|
|
||
|
static int reset_i2c_topology(void);
|
||
|
|
||
|
static int
|
||
|
__swp_match(struct device *dev,
|
||
|
#ifdef SWPS_KERN_VER_AF_3_10
|
||
|
|
||
|
const void *data){
|
||
|
#else
|
||
|
void *data){
|
||
|
#endif
|
||
|
|
||
|
char *name = (char *)data;
|
||
|
if (strcmp(dev_name(dev), name) == 0)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
struct device *
|
||
|
get_swpdev_by_name(char *name){
|
||
|
struct device *dev = class_find_device(swp_class_p,
|
||
|
NULL,
|
||
|
name,
|
||
|
__swp_match);
|
||
|
return dev;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
sscanf_2_int(const char *buf) {
|
||
|
|
||
|
int result = -EBFONT;
|
||
|
char *hex_tag = "0x";
|
||
|
|
||
|
if (strcspn(buf, hex_tag) == 0) {
|
||
|
if (sscanf(buf,"%x",&result)) {
|
||
|
return result;
|
||
|
}
|
||
|
} else {
|
||
|
if (sscanf(buf,"%d",&result)) {
|
||
|
return result;
|
||
|
}
|
||
|
if(sscanf(buf,"-%d",&result)) {
|
||
|
return -result;
|
||
|
}
|
||
|
if (sscanf(buf,"%x",&result)) {
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
sscanf_2_binary(const char *buf) {
|
||
|
|
||
|
int result = sscanf_2_int(buf);
|
||
|
|
||
|
if (result < 0){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
switch (result) {
|
||
|
case 0:
|
||
|
case 1:
|
||
|
return result;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_get_polling_period(void) {
|
||
|
|
||
|
int retval = 0;
|
||
|
|
||
|
if (SWP_POLLING_PERIOD == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
retval = ((SWP_POLLING_PERIOD * HZ) / 1000);
|
||
|
if (retval == 0) {
|
||
|
return 1;
|
||
|
}
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
static struct transvr_obj_s *
|
||
|
_get_transvr_obj(char *dev_name) {
|
||
|
|
||
|
struct device *dev_p = NULL;
|
||
|
struct transvr_obj_s *transvr_obj_p = NULL;
|
||
|
|
||
|
dev_p = get_swpdev_by_name(dev_name);
|
||
|
if (!dev_p){
|
||
|
return NULL;
|
||
|
}
|
||
|
transvr_obj_p = dev_get_drvdata(dev_p);
|
||
|
if (!transvr_obj_p){
|
||
|
return NULL;
|
||
|
}
|
||
|
return transvr_obj_p;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_is_i2c_target_exist(int chan, int addr) {
|
||
|
/* retval: Exist = 1 / Not exist = 0 / Error < 0
|
||
|
*/
|
||
|
struct i2c_adapter *adap = NULL;
|
||
|
struct i2c_client *client = NULL;
|
||
|
int retval = -1;
|
||
|
int err = -1;
|
||
|
int d_offs = 0;
|
||
|
|
||
|
adap = i2c_get_adapter(chan);
|
||
|
if (!adap) {
|
||
|
SWPS_DEBUG("%s: can't get adapter\n", __func__);
|
||
|
retval = 0;
|
||
|
goto out_is_i2c_target_exist_1;
|
||
|
}
|
||
|
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||
|
if (!client) {
|
||
|
SWPS_ERR("%s: kzalloc fail\n", __func__);
|
||
|
retval = -1;
|
||
|
goto out_is_i2c_target_exist_2;
|
||
|
}
|
||
|
client->adapter = adap;
|
||
|
client->addr = addr;
|
||
|
err = i2c_smbus_read_byte_data(client, d_offs);
|
||
|
if (err < 0) {
|
||
|
retval = 0;
|
||
|
} else {
|
||
|
retval = 1;
|
||
|
}
|
||
|
i2c_put_adapter(adap);
|
||
|
kfree(client);
|
||
|
return retval;
|
||
|
|
||
|
out_is_i2c_target_exist_2:
|
||
|
i2c_put_adapter(adap);
|
||
|
out_is_i2c_target_exist_1:
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
unlock_tobj_all(void) {
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
char port_name[32];
|
||
|
int port_id = 0;
|
||
|
int minor_curr = 0;
|
||
|
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
continue;
|
||
|
}
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
lock_tobj_all(void) {
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
char port_name[32] = "ERR";
|
||
|
int port_id = 0;
|
||
|
int minor_curr = 0;
|
||
|
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
SWPS_DEBUG("%s: get %s tobj fail!\n", __func__, port_name);
|
||
|
goto err_lock_tobj_all;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_lock_tobj_all:
|
||
|
unlock_tobj_all();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_update_auto_config_2_trnasvr(void) {
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
char port_name[32] = "ERR";
|
||
|
int port_id = 0;
|
||
|
int curr = 0;
|
||
|
int retval = 0;
|
||
|
|
||
|
for (curr=0; curr<port_total; curr++) {
|
||
|
port_id = port_layout[curr].port_id;
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
retval = -1;
|
||
|
continue;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
tobj_p->auto_config = auto_config;
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
SWPS_DEBUG("%s: Set %s auto_config=%d\n",
|
||
|
__func__, tobj_p->swp_name, auto_config);
|
||
|
}
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== R/W Functions module control attribute ==========
|
||
|
*/
|
||
|
static ssize_t
|
||
|
show_attr_platform(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 32, "%s\n", platform_p->name);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_version(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 8, "%s\n", SWP_VERSION);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_status(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 8, "%d\n", flag_mod_state);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_auto_config(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 8, "%d\n", auto_config);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_block_poll(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 8, "%d\n", block_polling);
|
||
|
}
|
||
|
static ssize_t
|
||
|
show_attr_io_no_init(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
return snprintf(buf_p, 8, "%d\n", io_no_init);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_check_reset_pwd(const char *buf_p,
|
||
|
size_t count) {
|
||
|
|
||
|
int in_max = 64;
|
||
|
int in_len = (int)count;
|
||
|
char in_val[64] = "ERR";
|
||
|
char *emsg = "ERR";
|
||
|
|
||
|
if (in_len >= in_max) {
|
||
|
emsg = "input too much";
|
||
|
goto err_check_reset_pwd;
|
||
|
}
|
||
|
if (!sscanf(buf_p,"%s",in_val)) {
|
||
|
emsg = "format incorrect";
|
||
|
goto err_check_reset_pwd;
|
||
|
}
|
||
|
if (strcmp(in_val, SWP_RESET_PWD) != 0) {
|
||
|
emsg = "password incorrect";
|
||
|
goto err_check_reset_pwd;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_check_reset_pwd:
|
||
|
SWPS_ERR("%s: %s\n", __func__, emsg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_reset_i2c(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
if (_check_reset_pwd(buf_p, count) < 0) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
/* Polling mode */
|
||
|
if (SWP_POLLING_ENABLE) {
|
||
|
SWPS_INFO("%s: reset I2C <mode>:polling\n", __func__);
|
||
|
flag_i2c_reset = 1;
|
||
|
return count;
|
||
|
}
|
||
|
/* Direct mode */
|
||
|
SWPS_INFO("%s: reset I2C go. <mode>:direct\n", __func__);
|
||
|
if (reset_i2c_topology() < 0) {
|
||
|
SWPS_ERR("%s: reset fail!\n", __func__);
|
||
|
return -EIO;
|
||
|
}
|
||
|
SWPS_INFO("%s: reset I2C ok. <mode>:direct\n", __func__);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_reset_swps(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
char port_name[32] = "ERR";
|
||
|
int port_id = 0;
|
||
|
int minor_curr = 0;
|
||
|
|
||
|
if (_check_reset_pwd(buf_p, count) < 0) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
continue;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
tobj_p->state = STATE_TRANSVR_DISCONNECTED;
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
SWPS_INFO("%s: reset:%s\n", __func__, tobj_p->swp_name);
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_auto_config(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
int input_val = sscanf_2_int(buf_p);
|
||
|
|
||
|
if (input_val < 0){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
if ((input_val != 0) && (input_val != 1)) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
auto_config = input_val;
|
||
|
_update_auto_config_2_trnasvr();
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_block_poll( struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
int input_val = sscanf_2_int(buf_p);
|
||
|
|
||
|
if (input_val < 0){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
if ((input_val != 0) && (input_val != 1)) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
|
||
|
if(input_val != block_polling){
|
||
|
block_polling = input_val;
|
||
|
if(block_polling){
|
||
|
cancel_delayed_work_sync(&swp_polling);
|
||
|
}
|
||
|
else{
|
||
|
schedule_delayed_work(&swp_polling, _get_polling_period());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_io_no_init( struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
int input_val = sscanf_2_int(buf_p);
|
||
|
|
||
|
if ((input_val != 0) && (input_val != 1)) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
|
||
|
if(input_val != io_no_init){
|
||
|
io_no_init = input_val;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/* ========== Show functions: For transceiver attribute ==========
|
||
|
*/
|
||
|
static ssize_t
|
||
|
_show_transvr_hex_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*get_func)(struct transvr_obj_s* tobj_p),
|
||
|
char *buf_p) {
|
||
|
size_t len;
|
||
|
int result;
|
||
|
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
result = get_func(tobj_p);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (result < 0){
|
||
|
len = snprintf(buf_p, 8, "%d\n", result);
|
||
|
} else {
|
||
|
len = snprintf(buf_p, 8, "0x%02x\n", result);
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
_show_transvr_int_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*get_func)(struct transvr_obj_s* tobj_p),
|
||
|
char *buf_p) {
|
||
|
size_t len;
|
||
|
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
len = snprintf(buf_p, 16, "%d\n", get_func(tobj_p));
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
_show_transvr_str_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*get_func)(struct transvr_obj_s* tobj_p, char* buf),
|
||
|
char *buf_p) {
|
||
|
size_t len;
|
||
|
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
len = get_func(tobj_p, buf_p);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_id(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_id,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_ext_id(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_ext_id,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_connector(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_connector,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_vendor_name(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_vendor_name,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_vendor_pn(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_vendor_pn,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_vendor_rev(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_vendor_rev,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_vendor_sn(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_vendor_sn,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_power_cls(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
size_t len;
|
||
|
int result;
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
|
||
|
tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
result = tobj_p->get_power_cls(tobj_p);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (result < 0){
|
||
|
len = snprintf(buf_p, 16, "%d\n", result);
|
||
|
} else {
|
||
|
len = snprintf(buf_p, 16, "Power Class %d\n", result);
|
||
|
}
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_br(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_br,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_sm(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_sm,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_smf(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_smf,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_om1(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_om1,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_om2(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_om2,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_om3(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_om3,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_len_om4(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_len_om4,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_comp_rev(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_comp_rev,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_comp_eth(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_comp_eth_1,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_comp_eth_10(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_comp_eth_10,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_comp_eth_10_40(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_comp_eth_10_40,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_comp_extend(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_comp_extend,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_rate_id(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_rate_id,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_temperature(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_curr_temp,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_voltage(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_curr_vol,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_tx_bias(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_tx_bias,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_tx_power(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_tx_power,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_tx_eq(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_tx_eq,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_rx_power(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_rx_power,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_rx_am(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_rx_am,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_rx_em(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_rx_em,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_wavelength(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_wavelength,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_extphy_offset(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_extphy_offset,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_extphy_reg(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_extphy_reg,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_info(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_info,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_if_type(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_if_type,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_if_speed(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_if_speed,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_if_lane(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_if_lane,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_cdr(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_hex_attr(tobj_p,
|
||
|
tobj_p->get_cdr,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_soft_rs0(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_soft_rs0,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_soft_rs1(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_int_attr(tobj_p,
|
||
|
tobj_p->get_soft_rs1,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_soft_rx_los(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_soft_rx_los,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_soft_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_soft_tx_disable,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_soft_tx_fault(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_soft_tx_fault,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_auto_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if(!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_transvr_str_attr(tobj_p,
|
||
|
tobj_p->get_auto_tx_disable,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== Store functions: transceiver (R/W) attribute ==========
|
||
|
*/
|
||
|
static ssize_t
|
||
|
_store_transvr_int_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*set_func)(struct transvr_obj_s *tobj_p, int input_val),
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
int input, err;
|
||
|
|
||
|
input = sscanf_2_int(buf_p);
|
||
|
if (input < 0){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
err = set_func(tobj_p, input);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (err < 0){
|
||
|
return err;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
_store_transvr_byte_hex_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*set_func)(struct transvr_obj_s *tobj_p, int input_val),
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
int input, err;
|
||
|
|
||
|
input = sscanf_2_int(buf_p);
|
||
|
if ((input < 0) || (input > 0xff)){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
err = set_func(tobj_p, input);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (err < 0){
|
||
|
return err;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
_store_transvr_binary_attr(struct transvr_obj_s* tobj_p,
|
||
|
int (*set_func)(struct transvr_obj_s *tobj_p, int input_val),
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
int input, err;
|
||
|
|
||
|
input = sscanf_2_binary(buf_p);
|
||
|
if (input < 0){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
err = set_func(tobj_p, input);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (err < 0){
|
||
|
return err;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_cdr(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_byte_hex_attr(tobj_p,
|
||
|
tobj_p->set_cdr,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_soft_rs0(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_binary_attr(tobj_p,
|
||
|
tobj_p->set_soft_rs0,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_soft_rs1(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_binary_attr(tobj_p,
|
||
|
tobj_p->set_soft_rs1,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_soft_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
|
||
|
int check = sscanf_2_int(buf_p);
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
if ((check < 0) || (check > 0xf)){
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
return _store_transvr_byte_hex_attr(tobj_p,
|
||
|
tobj_p->set_soft_tx_disable,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_auto_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
|
||
|
int err = -EPERM;
|
||
|
int input = sscanf_2_int(buf_p);
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
if ((input < 0) || (input > 0xf)){
|
||
|
if (input != VAL_TRANSVR_FUNCTION_DISABLE) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
}
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
err = tobj_p->set_auto_tx_disable(tobj_p, input);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
if (err < 0){
|
||
|
return err;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_tx_eq(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_int_attr(tobj_p,
|
||
|
tobj_p->set_tx_eq,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_rx_am(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_int_attr(tobj_p,
|
||
|
tobj_p->set_rx_am,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_rx_em(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_int_attr(tobj_p,
|
||
|
tobj_p->set_rx_em,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_extphy_offset(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_int_attr(tobj_p,
|
||
|
tobj_p->set_extphy_offset,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_extphy_reg(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_transvr_int_attr(tobj_p,
|
||
|
tobj_p->set_extphy_reg,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
/* ========== Show functions: For I/O Expander attribute ==========
|
||
|
*/
|
||
|
static ssize_t
|
||
|
_show_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
|
||
|
int (*get_func)(struct ioexp_obj_s *ioexp_p, int voffset),
|
||
|
char *buf_p) {
|
||
|
size_t len;
|
||
|
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
|
||
|
|
||
|
if (!ioexp_p) {
|
||
|
SWPS_ERR(" %s: data corruption! <port>:%s\n", __func__, tobj_p->swp_name);
|
||
|
return -ENODATA;
|
||
|
}
|
||
|
mutex_lock(&ioexp_p->lock);
|
||
|
len = snprintf(buf_p, 8, "%d\n", get_func(ioexp_p, tobj_p->ioexp_virt_offset));
|
||
|
mutex_unlock(&ioexp_p->lock);
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_present(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_present,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_tx_fault(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_tx_fault,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_rxlos(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_rxlos,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_tx_disable,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_reset(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_reset,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_lpmod(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_lpmod,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_modsel(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_modsel,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_hard_rs0(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_hard_rs0,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
show_attr_hard_rs1(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
char *buf_p){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p){
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _show_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->get_hard_rs1,
|
||
|
buf_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== Store functions: For I/O Expander (R/W) attribute ==========
|
||
|
*/
|
||
|
static ssize_t
|
||
|
_store_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
|
||
|
int (*set_func)(struct ioexp_obj_s *ioexp_p,
|
||
|
int virt_offset, int input_val),
|
||
|
const char *buf_p,
|
||
|
size_t count) {
|
||
|
|
||
|
int input, err;
|
||
|
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
|
||
|
|
||
|
if (!ioexp_p) {
|
||
|
SWPS_ERR("%s: data corruption! <port>:%s\n",
|
||
|
__func__, tobj_p->swp_name);
|
||
|
return -ENODATA;
|
||
|
}
|
||
|
input = sscanf_2_binary(buf_p);
|
||
|
if (input < 0) {
|
||
|
return -EBFONT;
|
||
|
}
|
||
|
mutex_lock(&ioexp_p->lock);
|
||
|
err = set_func(ioexp_p, tobj_p->ioexp_virt_offset, input);
|
||
|
mutex_unlock(&ioexp_p->lock);
|
||
|
if (err < 0){
|
||
|
return err;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_tx_disable(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_tx_disable,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_reset(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_reset,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_lpmod(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_lpmod,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_modsel(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_modsel,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_hard_rs0(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_hard_rs0,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t
|
||
|
store_attr_hard_rs1(struct device *dev_p,
|
||
|
struct device_attribute *attr_p,
|
||
|
const char *buf_p,
|
||
|
size_t count){
|
||
|
|
||
|
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
|
||
|
if (!tobj_p) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return _store_ioexp_binary_attr(tobj_p,
|
||
|
tobj_p->ioexp_obj_p->set_hard_rs1,
|
||
|
buf_p,
|
||
|
count);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== SWPS attribute: For module control ==========
|
||
|
*/
|
||
|
static DEVICE_ATTR(platform, S_IRUGO, show_attr_platform, NULL);
|
||
|
static DEVICE_ATTR(version, S_IRUGO, show_attr_version, NULL);
|
||
|
static DEVICE_ATTR(status, S_IRUGO, show_attr_status, NULL);
|
||
|
static DEVICE_ATTR(reset_i2c, S_IWUSR, NULL, store_attr_reset_i2c);
|
||
|
static DEVICE_ATTR(reset_swps, S_IWUSR, NULL, store_attr_reset_swps);
|
||
|
static DEVICE_ATTR(auto_config, S_IRUGO|S_IWUSR, show_attr_auto_config, store_attr_auto_config);
|
||
|
static DEVICE_ATTR(block_poll, S_IRUGO|S_IWUSR, show_attr_block_poll, store_attr_block_poll);
|
||
|
static DEVICE_ATTR(io_no_init, S_IRUGO|S_IWUSR, show_attr_io_no_init, store_attr_io_no_init);
|
||
|
|
||
|
|
||
|
/* ========== Transceiver attribute: from eeprom ==========
|
||
|
*/
|
||
|
static DEVICE_ATTR(id, S_IRUGO, show_attr_id, NULL);
|
||
|
static DEVICE_ATTR(ext_id, S_IRUGO, show_attr_ext_id, NULL);
|
||
|
static DEVICE_ATTR(connector, S_IRUGO, show_attr_connector, NULL);
|
||
|
static DEVICE_ATTR(vendor_name, S_IRUGO, show_attr_vendor_name, NULL);
|
||
|
static DEVICE_ATTR(vendor_pn, S_IRUGO, show_attr_vendor_pn, NULL);
|
||
|
static DEVICE_ATTR(vendor_rev, S_IRUGO, show_attr_vendor_rev, NULL);
|
||
|
static DEVICE_ATTR(vendor_sn, S_IRUGO, show_attr_vendor_sn, NULL);
|
||
|
static DEVICE_ATTR(power_cls, S_IRUGO, show_attr_power_cls, NULL);
|
||
|
static DEVICE_ATTR(br, S_IRUGO, show_attr_br, NULL);
|
||
|
static DEVICE_ATTR(len_sm, S_IRUGO, show_attr_len_sm, NULL);
|
||
|
static DEVICE_ATTR(len_smf, S_IRUGO, show_attr_len_smf, NULL);
|
||
|
static DEVICE_ATTR(len_om1, S_IRUGO, show_attr_len_om1, NULL);
|
||
|
static DEVICE_ATTR(len_om2, S_IRUGO, show_attr_len_om2, NULL);
|
||
|
static DEVICE_ATTR(len_om3, S_IRUGO, show_attr_len_om3, NULL);
|
||
|
static DEVICE_ATTR(len_om4, S_IRUGO, show_attr_len_om4, NULL);
|
||
|
static DEVICE_ATTR(comp_rev, S_IRUGO, show_attr_comp_rev, NULL);
|
||
|
static DEVICE_ATTR(comp_eth, S_IRUGO, show_attr_comp_eth, NULL);
|
||
|
static DEVICE_ATTR(comp_eth_10, S_IRUGO, show_attr_comp_eth_10, NULL);
|
||
|
static DEVICE_ATTR(comp_eth_10_40, S_IRUGO, show_attr_comp_eth_10_40, NULL);
|
||
|
static DEVICE_ATTR(comp_extend, S_IRUGO, show_attr_comp_extend, NULL);
|
||
|
static DEVICE_ATTR(rate_id, S_IRUGO, show_attr_rate_id, NULL);
|
||
|
static DEVICE_ATTR(temperature, S_IRUGO, show_attr_temperature, NULL);
|
||
|
static DEVICE_ATTR(voltage, S_IRUGO, show_attr_voltage, NULL);
|
||
|
static DEVICE_ATTR(tx_bias, S_IRUGO, show_attr_tx_bias, NULL);
|
||
|
static DEVICE_ATTR(tx_power, S_IRUGO, show_attr_tx_power, NULL);
|
||
|
static DEVICE_ATTR(rx_power, S_IRUGO, show_attr_rx_power, NULL);
|
||
|
static DEVICE_ATTR(info, S_IRUGO, show_attr_info, NULL);
|
||
|
static DEVICE_ATTR(if_type, S_IRUGO, show_attr_if_type, NULL);
|
||
|
static DEVICE_ATTR(if_speed, S_IRUGO, show_attr_if_speed, NULL);
|
||
|
static DEVICE_ATTR(if_lane, S_IRUGO, show_attr_if_lane, NULL);
|
||
|
static DEVICE_ATTR(soft_rx_los, S_IRUGO, show_attr_soft_rx_los, NULL);
|
||
|
static DEVICE_ATTR(soft_tx_fault, S_IRUGO, show_attr_soft_tx_fault, NULL);
|
||
|
static DEVICE_ATTR(wavelength, S_IRUGO, show_attr_wavelength, NULL);
|
||
|
static DEVICE_ATTR(tx_eq, S_IRUGO|S_IWUSR, show_attr_tx_eq, store_attr_tx_eq);
|
||
|
static DEVICE_ATTR(rx_am, S_IRUGO|S_IWUSR, show_attr_rx_am, store_attr_rx_am);
|
||
|
static DEVICE_ATTR(rx_em, S_IRUGO|S_IWUSR, show_attr_rx_em, store_attr_rx_em);
|
||
|
static DEVICE_ATTR(cdr, S_IRUGO|S_IWUSR, show_attr_cdr, store_attr_cdr);
|
||
|
static DEVICE_ATTR(soft_rs0, S_IRUGO|S_IWUSR, show_attr_soft_rs0, store_attr_soft_rs0);
|
||
|
static DEVICE_ATTR(soft_rs1, S_IRUGO|S_IWUSR, show_attr_soft_rs1, store_attr_soft_rs1);
|
||
|
static DEVICE_ATTR(soft_tx_disable, S_IRUGO|S_IWUSR, show_attr_soft_tx_disable, store_attr_soft_tx_disable);
|
||
|
static DEVICE_ATTR(auto_tx_disable, S_IRUGO|S_IWUSR, show_attr_auto_tx_disable, store_attr_auto_tx_disable);
|
||
|
static DEVICE_ATTR(extphy_offset, S_IRUGO|S_IWUSR, show_attr_extphy_offset, store_attr_extphy_offset);
|
||
|
static DEVICE_ATTR(extphy_reg, S_IRUGO|S_IWUSR, show_attr_extphy_reg, store_attr_extphy_reg);
|
||
|
|
||
|
/* ========== IO Expander attribute: from expander ==========
|
||
|
*/
|
||
|
static DEVICE_ATTR(present, S_IRUGO, show_attr_present, NULL);
|
||
|
static DEVICE_ATTR(tx_fault, S_IRUGO, show_attr_tx_fault, NULL);
|
||
|
static DEVICE_ATTR(rxlos, S_IRUGO, show_attr_rxlos, NULL);
|
||
|
static DEVICE_ATTR(tx_disable, S_IRUGO|S_IWUSR, show_attr_tx_disable, store_attr_tx_disable);
|
||
|
static DEVICE_ATTR(reset, S_IRUGO|S_IWUSR, show_attr_reset, store_attr_reset);
|
||
|
static DEVICE_ATTR(lpmod, S_IRUGO|S_IWUSR, show_attr_lpmod, store_attr_lpmod);
|
||
|
static DEVICE_ATTR(modsel, S_IRUGO|S_IWUSR, show_attr_modsel, store_attr_modsel);
|
||
|
static DEVICE_ATTR(hard_rs0, S_IRUGO|S_IWUSR, show_attr_hard_rs0, store_attr_hard_rs0);
|
||
|
static DEVICE_ATTR(hard_rs1, S_IRUGO|S_IWUSR, show_attr_hard_rs1, store_attr_hard_rs1);
|
||
|
|
||
|
/* ========== Functions for module handling ==========
|
||
|
*/
|
||
|
static void
|
||
|
clean_port_objs(void){
|
||
|
|
||
|
dev_t dev_num;
|
||
|
char dev_name[32];
|
||
|
struct device *device_p;
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
int minor_curr, port_id;
|
||
|
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++){
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
memset(dev_name, 0, sizeof(dev_name));
|
||
|
snprintf(dev_name, sizeof(dev_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
device_p = get_swpdev_by_name(dev_name);
|
||
|
if (!device_p){
|
||
|
continue;
|
||
|
}
|
||
|
tobj_p = dev_get_drvdata(device_p);
|
||
|
if (tobj_p){
|
||
|
if (tobj_p->i2c_client_p) {
|
||
|
i2c_put_adapter(tobj_p->i2c_client_p->adapter);
|
||
|
kfree(tobj_p->i2c_client_p);
|
||
|
}
|
||
|
kfree(tobj_p->vendor_name);
|
||
|
kfree(tobj_p->vendor_pn);
|
||
|
kfree(tobj_p->vendor_rev);
|
||
|
kfree(tobj_p->vendor_sn);
|
||
|
kfree(tobj_p->worker_p);
|
||
|
kfree(tobj_p);
|
||
|
}
|
||
|
dev_num = MKDEV(port_major, minor_curr);
|
||
|
device_unregister(device_p);
|
||
|
device_destroy(swp_class_p, dev_num);
|
||
|
}
|
||
|
SWPS_DEBUG("%s: done.\n", __func__);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
clean_swps_common(void){
|
||
|
|
||
|
dev_t dev_num;
|
||
|
struct device *device_p;
|
||
|
|
||
|
device_p = get_swpdev_by_name(SWP_DEV_MODCTL);
|
||
|
if (device_p){
|
||
|
dev_num = MKDEV(ctl_major, 1);
|
||
|
device_unregister(device_p);
|
||
|
device_destroy(swp_class_p, dev_num);
|
||
|
}
|
||
|
cancel_delayed_work_sync(&swp_polling);
|
||
|
if (platform_p) {
|
||
|
kfree(platform_p);
|
||
|
}
|
||
|
SWPS_DEBUG("%s: done.\n", __func__);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
get_platform_type(void){
|
||
|
|
||
|
int i, tmp;
|
||
|
int auto_chan = -1;
|
||
|
int auto_addr = -1;
|
||
|
int pf_total = ARRAY_SIZE(platform_map);
|
||
|
char log_msg[64] = "ERROR";
|
||
|
|
||
|
platform_p = kzalloc(sizeof(struct inv_platform_s), GFP_KERNEL);
|
||
|
if (!platform_p){
|
||
|
snprintf(log_msg, sizeof(log_msg), "kzalloc fail");
|
||
|
goto err_get_platform_type_1;
|
||
|
}
|
||
|
memset(platform_p->name, 0, sizeof(platform_p->name));
|
||
|
|
||
|
switch (PLATFORM_SETTINGS) {
|
||
|
case PLATFORM_TYPE_AUTO:
|
||
|
snprintf(platform_p->name, (sizeof(platform_p->name) - 1),
|
||
|
"%s", dmi_get_system_info(DMI_BOARD_NAME));
|
||
|
for (i=0; i<pf_total; i++) {
|
||
|
if (strcmp(platform_p->name, platform_map[i].name) == 0) {
|
||
|
platform_p->id = platform_map[i].id;
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"Auto detect platform: %d (%s)",
|
||
|
platform_p->id, platform_p->name);
|
||
|
goto ok_get_platform_type_1;
|
||
|
}
|
||
|
}
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"Auto detect fail! detect platform: %s",
|
||
|
platform_p->name);
|
||
|
goto err_get_platform_type_2;
|
||
|
|
||
|
case PLATFORM_TYPE_PEONY_AUTO:
|
||
|
#ifdef SWPS_PEONY_SFP
|
||
|
auto_chan = peony_sfp_ioexp_layout[0].addr[0].chan_id;
|
||
|
auto_addr = peony_sfp_ioexp_layout[0].addr[0].chip_addr;
|
||
|
#endif
|
||
|
tmp = _is_i2c_target_exist(auto_chan, auto_addr);
|
||
|
switch (tmp) {
|
||
|
case 0: /* Copper SKU */
|
||
|
SWPS_INFO("Auto-detected <platform>:Peony <SKU>:Copper\n");
|
||
|
platform_p->id = PLATFORM_TYPE_PEONY_COPPER_GA;
|
||
|
goto map_platform_name;
|
||
|
|
||
|
case 1: /* SFP SKU */
|
||
|
SWPS_INFO("Auto-detected <platform>:Peony <SKU>:SFP\n");
|
||
|
platform_p->id = PLATFORM_TYPE_PEONY_SFP_GA;
|
||
|
goto map_platform_name;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"Auto detect Peony SKU fail! <err>:%d", tmp);
|
||
|
goto err_get_platform_type_2;
|
||
|
|
||
|
case PLATFORM_TYPE_MAGNOLIA:
|
||
|
case PLATFORM_TYPE_MAGNOLIA_FNC:
|
||
|
case PLATFORM_TYPE_REDWOOD:
|
||
|
case PLATFORM_TYPE_REDWOOD_FSL:
|
||
|
case PLATFORM_TYPE_HUDSON32I_GA:
|
||
|
case PLATFORM_TYPE_SPRUCE:
|
||
|
case PLATFORM_TYPE_CYPRESS_GA1:
|
||
|
case PLATFORM_TYPE_CYPRESS_GA2:
|
||
|
case PLATFORM_TYPE_CYPRESS_BAI:
|
||
|
case PLATFORM_TYPE_TAHOE:
|
||
|
case PLATFORM_TYPE_SEQUOIA_GA:
|
||
|
case PLATFORM_TYPE_LAVENDER_GA:
|
||
|
case PLATFORM_TYPE_LAVENDER_ONL:
|
||
|
case PLATFORM_TYPE_COTTONWOOD_RANGELEY:
|
||
|
case PLATFORM_TYPE_MAPLE_GA:
|
||
|
case PLATFORM_TYPE_MAPLE_B:
|
||
|
case PLATFORM_TYPE_MAPLE_J:
|
||
|
case PLATFORM_TYPE_GULMOHAR_GA:
|
||
|
case PLATFORM_TYPE_GULMOHAR_2T_EVT1_GA:
|
||
|
case PLATFORM_TYPE_PEONY_SFP_GA:
|
||
|
case PLATFORM_TYPE_PEONY_COPPER_GA:
|
||
|
case PLATFORM_TYPE_CEDAR_GA:
|
||
|
platform_p->id = PLATFORM_SETTINGS;
|
||
|
goto map_platform_name;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"PLATFORM_SETTINGS:%d undefined", PLATFORM_SETTINGS);
|
||
|
goto err_get_platform_type_2;
|
||
|
|
||
|
map_platform_name:
|
||
|
for (i=0; i<pf_total; i++) {
|
||
|
if (platform_p->id == platform_map[i].id) {
|
||
|
snprintf(platform_p->name, (sizeof(platform_p->name) - 1),
|
||
|
"%s", platform_map[i].name);
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"User setup platform: %d (%s)",
|
||
|
platform_p->id, platform_p->name);
|
||
|
goto ok_get_platform_type_1;
|
||
|
}
|
||
|
}
|
||
|
snprintf(log_msg, sizeof(log_msg),
|
||
|
"Internal error, can not map id:%d",
|
||
|
platform_p->id );
|
||
|
goto err_get_platform_type_2;
|
||
|
|
||
|
ok_get_platform_type_1:
|
||
|
SWPS_DEBUG("%s: %s, <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
|
||
|
return 0;
|
||
|
|
||
|
err_get_platform_type_2:
|
||
|
kfree(platform_p);
|
||
|
err_get_platform_type_1:
|
||
|
SWPS_ERR("%s: %s <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
get_layout_info(void){
|
||
|
|
||
|
switch (platform_p->id) {
|
||
|
#ifdef SWPS_MAGNOLIA
|
||
|
case PLATFORM_TYPE_MAGNOLIA:
|
||
|
case PLATFORM_TYPE_MAGNOLIA_FNC:
|
||
|
gpio_rest_mux = gpio_base + magnolia_gpio_rest_mux;
|
||
|
ioexp_layout = magnolia_ioexp_layout;
|
||
|
port_layout = magnolia_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(magnolia_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(magnolia_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_REDWOOD
|
||
|
case PLATFORM_TYPE_REDWOOD:
|
||
|
gpio_rest_mux = gpio_base + redwood_gpio_rest_mux;
|
||
|
ioexp_layout = redwood_ioexp_layout;
|
||
|
port_layout = redwood_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(redwood_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(redwood_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_HUDSON32I_GA
|
||
|
case PLATFORM_TYPE_HUDSON32I_GA:
|
||
|
gpio_rest_mux = gpio_base + hudsin32iga_gpio_rest_mux;
|
||
|
ioexp_layout = hudson32iga_ioexp_layout;
|
||
|
port_layout = hudson32iga_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(hudson32iga_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(hudson32iga_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_SPRUCE
|
||
|
case PLATFORM_TYPE_SPRUCE:
|
||
|
gpio_rest_mux = gpio_base + spruce_gpio_rest_mux;
|
||
|
ioexp_layout = spruce_ioexp_layout;
|
||
|
port_layout = spruce_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(spruce_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(spruce_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_CYPRESS_GA1
|
||
|
case PLATFORM_TYPE_CYPRESS_GA1:
|
||
|
gpio_rest_mux = gpio_base + cypress_ga1_gpio_rest_mux;
|
||
|
ioexp_layout = cypress_ga1_ioexp_layout;
|
||
|
port_layout = cypress_ga1_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(cypress_ga1_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(cypress_ga1_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_CYPRESS_GA2
|
||
|
case PLATFORM_TYPE_CYPRESS_GA2:
|
||
|
gpio_rest_mux = gpio_base + cypress_ga2_gpio_rest_mux;
|
||
|
ioexp_layout = cypress_ga2_ioexp_layout;
|
||
|
port_layout = cypress_ga2_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(cypress_ga2_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(cypress_ga2_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_CYPRESS_BAI
|
||
|
case PLATFORM_TYPE_CYPRESS_BAI:
|
||
|
gpio_rest_mux = gpio_base + cypress_b_gpio_rest_mux;
|
||
|
ioexp_layout = cypress_b_ioexp_layout;
|
||
|
port_layout = cypress_b_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(cypress_b_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(cypress_b_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_REDWOOD_FSL
|
||
|
case PLATFORM_TYPE_REDWOOD_FSL:
|
||
|
gpio_rest_mux = gpio_base + redwood_fsl_gpio_rest_mux;
|
||
|
ioexp_layout = redwood_fsl_ioexp_layout;
|
||
|
port_layout = redwood_fsl_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(redwood_fsl_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(redwood_fsl_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_TAHOE
|
||
|
case PLATFORM_TYPE_TAHOE:
|
||
|
gpio_rest_mux = gpio_base + tahoe_gpio_rest_mux;
|
||
|
ioexp_layout = tahoe_ioexp_layout;
|
||
|
port_layout = tahoe_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(tahoe_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(tahoe_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_SEQUOIA
|
||
|
case PLATFORM_TYPE_SEQUOIA_GA:
|
||
|
gpio_rest_mux = gpio_base + sequoia_gpio_rest_mux;
|
||
|
ioexp_layout = sequoia_ioexp_layout;
|
||
|
port_layout = sequoia_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(sequoia_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(sequoia_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_LAVENDER
|
||
|
case PLATFORM_TYPE_LAVENDER_GA:
|
||
|
case PLATFORM_TYPE_LAVENDER_ONL:
|
||
|
gpio_rest_mux = gpio_base + lavender_gpio_rest_mux;
|
||
|
ioexp_layout = lavender_ioexp_layout;
|
||
|
port_layout = lavender_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(lavender_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(lavender_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_COTTONWOOD_RANGELEY
|
||
|
case PLATFORM_TYPE_COTTONWOOD_RANGELEY:
|
||
|
gpio_rest_mux = gpio_base + cottonwood_rangeley_gpio_rest_mux;
|
||
|
ioexp_layout = cottonwood_rangeley_ioexp_layout;
|
||
|
port_layout = cottonwood_rangeley_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(cottonwood_rangeley_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(cottonwood_rangeley_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_MAPLE_GA
|
||
|
case PLATFORM_TYPE_MAPLE_GA:
|
||
|
gpio_rest_mux = gpio_base + maple_ga_gpio_rest_mux;
|
||
|
ioexp_layout = maple_ga_ioexp_layout;
|
||
|
port_layout = maple_ga_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(maple_ga_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(maple_ga_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_MAPLE_B
|
||
|
case PLATFORM_TYPE_MAPLE_B:
|
||
|
gpio_rest_mux = gpio_base + maple_b_gpio_rest_mux;
|
||
|
ioexp_layout = maple_b_ioexp_layout;
|
||
|
port_layout = maple_b_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(maple_b_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(maple_b_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_MAPLE_J
|
||
|
case PLATFORM_TYPE_MAPLE_J:
|
||
|
gpio_rest_mux = gpio_base + maple_j_gpio_rest_mux;
|
||
|
ioexp_layout = maple_j_ioexp_layout;
|
||
|
port_layout = maple_j_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(maple_j_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(maple_j_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_GULMOHAR
|
||
|
case PLATFORM_TYPE_GULMOHAR_GA:
|
||
|
gpio_rest_mux = gpio_base + gulmohar_gpio_rest_mux;
|
||
|
ioexp_layout = gulmohar_ioexp_layout;
|
||
|
port_layout = gulmohar_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(gulmohar_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(gulmohar_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_GULMOHAR_2T_EVT1
|
||
|
case PLATFORM_TYPE_GULMOHAR_2T_EVT1_GA:
|
||
|
gpio_rest_mux = gpio_base + gulmohar_2t_evt1_gpio_rest_mux;
|
||
|
ioexp_layout = gulmohar_2t_evt1_ioexp_layout;
|
||
|
port_layout = gulmohar_2t_evt1_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(gulmohar_2t_evt1_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(gulmohar_2t_evt1_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_PEONY_SFP
|
||
|
case PLATFORM_TYPE_PEONY_SFP_GA:
|
||
|
gpio_rest_mux = gpio_base + peony_sfp_gpio_rest_mux;
|
||
|
ioexp_layout = peony_sfp_ioexp_layout;
|
||
|
port_layout = peony_sfp_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(peony_sfp_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(peony_sfp_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_PEONY_COPPER
|
||
|
case PLATFORM_TYPE_PEONY_COPPER_GA:
|
||
|
gpio_rest_mux = gpio_base + peony_copper_gpio_rest_mux;
|
||
|
ioexp_layout = peony_copper_ioexp_layout;
|
||
|
port_layout = peony_copper_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(peony_copper_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(peony_copper_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
#ifdef SWPS_CEDAR_GA
|
||
|
case PLATFORM_TYPE_CEDAR_GA:
|
||
|
gpio_rest_mux = gpio_base + cedar_ga_gpio_rest_mux;
|
||
|
ioexp_layout = cedar_ga_ioexp_layout;
|
||
|
port_layout = cedar_ga_port_layout;
|
||
|
ioexp_total = ARRAY_SIZE(cedar_ga_ioexp_layout);
|
||
|
port_total = ARRAY_SIZE(cedar_ga_port_layout);
|
||
|
break;
|
||
|
#endif
|
||
|
|
||
|
default:
|
||
|
SWPS_ERR(" Invalid platform: %d (%s)\n",
|
||
|
platform_p->id, platform_p->name);
|
||
|
return -1;
|
||
|
}
|
||
|
SWPS_INFO("Start to initial platform: %d (%s)\n",
|
||
|
platform_p->id, platform_p->name);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== Functions for objects operations ==========
|
||
|
*/
|
||
|
static int
|
||
|
__detect_issues_port(int minor_num) {
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
int port_id = port_layout[minor_num].port_id;
|
||
|
char port_name[32] = "ERR";
|
||
|
char *i2c_emsg = "detected bad transceiver/cable";
|
||
|
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
SWPS_INFO("%s: tobj_p is NULL <minor>:%d\n", __func__, minor_num);
|
||
|
return -1;
|
||
|
}
|
||
|
if (resync_channel_tier_2(tobj_p) < 0) {
|
||
|
if (check_channel_tier_1() < 0) {
|
||
|
goto get_target_issues_port;
|
||
|
}
|
||
|
}
|
||
|
/* Re-check again for i2c-gpio special case */
|
||
|
if (check_channel_tier_1() < 0) {
|
||
|
goto get_target_issues_port;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
get_target_issues_port:
|
||
|
alarm_msg_2_user(tobj_p, i2c_emsg);
|
||
|
return -2;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_detect_issues_port(void) {
|
||
|
/* OK : retrun -1;
|
||
|
* Fail: return fail at which minor number (0~N)
|
||
|
*/
|
||
|
char *emsg = "ERR";
|
||
|
int minor = 0;
|
||
|
int minor_2st = 1;
|
||
|
|
||
|
/* Force moving the initial channel pointer
|
||
|
* Filter out case of fail at minor-0 port
|
||
|
*/
|
||
|
while (minor_2st < port_total) {
|
||
|
minor = minor_2st;
|
||
|
if (__detect_issues_port(minor_2st) < 0) {
|
||
|
emsg = "detect minor_2st fail";
|
||
|
goto err_p_detect_issues_port;
|
||
|
}
|
||
|
minor_2st += 8;
|
||
|
}
|
||
|
/* Scan all port */
|
||
|
for (minor=0; minor<port_total; minor++) {
|
||
|
if (__detect_issues_port(minor) < 0) {
|
||
|
emsg = "is I2C issues port";
|
||
|
goto err_p_detect_issues_port;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
|
||
|
err_p_detect_issues_port:
|
||
|
SWPS_DEBUG("%s: minor:%d %s\n", __func__, minor, emsg);
|
||
|
return minor;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_isolate_issues_port(int minor_num) {
|
||
|
|
||
|
struct transvr_obj_s *tobj_p;
|
||
|
char port_name[32];
|
||
|
int port_id = port_layout[minor_num].port_id;
|
||
|
|
||
|
memset(port_name, 0, sizeof(port_name));
|
||
|
snprintf(port_name, sizeof(port_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
tobj_p = _get_transvr_obj(port_name);
|
||
|
if (!tobj_p) {
|
||
|
return -1;
|
||
|
}
|
||
|
return isolate_transvr_obj(tobj_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
_reset_i2c_topology_tier_1(void) {
|
||
|
|
||
|
if (reset_mux_objs() < 0) {
|
||
|
SWPS_ERR("%s: reset MUX GPIO fail!\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: reset_mux_objs OK.\n", __func__);
|
||
|
if (resync_channel_tier_1() < 0) {
|
||
|
SWPS_ERR("%s: resync tier-1 channel fail!\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: resync_channel_tier_1 OK.\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
reset_i2c_topology(void) {
|
||
|
|
||
|
int run_count = 0;
|
||
|
int minor_err = 0;
|
||
|
char *emsg = "ERR";
|
||
|
|
||
|
/* Get all lock */
|
||
|
if (lock_ioexp_all() < 0) {
|
||
|
emsg = "lock_ioexp_all fail";
|
||
|
goto err_reset_i2c_topology_1;
|
||
|
}
|
||
|
if (lock_tobj_all() < 0) {
|
||
|
emsg = "lock_tobj_all fail";
|
||
|
goto err_reset_i2c_topology_2;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: get Lock OK.\n", __func__);
|
||
|
/* Reset tier-1 topology */
|
||
|
if (_reset_i2c_topology_tier_1() < 0) {
|
||
|
emsg = "initial task fail";
|
||
|
goto err_reset_i2c_topology_3;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: reset tier-1 OK.\n", __func__);
|
||
|
/* Reset tier-2 topology */
|
||
|
for (run_count=0; run_count<port_total; run_count++) {
|
||
|
SWPS_DEBUG("%s: reset tier-2 No.%d-run go.\n", __func__, run_count);
|
||
|
minor_err = _detect_issues_port();
|
||
|
/* Case: no issues port */
|
||
|
if (minor_err < 0) {
|
||
|
break;
|
||
|
}
|
||
|
/* Case: has issues port */
|
||
|
if (_isolate_issues_port(minor_err) < 0) {
|
||
|
emsg = "isolate fail";
|
||
|
goto err_reset_i2c_topology_3;
|
||
|
}
|
||
|
if (_reset_i2c_topology_tier_1() < 0) {
|
||
|
emsg = "reset tier-1 fail";
|
||
|
goto err_reset_i2c_topology_3;
|
||
|
}
|
||
|
}
|
||
|
unlock_tobj_all();
|
||
|
unlock_ioexp_all();
|
||
|
flag_mod_state = SWP_STATE_NORMAL;
|
||
|
SWPS_INFO("%s: done\n", __func__);
|
||
|
return 0;
|
||
|
|
||
|
err_reset_i2c_topology_3:
|
||
|
unlock_tobj_all();
|
||
|
err_reset_i2c_topology_2:
|
||
|
unlock_ioexp_all();
|
||
|
err_reset_i2c_topology_1:
|
||
|
flag_mod_state = SWP_STATE_I2C_DIE;
|
||
|
SWPS_INFO("%s: %s <minor>:%d\n", __func__, emsg, minor_err);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
check_transvr_obj_one(char *dev_name){
|
||
|
/* [Return]
|
||
|
* 0 : Doesn't need to take care
|
||
|
* -1 : Single error
|
||
|
* -2 : Critical error (I2C topology die)
|
||
|
* -9 : Internal error
|
||
|
*/
|
||
|
struct transvr_obj_s *tobj_p = NULL;
|
||
|
int retval = -9;
|
||
|
|
||
|
tobj_p = _get_transvr_obj(dev_name);
|
||
|
if (!tobj_p) {
|
||
|
SWPS_ERR("%s: %s _get_transvr_obj fail\n",
|
||
|
__func__, dev_name);
|
||
|
return -9;
|
||
|
}
|
||
|
/* Check transceiver current status */
|
||
|
lock_transvr_obj(tobj_p);
|
||
|
retval = tobj_p->check(tobj_p);
|
||
|
unlock_transvr_obj(tobj_p);
|
||
|
switch (retval) {
|
||
|
case 0:
|
||
|
case ERR_TRANSVR_UNPLUGGED:
|
||
|
case ERR_TRNASVR_BE_ISOLATED:
|
||
|
case ERR_TRANSVR_TASK_BUSY:
|
||
|
return 0;
|
||
|
|
||
|
case ERR_TRANSVR_I2C_CRASH:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
/* Identify abnormal case */
|
||
|
if (check_channel_tier_1() < 0) {
|
||
|
SWPS_DEBUG("%s: %s critical error <err>:%d\n",
|
||
|
__func__, dev_name, retval);
|
||
|
return -2;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: %s single error <err>:%d\n",
|
||
|
__func__, dev_name, retval);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
check_transvr_objs(void){
|
||
|
|
||
|
char dev_name[32];
|
||
|
int port_id, err_code;
|
||
|
int minor_curr = 0;
|
||
|
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
|
||
|
/* Generate device name */
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
memset(dev_name, 0, sizeof(dev_name));
|
||
|
snprintf(dev_name, sizeof(dev_name), "%s%d", SWP_DEV_PORT, port_id);
|
||
|
/* Handle current status */
|
||
|
err_code = check_transvr_obj_one(dev_name);
|
||
|
switch (err_code) {
|
||
|
case 0:
|
||
|
case -1:
|
||
|
break;
|
||
|
|
||
|
case -2:
|
||
|
SWPS_DEBUG("%s: %s reset I2C GO.\n",
|
||
|
__func__, dev_name);
|
||
|
if (reset_i2c_topology() < 0) {
|
||
|
goto err_check_transvr_objs;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: %s reset I2C OK.\n",
|
||
|
__func__, dev_name);
|
||
|
break;
|
||
|
|
||
|
case -9:
|
||
|
default:
|
||
|
SWPS_DEBUG("%s: %s internal error <err>:%d\n",
|
||
|
__func__, dev_name, err_code);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_check_transvr_objs:
|
||
|
SWPS_ERR("%s: %s reset_i2c_topology fail.\n",
|
||
|
__func__, dev_name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
swp_polling_worker(struct work_struct *work){
|
||
|
|
||
|
/* Reset I2C */
|
||
|
if (flag_i2c_reset) {
|
||
|
goto polling_reset_i2c;
|
||
|
}
|
||
|
/* Check IOEXP */
|
||
|
if (check_ioexp_objs() < 0) {
|
||
|
goto polling_reset_i2c;
|
||
|
}
|
||
|
/* Check transceiver */
|
||
|
if (check_transvr_objs() < 0) {
|
||
|
SWPS_DEBUG("%s: check_transvr_objs fail.\n", __func__);
|
||
|
flag_i2c_reset = 1;
|
||
|
}
|
||
|
goto polling_schedule_round;
|
||
|
|
||
|
polling_reset_i2c:
|
||
|
SWPS_DEBUG("%s: reset_i2c_topology start.\n", __func__);
|
||
|
if (reset_i2c_topology() < 0) {
|
||
|
SWPS_ERR("%s: reset i2c fail!\n", __func__);
|
||
|
flag_i2c_reset = 1;
|
||
|
} else {
|
||
|
SWPS_DEBUG("%s: reset_i2c_topology OK.\n", __func__);
|
||
|
flag_i2c_reset = 0;
|
||
|
}
|
||
|
polling_schedule_round:
|
||
|
schedule_delayed_work(&swp_polling, _get_polling_period());
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== Functions for register something ==========
|
||
|
*/
|
||
|
static int
|
||
|
register_transvr_common_attr(struct device *device_p){
|
||
|
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (device_create_file(device_p, &dev_attr_id) < 0) {
|
||
|
err_attr = "dev_attr_id";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_ext_id) < 0) {
|
||
|
err_attr = "dev_attr_ext_id";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_connector) < 0) {
|
||
|
err_attr = "dev_attr_connector";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_vendor_name) < 0) {
|
||
|
err_attr = "dev_attr_vendor_name";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_vendor_pn) < 0) {
|
||
|
err_attr = "dev_attr_vendor_pn";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_vendor_rev) < 0) {
|
||
|
err_attr = "dev_attr_vendor_rev";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_vendor_sn) < 0) {
|
||
|
err_attr = "dev_attr_vendor_sn";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_br) < 0) {
|
||
|
err_attr = "dev_attr_br";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_smf) < 0) {
|
||
|
err_attr = "dev_attr_len_smf";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_om1) < 0) {
|
||
|
err_attr = "dev_attr_len_om1";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_om2) < 0) {
|
||
|
err_attr = "dev_attr_len_om2";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_om3) < 0) {
|
||
|
err_attr = "dev_attr_len_om3";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_om4) < 0) {
|
||
|
err_attr = "dev_attr_len_om4";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_comp_extend) < 0) {
|
||
|
err_attr = "dev_attr_comp_extend";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_comp_eth) < 0) {
|
||
|
err_attr = "dev_attr_comp_eth";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_comp_rev) < 0) {
|
||
|
err_attr = "dev_attr_comp_rev";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_info) < 0) {
|
||
|
err_attr = "dev_attr_info";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_if_type) < 0) {
|
||
|
err_attr = "dev_attr_if_type";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_if_speed) < 0) {
|
||
|
err_attr = "dev_attr_if_speed";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_if_lane) < 0) {
|
||
|
err_attr = "dev_attr_if_lane";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_temperature) < 0) {
|
||
|
err_attr = "dev_attr_temperature";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_voltage) < 0) {
|
||
|
err_attr = "dev_attr_voltage";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_tx_bias) < 0) {
|
||
|
err_attr = "dev_attr_tx_bias";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_tx_power) < 0) {
|
||
|
err_attr = "dev_attr_tx_power";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_rx_power) < 0) {
|
||
|
err_attr = "dev_attr_rx_power";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_tx_eq) < 0) {
|
||
|
err_attr = "dev_attr_tx_eq";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_rx_em) < 0) {
|
||
|
err_attr = "dev_attr_rx_em";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_wavelength) < 0) {
|
||
|
err_attr = "dev_attr_wavelength";
|
||
|
goto err_transvr_comm_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_transvr_comm_attr:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
register_transvr_sfp_attr(struct device *device_p){
|
||
|
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (register_transvr_common_attr(device_p) < 0) {
|
||
|
err_attr = "register_transvr_common_attr";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_comp_eth_10) < 0) {
|
||
|
err_attr = "dev_attr_comp_eth_10";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_len_sm) < 0) {
|
||
|
err_attr = "dev_attr_len_sm";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_rate_id) < 0) {
|
||
|
err_attr = "dev_attr_rate_id";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_soft_rs0) < 0) {
|
||
|
err_attr = "dev_attr_soft_rs0";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_soft_rs1) < 0) {
|
||
|
err_attr = "dev_attr_soft_rs1";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_extphy_offset) < 0) {
|
||
|
err_attr = "dev_attr_extphy_offset";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_extphy_reg) < 0) {
|
||
|
err_attr = "dev_attr_extphy_reg";
|
||
|
goto err_transvr_sfp_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_transvr_sfp_attr:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_transvr_qsfp_attr(struct device *device_p){
|
||
|
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (register_transvr_common_attr(device_p) < 0) {
|
||
|
err_attr = "register_transvr_common_attr";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_comp_eth_10_40) < 0) {
|
||
|
err_attr = "dev_attr_comp_eth_10_40";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_power_cls) < 0) {
|
||
|
err_attr = "dev_attr_power_cls";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_soft_rx_los) < 0) {
|
||
|
err_attr = "soft_rx_los";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_soft_tx_disable) < 0) {
|
||
|
err_attr = "soft_tx_disable";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_auto_tx_disable) < 0) {
|
||
|
err_attr = "auto_tx_disable";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_soft_tx_fault) < 0) {
|
||
|
err_attr = "soft_tx_fault";
|
||
|
goto err_transvr_qsfp_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_transvr_qsfp_attr:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_transvr_qsfp28_attr(struct device *device_p){
|
||
|
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (register_transvr_qsfp_attr(device_p) < 0){
|
||
|
err_attr = "register_transvr_qsfp_attr";
|
||
|
goto err_transvr_qsfp28_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_cdr) < 0) {
|
||
|
err_attr = "dev_attr_cdr";
|
||
|
goto err_transvr_qsfp28_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_rx_am) < 0) {
|
||
|
err_attr = "dev_attr_rx_am";
|
||
|
goto err_transvr_qsfp28_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_transvr_qsfp28_attr:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_transvr_attr(struct device *device_p,
|
||
|
struct transvr_obj_s *transvr_obj){
|
||
|
|
||
|
switch (transvr_obj->layout){
|
||
|
case TRANSVR_TYPE_SFP:
|
||
|
if (register_transvr_sfp_attr(device_p) < 0){
|
||
|
goto err_reg_tvr_attr;
|
||
|
}
|
||
|
break;
|
||
|
case TRANSVR_TYPE_QSFP:
|
||
|
case TRANSVR_TYPE_QSFP_PLUS:
|
||
|
if (register_transvr_qsfp_attr(device_p) < 0){
|
||
|
goto err_reg_tvr_attr;
|
||
|
}
|
||
|
break;
|
||
|
case TRANSVR_TYPE_QSFP_28:
|
||
|
if (register_transvr_qsfp28_attr(device_p) < 0){
|
||
|
goto err_reg_tvr_attr;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
goto err_reg_tvr_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_reg_tvr_attr:
|
||
|
SWPS_ERR("%s: fail! type=%d \n", __func__, transvr_obj->type);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_ioexp_attr_sfp_1(struct device *device_p){
|
||
|
/* Support machine type:
|
||
|
* - SFP : Magnolia
|
||
|
*/
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (device_create_file(device_p, &dev_attr_present) < 0) {
|
||
|
err_attr = "dev_attr_present";
|
||
|
goto err_ioexp_sfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_tx_fault) < 0) {
|
||
|
err_attr = "dev_attr_tx_fault";
|
||
|
goto err_ioexp_sfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_rxlos) < 0) {
|
||
|
err_attr = "dev_attr_rxlos";
|
||
|
goto err_ioexp_sfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_tx_disable) < 0) {
|
||
|
err_attr = "dev_attr_tx_disable";
|
||
|
goto err_ioexp_sfp1_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_ioexp_sfp1_attr:
|
||
|
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_ioexp_attr_sfp_2(struct device *device_p){
|
||
|
/* Support machine type:
|
||
|
* - SFP28 : Cypress
|
||
|
*/
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (register_ioexp_attr_sfp_1(device_p) < 0){
|
||
|
goto err_ioexp_sfp2_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_hard_rs0) < 0) {
|
||
|
err_attr = "dev_attr_hard_rs0";
|
||
|
goto err_ioexp_sfp2_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_hard_rs1) < 0) {
|
||
|
err_attr = "dev_attr_hard_rs1";
|
||
|
goto err_ioexp_sfp2_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_ioexp_sfp2_attr:
|
||
|
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_ioexp_attr_qsfp_1(struct device *device_p){
|
||
|
/* Support machine type:
|
||
|
* - QSFP : Magnolia, Redwood, Hudson32i
|
||
|
* - QSFP+ : Magnolia, Redwood, Hudson32i
|
||
|
* - QSFP28: Redwood
|
||
|
*/
|
||
|
char *err_attr = NULL;
|
||
|
|
||
|
if (device_create_file(device_p, &dev_attr_present) < 0) {
|
||
|
err_attr = "dev_attr_present";
|
||
|
goto err_ioexp_qsfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_reset) < 0) {
|
||
|
err_attr = "dev_attr_reset";
|
||
|
goto err_ioexp_qsfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_lpmod) < 0) {
|
||
|
err_attr = "dev_attr_lpmod";
|
||
|
goto err_ioexp_qsfp1_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_modsel) < 0) {
|
||
|
err_attr = "dev_attr_modsel";
|
||
|
goto err_ioexp_qsfp1_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_ioexp_qsfp1_attr:
|
||
|
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_modctl_attr(struct device *device_p){
|
||
|
|
||
|
char *err_msg = NULL;
|
||
|
|
||
|
if (device_create_file(device_p, &dev_attr_platform) < 0) {
|
||
|
err_msg = "dev_attr_platform";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_version) < 0) {
|
||
|
err_msg = "dev_attr_version";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_status) < 0) {
|
||
|
err_msg = "dev_attr_status";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_reset_i2c) < 0) {
|
||
|
err_msg = "dev_attr_reset_i2c";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_reset_swps) < 0) {
|
||
|
err_msg = "dev_attr_reset_swps";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_auto_config) < 0) {
|
||
|
err_msg = "dev_attr_auto_config";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_block_poll) < 0) {
|
||
|
err_msg = "dev_attr_block_poll";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
if (device_create_file(device_p, &dev_attr_io_no_init) < 0) {
|
||
|
err_msg = "dev_attr_io_no_init";
|
||
|
goto err_reg_modctl_attr;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_reg_modctl_attr:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_msg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_ioexp_attr(struct device *device_p,
|
||
|
struct transvr_obj_s *transvr_obj){
|
||
|
|
||
|
char *err_msg = "ERR";
|
||
|
|
||
|
switch (transvr_obj->ioexp_obj_p->ioexp_type){
|
||
|
case IOEXP_TYPE_MAGINOLIA_NAB:
|
||
|
case IOEXP_TYPE_MAGINOLIA_4AB:
|
||
|
case CPLD_TYPE_COTTONWOOD:
|
||
|
if (register_ioexp_attr_sfp_1(device_p) < 0){
|
||
|
err_msg = "register_ioexp_attr_sfp_1 fail";
|
||
|
goto err_reg_ioexp_attr;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IOEXP_TYPE_MAPLE_NABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_NABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_2T_EVT1_NABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_2T_EVT1_1ABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_2T_EVT1_3ABC:
|
||
|
case IOEXP_TYPE_SFP_8P_LAYOUT_1:
|
||
|
if (register_ioexp_attr_sfp_2(device_p) < 0){
|
||
|
err_msg = "register_ioexp_attr_sfp_2 fail";
|
||
|
goto err_reg_ioexp_attr;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IOEXP_TYPE_MAGINOLIA_7AB:
|
||
|
case IOEXP_TYPE_SPRUCE_7AB:
|
||
|
case IOEXP_TYPE_CYPRESS_7ABC:
|
||
|
case IOEXP_TYPE_REDWOOD_P01P08:
|
||
|
case IOEXP_TYPE_REDWOOD_P09P16:
|
||
|
case IOEXP_TYPE_HUDSON32IGA_P01P08:
|
||
|
case IOEXP_TYPE_HUDSON32IGA_P09P16:
|
||
|
case IOEXP_TYPE_TAHOE_5A:
|
||
|
case IOEXP_TYPE_TAHOE_6ABC:
|
||
|
case IOEXP_TYPE_SEQUOIA_NABC:
|
||
|
case IOEXP_TYPE_LAVENDER_P65:
|
||
|
case IOEXP_TYPE_MAPLE_0ABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_7ABC:
|
||
|
case IOEXP_TYPE_GULMOHAR_2T_EVT1_7ABC:
|
||
|
case IOEXP_TYPE_QSFP_6P_LAYOUT_1:
|
||
|
case IOEXP_TYPE_CEDAR_0ABC:
|
||
|
if (register_ioexp_attr_qsfp_1(device_p) < 0){
|
||
|
err_msg = "register_ioexp_attr_qsfp_1 fail";
|
||
|
goto err_reg_ioexp_attr;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
err_msg = "Unknow type";
|
||
|
goto err_reg_ioexp_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_reg_ioexp_attr:
|
||
|
SWPS_ERR("%s: %s <type>:%d \n",
|
||
|
__func__, err_msg, transvr_obj->ioexp_obj_p->ioexp_type);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_modctl_device(void) {
|
||
|
|
||
|
struct device *device_p = NULL;
|
||
|
int minor_comm = 0; /* Default minor number for common device */
|
||
|
dev_t dev_num = MKDEV(ctl_major, minor_comm);
|
||
|
char *err_msg = "ERROR";
|
||
|
|
||
|
device_p = device_create(swp_class_p, /* struct class *cls */
|
||
|
NULL, /* struct device *parent */
|
||
|
dev_num, /* dev_t devt */
|
||
|
NULL, /* void *private_data */
|
||
|
SWP_DEV_MODCTL); /* const char *fmt */
|
||
|
if (IS_ERR(device_p)){
|
||
|
err_msg = "device_create fail";
|
||
|
goto err_register_modctl_device_1;
|
||
|
}
|
||
|
if (register_modctl_attr(device_p) < 0) {
|
||
|
err_msg = "register_modctl_attr fail";
|
||
|
goto err_register_modctl_device_2;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_register_modctl_device_2:
|
||
|
device_unregister(device_p);
|
||
|
device_destroy(swp_class_p, dev_num);
|
||
|
err_register_modctl_device_1:
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_msg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_port_device(char *dev_name,
|
||
|
dev_t dev_num,
|
||
|
struct transvr_obj_s *transvr_obj){
|
||
|
|
||
|
struct device *device_p = NULL;
|
||
|
device_p = device_create(swp_class_p, /* struct class *cls */
|
||
|
NULL, /* struct device *parent */
|
||
|
dev_num, /* dev_t devt */
|
||
|
transvr_obj, /* void *private_data */
|
||
|
dev_name); /* const char *fmt */
|
||
|
if (IS_ERR(device_p)){
|
||
|
goto err_regswp_create_dev;
|
||
|
}
|
||
|
if (register_transvr_attr(device_p, transvr_obj) < 0){
|
||
|
goto err_regswp_reg_attr;
|
||
|
}
|
||
|
if (register_ioexp_attr(device_p, transvr_obj) < 0){
|
||
|
goto err_regswp_reg_attr;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_regswp_reg_attr:
|
||
|
device_unregister(device_p);
|
||
|
device_destroy(swp_class_p, dev_num);
|
||
|
err_regswp_create_dev:
|
||
|
SWPS_ERR("%s fail! <port>:%s\n", __func__, dev_name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
register_swp_module(void){
|
||
|
|
||
|
dev_t ctl_devt = 0;
|
||
|
dev_t port_devt = 0;
|
||
|
//int dev_total = port_total + 1; /* char_dev for module control */
|
||
|
|
||
|
/* Register device number */
|
||
|
if (alloc_chrdev_region(&ctl_devt, 0, 1, SWP_DEV_MODCTL) < 0){
|
||
|
SWPS_WARN("Allocate CTL MAJOR failure! \n");
|
||
|
goto err_register_swp_module_1;
|
||
|
}
|
||
|
if (alloc_chrdev_region(&port_devt, 0, port_total, SWP_CLS_NAME) < 0){
|
||
|
SWPS_WARN("Allocate PORT MAJOR failure! \n");
|
||
|
goto err_register_swp_module_2;
|
||
|
}
|
||
|
ctl_major = MAJOR(ctl_devt);
|
||
|
port_major = MAJOR(port_devt);
|
||
|
|
||
|
/* Create class object */
|
||
|
swp_class_p = class_create(THIS_MODULE, SWP_CLS_NAME);
|
||
|
if (IS_ERR(swp_class_p)) {
|
||
|
SWPS_ERR("Create class failure! \n");
|
||
|
goto err_register_swp_module_3;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_register_swp_module_3:
|
||
|
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
|
||
|
err_register_swp_module_2:
|
||
|
unregister_chrdev_region(MKDEV(ctl_major, 0), 1);
|
||
|
err_register_swp_module_1:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ========== Module initial relate ==========
|
||
|
*/
|
||
|
static int
|
||
|
create_ioexp_objs(void) {
|
||
|
|
||
|
int i, run_mod;
|
||
|
|
||
|
/* Clean IOEXP object */
|
||
|
clean_ioexp_objs();
|
||
|
/* Get running mode */
|
||
|
run_mod = IOEXP_MODE_DIRECT;
|
||
|
if (SWP_POLLING_ENABLE){
|
||
|
run_mod = IOEXP_MODE_POLLING;
|
||
|
}
|
||
|
/* Create IOEXP object */
|
||
|
for(i=0; i<ioexp_total; i++){
|
||
|
if (create_ioexp_obj(ioexp_layout[i].ioexp_id,
|
||
|
ioexp_layout[i].ioexp_type,
|
||
|
(ioexp_layout[i].addr),
|
||
|
run_mod) < 0) {
|
||
|
goto err_initioexp_create_obj_1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_initioexp_create_obj_1:
|
||
|
clean_ioexp_objs();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
create_port_objs(void) {
|
||
|
|
||
|
int port_id, chan_id, ioexp_id, ioexp_virt_offset;
|
||
|
int transvr_type, chipset_type, run_mod, i, j;
|
||
|
int minor_curr = 0;
|
||
|
int ok_count = 0;
|
||
|
int devlen_max = 31; // 32 - 1
|
||
|
char dev_name[32] = "ERROR";
|
||
|
char err_msg[64] = "ERROR";
|
||
|
struct transvr_obj_s* transvr_obj_p = NULL;
|
||
|
struct ioexp_obj_s *ioexp_obj_p = NULL;
|
||
|
struct device *dev_p = NULL;
|
||
|
|
||
|
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
|
||
|
/* Get info from port_layout[] */
|
||
|
port_id = port_layout[minor_curr].port_id;
|
||
|
chan_id = port_layout[minor_curr].chan_id;
|
||
|
ioexp_id = port_layout[minor_curr].ioexp_id;
|
||
|
ioexp_virt_offset = port_layout[minor_curr].ioexp_offset;
|
||
|
transvr_type = port_layout[minor_curr].transvr_type;
|
||
|
chipset_type = port_layout[minor_curr].chipset_type;
|
||
|
/* Get running mode */
|
||
|
run_mod = TRANSVR_MODE_DIRECT;
|
||
|
if (SWP_POLLING_ENABLE){
|
||
|
run_mod = TRANSVR_MODE_POLLING;
|
||
|
}
|
||
|
/* Prepare device name */
|
||
|
if (strlen(SWP_DEV_PORT) > devlen_max) {
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"SWP_DEV_PORT too long!");
|
||
|
goto err_initport_create_tranobj;
|
||
|
}
|
||
|
memset(dev_name, 0, sizeof(dev_name));
|
||
|
snprintf(dev_name, devlen_max, "%s%d", SWP_DEV_PORT, port_id);
|
||
|
/* Create transceiver object */
|
||
|
ioexp_obj_p = get_ioexp_obj(ioexp_id);
|
||
|
if (!ioexp_obj_p){
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"IOEXP object:%d not exist", ioexp_id);
|
||
|
goto err_initport_create_tranobj;
|
||
|
}
|
||
|
transvr_obj_p = create_transvr_obj(dev_name, chan_id, ioexp_obj_p,
|
||
|
ioexp_virt_offset, transvr_type,
|
||
|
chipset_type, run_mod);
|
||
|
if (!transvr_obj_p){
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"Create transceiver object fail <id>:%s", dev_name);
|
||
|
goto err_initport_create_tranobj;
|
||
|
}
|
||
|
/* Setup Lane_ID mapping */
|
||
|
i = ARRAY_SIZE(port_layout[minor_curr].lane_id);
|
||
|
j = ARRAY_SIZE(transvr_obj_p->lane_id);
|
||
|
if (i != j) {
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"Lane_id size inconsistent %d/%d", i, j);
|
||
|
goto err_initport_reg_device;
|
||
|
}
|
||
|
memcpy(transvr_obj_p->lane_id, port_layout[minor_curr].lane_id, i*sizeof(int));
|
||
|
/* Create and register device object */
|
||
|
if (register_port_device(dev_name, MKDEV(port_major, minor_curr), transvr_obj_p) < 0){
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"register_port_device fail");
|
||
|
goto err_initport_reg_device;
|
||
|
}
|
||
|
/* Setup device_ptr of transvr_obj */
|
||
|
dev_p = get_swpdev_by_name(dev_name);
|
||
|
if (!dev_p){
|
||
|
snprintf(err_msg, sizeof(err_msg),
|
||
|
"get_swpdev_by_name fail");
|
||
|
goto err_initport_reg_device;
|
||
|
}
|
||
|
transvr_obj_p->transvr_dev_p = dev_p;
|
||
|
/* Success */
|
||
|
ok_count++;
|
||
|
}
|
||
|
SWPS_INFO("%s: initialed %d port-dev\n",__func__, ok_count);
|
||
|
return 0;
|
||
|
|
||
|
err_initport_reg_device:
|
||
|
kfree(transvr_obj_p);
|
||
|
err_initport_create_tranobj:
|
||
|
clean_port_objs();
|
||
|
SWPS_ERR("%s: %s", __func__, err_msg);
|
||
|
SWPS_ERR("Dump: <port_id>:%d <chan_id>:%d <ioexp_id>:%d <voffset>:%d <tvr_type>:%d <run_mod>:%d\n",
|
||
|
port_id, chan_id, ioexp_id, ioexp_virt_offset, transvr_type, run_mod);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
init_dev_topology(void){
|
||
|
|
||
|
int err;
|
||
|
char *emsg = "ERR";
|
||
|
flag_mod_state = SWP_STATE_NORMAL;
|
||
|
|
||
|
err = init_ioexp_objs();
|
||
|
switch(err){
|
||
|
case 0: /* Normal */
|
||
|
SWPS_DEBUG("%s: normal case\n", __func__);
|
||
|
break;
|
||
|
|
||
|
case -1: /* topology error */
|
||
|
SWPS_DEBUG("%s: detect tier-1 topology initial failure <err>:%d\n",
|
||
|
__func__, err);
|
||
|
/* Reset and isolate */
|
||
|
err = reset_i2c_topology();
|
||
|
if (err < 0) {
|
||
|
emsg = "reset i2c topology fail";
|
||
|
goto err_init_dev_topology;
|
||
|
}
|
||
|
/* Re-initial again */
|
||
|
err = init_ioexp_objs();
|
||
|
if (err < 0) {
|
||
|
emsg = "re-init ioexp objects fail";
|
||
|
goto err_init_dev_topology;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case -2: /* Internal error */
|
||
|
SWPS_DEBUG("%s: internal error case\n", __func__);
|
||
|
err = -2;
|
||
|
emsg = "internal error";
|
||
|
goto err_init_dev_topology;
|
||
|
|
||
|
default:
|
||
|
SWPS_DEBUG("%s: undefined error case\n", __func__);
|
||
|
emsg = "undefined error case";
|
||
|
goto err_init_dev_topology;
|
||
|
}
|
||
|
SWPS_DEBUG("%s: initial I2C topology success\n", __func__);
|
||
|
return 0;
|
||
|
|
||
|
err_init_dev_topology:
|
||
|
SWPS_ERR("%s: %s <err>:%d\n", __func__, emsg, err);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
init_polling_task(void){
|
||
|
|
||
|
if (SWP_POLLING_ENABLE){
|
||
|
schedule_delayed_work(&swp_polling, _get_polling_period());
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
init_swps_common(void){
|
||
|
|
||
|
char *err_msg = "ERR";
|
||
|
|
||
|
block_polling = 0;
|
||
|
auto_config = 0;
|
||
|
if ((SWP_AUTOCONFIG_ENABLE) && (SWP_POLLING_ENABLE)){
|
||
|
auto_config = 1;
|
||
|
}
|
||
|
if (register_modctl_device() < 0) {
|
||
|
err_msg = "register_modctl_device fail";
|
||
|
goto err_init_swps_common_1;
|
||
|
}
|
||
|
if (_update_auto_config_2_trnasvr() < 0) {
|
||
|
err_msg = "_update_auto_config_2_trnasvr fail";
|
||
|
goto err_init_swps_common_1;
|
||
|
}
|
||
|
if (init_polling_task() < 0){
|
||
|
err_msg = "init_polling_task fail";
|
||
|
goto err_init_swps_common_1;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err_init_swps_common_1:
|
||
|
clean_swps_common();
|
||
|
SWPS_ERR("%s: %s\n", __func__, err_msg);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int __init
|
||
|
swp_module_init(void){
|
||
|
|
||
|
if (get_platform_type() < 0){
|
||
|
goto err_init_out;
|
||
|
}
|
||
|
if (get_layout_info() < 0){
|
||
|
goto err_init_out;
|
||
|
}
|
||
|
if (register_swp_module() < 0){
|
||
|
goto err_init_out;
|
||
|
}
|
||
|
if (create_ioexp_objs() < 0){
|
||
|
goto err_init_ioexp;
|
||
|
}
|
||
|
if (create_port_objs() < 0){
|
||
|
goto err_init_portobj;
|
||
|
}
|
||
|
if (init_mux_objs(gpio_rest_mux) < 0){
|
||
|
goto err_init_mux;
|
||
|
}
|
||
|
if (init_dev_topology() < 0){
|
||
|
goto err_init_topology;
|
||
|
}
|
||
|
if (init_swps_common() < 0){
|
||
|
goto err_init_topology;
|
||
|
}
|
||
|
SWPS_INFO("Inventec switch-port module V.%s initial success.\n", SWP_VERSION);
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
err_init_topology:
|
||
|
clean_mux_objs();
|
||
|
err_init_mux:
|
||
|
clean_port_objs();
|
||
|
err_init_portobj:
|
||
|
clean_ioexp_objs();
|
||
|
err_init_ioexp:
|
||
|
class_destroy(swp_class_p);
|
||
|
unregister_chrdev_region(MKDEV(ctl_major, 0), 1);
|
||
|
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
|
||
|
err_init_out:
|
||
|
SWPS_ERR("Inventec switch-port module V.%s initial failure.\n", SWP_VERSION);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void __exit
|
||
|
swp_module_exit(void){
|
||
|
|
||
|
clean_swps_common();
|
||
|
clean_port_objs();
|
||
|
clean_ioexp_objs();
|
||
|
clean_mux_objs();
|
||
|
class_destroy(swp_class_p);
|
||
|
unregister_chrdev_region(MKDEV(ctl_major, 0), 1);
|
||
|
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
|
||
|
SWPS_INFO("Remove Inventec switch-port module success.\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Module information */
|
||
|
MODULE_AUTHOR(SWP_AUTHOR);
|
||
|
MODULE_DESCRIPTION(SWP_DESC);
|
||
|
MODULE_VERSION(SWP_VERSION);
|
||
|
MODULE_LICENSE(SWP_LICENSE);
|
||
|
MODULE_SOFTDEP("pre: inv_platform");
|
||
|
module_param(gpio_base, int, S_IRUGO);
|
||
|
|
||
|
module_init(swp_module_init);
|
||
|
module_exit(swp_module_exit);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|