mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-25 17:27:01 +00:00
Merge pull request #350 from brandonchuang/as7512_32x
[as7512-32x] Add support for OOM
This commit is contained in:
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright (C) Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* This module supports the accton cpld that hold the channel select
|
||||
* mechanism for other i2c slave devices, such as SFP.
|
||||
* This includes the:
|
||||
* Accton as7512_32x CPLD1/CPLD2/CPLD3
|
||||
*
|
||||
* Based on:
|
||||
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
|
||||
* Copyright (C) 2006
|
||||
*
|
||||
* Based on:
|
||||
* pca954x.c from Ken Harrenstien
|
||||
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
|
||||
*
|
||||
* Based on:
|
||||
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
|
||||
* and
|
||||
* pca9540.c from Jean Delvare <khali@linux-fr.org>.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define I2C_RW_RETRY_COUNT 10
|
||||
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
|
||||
|
||||
static LIST_HEAD(cpld_client_list);
|
||||
static struct mutex list_lock;
|
||||
|
||||
struct cpld_client_node {
|
||||
struct i2c_client *client;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
enum cpld_type {
|
||||
as7512_32x_cpld1,
|
||||
as7512_32x_cpld2,
|
||||
as7512_32x_cpld3
|
||||
};
|
||||
|
||||
struct as7512_32x_cpld_data {
|
||||
enum cpld_type type;
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
};
|
||||
|
||||
static const struct i2c_device_id as7512_32x_cpld_id[] = {
|
||||
{ "as7512_32x_cpld1", as7512_32x_cpld1 },
|
||||
{ "as7512_32x_cpld2", as7512_32x_cpld2 },
|
||||
{ "as7512_32x_cpld3", as7512_32x_cpld3 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, as7512_32x_cpld_id);
|
||||
|
||||
#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index
|
||||
#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index
|
||||
#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index
|
||||
#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index
|
||||
|
||||
enum as7512_32x_cpld1_sysfs_attributes {
|
||||
CPLD_VERSION,
|
||||
ACCESS,
|
||||
MODULE_PRESENT_ALL,
|
||||
MODULE_RXLOS_ALL,
|
||||
/* transceiver attributes */
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(1),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(2),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(3),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(4),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(5),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(6),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(7),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(8),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(9),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(10),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(11),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(12),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(13),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(14),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(15),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(16),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(17),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(18),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(19),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(20),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(21),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(22),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(23),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(24),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(25),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(26),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(27),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(28),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(29),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(30),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(31),
|
||||
TRANSCEIVER_PRESENT_ATTR_ID(32),
|
||||
};
|
||||
|
||||
/* sysfs attributes for hwmon
|
||||
*/
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_present_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_version(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static int as7512_32x_cpld_read_internal(struct i2c_client *client, u8 reg);
|
||||
static int as7512_32x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value);
|
||||
|
||||
/* transceiver attributes */
|
||||
#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index)
|
||||
#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr
|
||||
|
||||
static SENSOR_DEVICE_ATTR(version, S_IRUGO, show_version, NULL, CPLD_VERSION);
|
||||
static SENSOR_DEVICE_ATTR(access, S_IWUSR, NULL, access, ACCESS);
|
||||
/* transceiver attributes */
|
||||
static SENSOR_DEVICE_ATTR(module_present_all, S_IRUGO, show_present_all, NULL, MODULE_PRESENT_ALL);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(7);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(8);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(9);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(10);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(11);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(12);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(13);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(14);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(15);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(16);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(17);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(18);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(19);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(20);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(21);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(22);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(23);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(24);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(25);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(26);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(27);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(28);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(29);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(30);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(31);
|
||||
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(32);
|
||||
|
||||
static struct attribute *as7512_32x_cpld1_attributes[] = {
|
||||
&sensor_dev_attr_version.dev_attr.attr,
|
||||
&sensor_dev_attr_access.dev_attr.attr,
|
||||
/* transceiver attributes */
|
||||
&sensor_dev_attr_module_present_all.dev_attr.attr,
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(1),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(2),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(3),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(4),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(5),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(6),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(7),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(8),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(9),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(10),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(11),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(12),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(13),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(14),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(15),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(16),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(17),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(18),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(19),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(20),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(21),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(22),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(23),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(24),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(25),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(26),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(27),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(28),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(29),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(30),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(31),
|
||||
DECLARE_TRANSCEIVER_PRESENT_ATTR(32),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group as7512_32x_cpld1_group = {
|
||||
.attrs = as7512_32x_cpld1_attributes,
|
||||
};
|
||||
|
||||
static struct attribute *as7512_32x_cpld2_attributes[] = {
|
||||
&sensor_dev_attr_version.dev_attr.attr,
|
||||
&sensor_dev_attr_access.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group as7512_32x_cpld2_group = {
|
||||
.attrs = as7512_32x_cpld2_attributes,
|
||||
};
|
||||
|
||||
static struct attribute *as7512_32x_cpld3_attributes[] = {
|
||||
&sensor_dev_attr_version.dev_attr.attr,
|
||||
&sensor_dev_attr_access.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group as7512_32x_cpld3_group = {
|
||||
.attrs = as7512_32x_cpld3_attributes,
|
||||
};
|
||||
|
||||
static ssize_t show_present_all(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
int i, status;
|
||||
u8 values[4] = {0};
|
||||
u8 regs[] = {0x30, 0x31, 0x32, 0x33};
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as7512_32x_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||
status = as7512_32x_cpld_read_internal(client, regs[i]);
|
||||
|
||||
if (status < 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
values[i] = ~(u8)status;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%.2x %.2x %.2x %.2x\n",
|
||||
values[0], values[1], values[2], values[3]);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as7512_32x_cpld_data *data = i2c_get_clientdata(client);
|
||||
int status = 0;
|
||||
u8 reg = 0, mask = 0, revert = 0;
|
||||
|
||||
switch (attr->index) {
|
||||
case MODULE_PRESENT_1 ... MODULE_PRESENT_8:
|
||||
reg = 0x30;
|
||||
mask = 0x1 << (attr->index - MODULE_PRESENT_1);
|
||||
break;
|
||||
case MODULE_PRESENT_9 ... MODULE_PRESENT_16:
|
||||
reg = 0x31;
|
||||
mask = 0x1 << (attr->index - MODULE_PRESENT_9);
|
||||
break;
|
||||
case MODULE_PRESENT_17 ... MODULE_PRESENT_24:
|
||||
reg = 0x32;
|
||||
mask = 0x1 << (attr->index - MODULE_PRESENT_17);
|
||||
break;
|
||||
case MODULE_PRESENT_25 ... MODULE_PRESENT_32:
|
||||
reg = 0x33;
|
||||
mask = 0x1 << (attr->index - MODULE_PRESENT_25);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_32) {
|
||||
revert = 1;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = as7512_32x_cpld_read_internal(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask));
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int status;
|
||||
u32 addr, val;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as7512_32x_cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr > 0xFF || val > 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = as7512_32x_cpld_write_internal(client, addr, val);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void as7512_32x_cpld_add_client(struct i2c_client *client)
|
||||
{
|
||||
struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
|
||||
|
||||
if (!node) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr);
|
||||
return;
|
||||
}
|
||||
|
||||
node->client = client;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_add(&node->list, &cpld_client_list);
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static void as7512_32x_cpld_remove_client(struct i2c_client *client)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client == client) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_del(list_node);
|
||||
kfree(cpld_node);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int val = 0;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, 0x1);
|
||||
|
||||
if (val < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d", val);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init/probing/exit functions
|
||||
*/
|
||||
static int as7512_32x_cpld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||
struct as7512_32x_cpld_data *data;
|
||||
int ret = -ENODEV;
|
||||
const struct attribute_group *group = NULL;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||
goto exit;
|
||||
|
||||
data = kzalloc(sizeof(struct as7512_32x_cpld_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->type = id->driver_data;
|
||||
|
||||
/* Register sysfs hooks */
|
||||
switch (data->type) {
|
||||
case as7512_32x_cpld1:
|
||||
group = &as7512_32x_cpld1_group;
|
||||
break;
|
||||
case as7512_32x_cpld2:
|
||||
group = &as7512_32x_cpld2_group;
|
||||
break;
|
||||
case as7512_32x_cpld3:
|
||||
group = &as7512_32x_cpld3_group;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (group) {
|
||||
ret = sysfs_create_group(&client->dev.kobj, group);
|
||||
if (ret) {
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
as7512_32x_cpld_add_client(client);
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int as7512_32x_cpld_remove(struct i2c_client *client)
|
||||
{
|
||||
struct as7512_32x_cpld_data *data = i2c_get_clientdata(client);
|
||||
const struct attribute_group *group = NULL;
|
||||
|
||||
as7512_32x_cpld_remove_client(client);
|
||||
|
||||
/* Remove sysfs hooks */
|
||||
switch (data->type) {
|
||||
case as7512_32x_cpld1:
|
||||
group = &as7512_32x_cpld1_group;
|
||||
break;
|
||||
case as7512_32x_cpld2:
|
||||
group = &as7512_32x_cpld2_group;
|
||||
break;
|
||||
case as7512_32x_cpld3:
|
||||
group = &as7512_32x_cpld3_group;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (group) {
|
||||
sysfs_remove_group(&client->dev.kobj, group);
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int as7512_32x_cpld_read_internal(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_read_byte_data(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int as7512_32x_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_write_byte_data(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int as7512_32x_cpld_read(unsigned short cpld_addr, u8 reg)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EPERM;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = as7512_32x_cpld_read_internal(cpld_node->client, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(as7512_32x_cpld_read);
|
||||
|
||||
int as7512_32x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EIO;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = as7512_32x_cpld_write_internal(cpld_node->client, reg, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(as7512_32x_cpld_write);
|
||||
|
||||
static struct i2c_driver as7512_32x_cpld_driver = {
|
||||
.driver = {
|
||||
.name = "as7512_32x_cpld",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = as7512_32x_cpld_probe,
|
||||
.remove = as7512_32x_cpld_remove,
|
||||
.id_table = as7512_32x_cpld_id,
|
||||
};
|
||||
|
||||
static int __init as7512_32x_cpld_init(void)
|
||||
{
|
||||
mutex_init(&list_lock);
|
||||
return i2c_add_driver(&as7512_32x_cpld_driver);
|
||||
}
|
||||
|
||||
static void __exit as7512_32x_cpld_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as7512_32x_cpld_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("Accton I2C CPLD driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(as7512_32x_cpld_init);
|
||||
module_exit(as7512_32x_cpld_exit);
|
||||
|
||||
@@ -36,8 +36,6 @@ static struct as7512_32x_fan_data *as7512_32x_fan_update_device(struct device *d
|
||||
static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
/* fan related data, the index should match sysfs_fan_attributes
|
||||
*/
|
||||
@@ -487,24 +485,9 @@ static struct i2c_driver as7512_32x_fan_driver = {
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int __init as7512_32x_fan_init(void)
|
||||
{
|
||||
extern int platform_accton_as7512_32x(void);
|
||||
if (!platform_accton_as7512_32x()) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return i2c_add_driver(&as7512_32x_fan_driver);
|
||||
}
|
||||
|
||||
static void __exit as7512_32x_fan_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as7512_32x_fan_driver);
|
||||
}
|
||||
module_i2c_driver(as7512_32x_fan_driver);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("as7512_32x_fan driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(as7512_32x_fan_init);
|
||||
module_exit(as7512_32x_fan_exit);
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
extern int accton_i2c_cpld_read (unsigned short cpld_addr, u8 reg);
|
||||
extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
extern int as7512_32x_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int as7512_32x_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
#define DRVNAME "as7512_32x_led"
|
||||
#define NUM_OF_LED_REG 5
|
||||
@@ -191,12 +191,12 @@ static u8 led_light_mode_to_reg_val(enum led_type type,
|
||||
|
||||
static int accton_as7512_32x_led_read_value(u8 reg)
|
||||
{
|
||||
return accton_i2c_cpld_read(LED_CNTRLER_I2C_ADDRESS, reg);
|
||||
return as7512_32x_cpld_read(LED_CNTRLER_I2C_ADDRESS, reg);
|
||||
}
|
||||
|
||||
static int accton_as7512_32x_led_write_value(u8 reg, u8 value)
|
||||
{
|
||||
return accton_i2c_cpld_write(LED_CNTRLER_I2C_ADDRESS, reg, value);
|
||||
return as7512_32x_cpld_write(LED_CNTRLER_I2C_ADDRESS, reg, value);
|
||||
}
|
||||
|
||||
static void accton_as7512_32x_led_update(void)
|
||||
@@ -457,11 +457,6 @@ static int __init accton_as7512_32x_led_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
extern int platform_accton_as7512_32x(void);
|
||||
if (!platform_accton_as7512_32x()) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = platform_driver_register(&accton_as7512_32x_led_driver);
|
||||
if (ret < 0) {
|
||||
goto exit;
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
static ssize_t show_status(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static ssize_t show_model_name(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static int as7512_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,int data_len);
|
||||
extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int as7512_32x_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
|
||||
/* Addresses scanned
|
||||
*/
|
||||
@@ -238,7 +238,7 @@ static struct as7512_32x_psu_data *as7512_32x_psu_update_device(struct device *d
|
||||
dev_dbg(&client->dev, "Starting as7512_32x update\n");
|
||||
|
||||
/* Read psu status */
|
||||
status = accton_i2c_cpld_read(0x60, 0x2);
|
||||
status = as7512_32x_cpld_read(0x60, 0x2);
|
||||
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status);
|
||||
@@ -276,24 +276,9 @@ exit:
|
||||
return data;
|
||||
}
|
||||
|
||||
static int __init as7512_32x_psu_init(void)
|
||||
{
|
||||
extern int platform_accton_as7512_32x(void);
|
||||
if (!platform_accton_as7512_32x()) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return i2c_add_driver(&as7512_32x_psu_driver);
|
||||
}
|
||||
|
||||
static void __exit as7512_32x_psu_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as7512_32x_psu_driver);
|
||||
}
|
||||
module_i2c_driver(as7512_32x_psu_driver);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("as7512_32x_psu driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(as7512_32x_psu_init);
|
||||
module_exit(as7512_32x_psu_exit);
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
/*
|
||||
* An hwmon driver for accton as7512_32x sfp
|
||||
*
|
||||
* Copyright (C) 2014 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define BIT_INDEX(i) (1UL << (i))
|
||||
|
||||
|
||||
/* Addresses scanned
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
|
||||
|
||||
/* Each client has this additional data
|
||||
*/
|
||||
struct as7512_32x_sfp_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if registers are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
int port; /* Front port index */
|
||||
char eeprom[256]; /* eeprom data */
|
||||
u32 is_present; /* present status */
|
||||
};
|
||||
|
||||
static struct as7512_32x_sfp_data *as7512_32x_sfp_update_device(struct device *dev);
|
||||
static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf);
|
||||
static ssize_t show_present(struct device *dev, struct device_attribute *da,char *buf);
|
||||
static ssize_t show_eeprom(struct device *dev, struct device_attribute *da, char *buf);
|
||||
extern int accton_i2c_cpld_read(unsigned short cpld_addr, u8 reg);
|
||||
extern int accton_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
enum as7512_32x_sfp_sysfs_attributes {
|
||||
SFP_PORT_NUMBER,
|
||||
SFP_IS_PRESENT,
|
||||
SFP_IS_PRESENT_ALL,
|
||||
SFP_EEPROM
|
||||
};
|
||||
|
||||
/* sysfs attributes for hwmon
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, SFP_PORT_NUMBER);
|
||||
static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, SFP_IS_PRESENT);
|
||||
static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, SFP_IS_PRESENT_ALL);
|
||||
static SENSOR_DEVICE_ATTR(sfp_eeprom, S_IRUGO, show_eeprom, NULL, SFP_EEPROM);
|
||||
|
||||
static struct attribute *as7512_32x_sfp_attributes[] = {
|
||||
&sensor_dev_attr_sfp_port_number.dev_attr.attr,
|
||||
&sensor_dev_attr_sfp_is_present.dev_attr.attr,
|
||||
&sensor_dev_attr_sfp_is_present_all.dev_attr.attr,
|
||||
&sensor_dev_attr_sfp_eeprom.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t show_port_number(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as7512_32x_sfp_data *data = i2c_get_clientdata(client);
|
||||
|
||||
return sprintf(buf, "%d\n", data->port+1);
|
||||
}
|
||||
|
||||
/* Error-check the CPLD read results. */
|
||||
#define VALIDATED_READ(_buf, _rv, _read_expr, _invert) \
|
||||
do { \
|
||||
_rv = (_read_expr); \
|
||||
if(_rv < 0) { \
|
||||
return sprintf(_buf, "READ ERROR\n"); \
|
||||
} \
|
||||
if(_invert) { \
|
||||
_rv = ~_rv; \
|
||||
} \
|
||||
_rv &= 0xFF; \
|
||||
} while(0)
|
||||
|
||||
static ssize_t show_present(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
|
||||
if(attr->index == SFP_IS_PRESENT_ALL) {
|
||||
int values[4];
|
||||
/*
|
||||
* Report the SFP_PRESENCE status for all ports.
|
||||
*/
|
||||
|
||||
/* SFP_PRESENT Ports 1-8 */
|
||||
VALIDATED_READ(buf, values[0], accton_i2c_cpld_read(0x60, 0x30), 1);
|
||||
/* SFP_PRESENT Ports 9-16 */
|
||||
VALIDATED_READ(buf, values[1], accton_i2c_cpld_read(0x60, 0x31), 1);
|
||||
/* SFP_PRESENT Ports 17-24 */
|
||||
VALIDATED_READ(buf, values[2], accton_i2c_cpld_read(0x60, 0x32), 1);
|
||||
/* SFP_PRESENT Ports 25-32 */
|
||||
VALIDATED_READ(buf, values[3], accton_i2c_cpld_read(0x60, 0x33), 1);
|
||||
|
||||
/* Return values 1 -> 32 in order */
|
||||
return sprintf(buf, "%.2x %.2x %.2x %.2x\n",
|
||||
values[0], values[1], values[2], values[3]);
|
||||
}
|
||||
else { /* SFP_IS_PRESENT */
|
||||
struct as7512_32x_sfp_data *data = as7512_32x_sfp_update_device(dev);
|
||||
|
||||
if (!data->valid) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", data->is_present);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t show_eeprom(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct as7512_32x_sfp_data *data = as7512_32x_sfp_update_device(dev);
|
||||
|
||||
if (!data->valid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data->is_present) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buf, data->eeprom, sizeof(data->eeprom));
|
||||
|
||||
return sizeof(data->eeprom);
|
||||
}
|
||||
|
||||
static const struct attribute_group as7512_32x_sfp_group = {
|
||||
.attrs = as7512_32x_sfp_attributes,
|
||||
};
|
||||
|
||||
static int as7512_32x_sfp_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct as7512_32x_sfp_data *data;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct as7512_32x_sfp_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
status = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_init(&data->update_lock);
|
||||
data->port = dev_id->driver_data;
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
dev_info(&client->dev, "chip found\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &as7512_32x_sfp_group);
|
||||
if (status) {
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
status = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "%s: sfp '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &as7512_32x_sfp_group);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int as7512_32x_sfp_remove(struct i2c_client *client)
|
||||
{
|
||||
struct as7512_32x_sfp_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &as7512_32x_sfp_group);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum port_numbers {
|
||||
as7512_32x_sfp1, as7512_32x_sfp2, as7512_32x_sfp3, as7512_32x_sfp4,
|
||||
as7512_32x_sfp5, as7512_32x_sfp6, as7512_32x_sfp7, as7512_32x_sfp8,
|
||||
as7512_32x_sfp9, as7512_32x_sfp10,as7512_32x_sfp11,as7512_32x_sfp12,
|
||||
as7512_32x_sfp13,as7512_32x_sfp14,as7512_32x_sfp15,as7512_32x_sfp16,
|
||||
as7512_32x_sfp17,as7512_32x_sfp18,as7512_32x_sfp19,as7512_32x_sfp20,
|
||||
as7512_32x_sfp21,as7512_32x_sfp22,as7512_32x_sfp23,as7512_32x_sfp24,
|
||||
as7512_32x_sfp25,as7512_32x_sfp26,as7512_32x_sfp27,as7512_32x_sfp28,
|
||||
as7512_32x_sfp29,as7512_32x_sfp30,as7512_32x_sfp31,as7512_32x_sfp32
|
||||
};
|
||||
|
||||
static const struct i2c_device_id as7512_32x_sfp_id[] = {
|
||||
{ "as7512_32x_sfp1", as7512_32x_sfp1 }, { "as7512_32x_sfp2", as7512_32x_sfp2 },
|
||||
{ "as7512_32x_sfp3", as7512_32x_sfp3 }, { "as7512_32x_sfp4", as7512_32x_sfp4 },
|
||||
{ "as7512_32x_sfp5", as7512_32x_sfp5 }, { "as7512_32x_sfp6", as7512_32x_sfp6 },
|
||||
{ "as7512_32x_sfp7", as7512_32x_sfp7 }, { "as7512_32x_sfp8", as7512_32x_sfp8 },
|
||||
{ "as7512_32x_sfp9", as7512_32x_sfp9 }, { "as7512_32x_sfp10", as7512_32x_sfp10 },
|
||||
{ "as7512_32x_sfp11", as7512_32x_sfp11 }, { "as7512_32x_sfp12", as7512_32x_sfp12 },
|
||||
{ "as7512_32x_sfp13", as7512_32x_sfp13 }, { "as7512_32x_sfp14", as7512_32x_sfp14 },
|
||||
{ "as7512_32x_sfp15", as7512_32x_sfp15 }, { "as7512_32x_sfp16", as7512_32x_sfp16 },
|
||||
{ "as7512_32x_sfp17", as7512_32x_sfp17 }, { "as7512_32x_sfp18", as7512_32x_sfp18 },
|
||||
{ "as7512_32x_sfp19", as7512_32x_sfp19 }, { "as7512_32x_sfp20", as7512_32x_sfp20 },
|
||||
{ "as7512_32x_sfp21", as7512_32x_sfp21 }, { "as7512_32x_sfp22", as7512_32x_sfp22 },
|
||||
{ "as7512_32x_sfp23", as7512_32x_sfp23 }, { "as7512_32x_sfp24", as7512_32x_sfp24 },
|
||||
{ "as7512_32x_sfp25", as7512_32x_sfp25 }, { "as7512_32x_sfp26", as7512_32x_sfp26 },
|
||||
{ "as7512_32x_sfp27", as7512_32x_sfp27 }, { "as7512_32x_sfp28", as7512_32x_sfp28 },
|
||||
{ "as7512_32x_sfp29", as7512_32x_sfp29 }, { "as7512_32x_sfp30", as7512_32x_sfp30 },
|
||||
{ "as7512_32x_sfp31", as7512_32x_sfp31 }, { "as7512_32x_sfp32", as7512_32x_sfp32 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, as7512_32x_sfp_id);
|
||||
|
||||
static struct i2c_driver as7512_32x_sfp_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "as7512_32x_sfp",
|
||||
},
|
||||
.probe = as7512_32x_sfp_probe,
|
||||
.remove = as7512_32x_sfp_remove,
|
||||
.id_table = as7512_32x_sfp_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int as7512_32x_sfp_read_block(struct i2c_client *client, u8 command, u8 *data,
|
||||
int data_len)
|
||||
{
|
||||
int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data);
|
||||
|
||||
if (unlikely(result < 0))
|
||||
goto abort;
|
||||
if (unlikely(result != data_len)) {
|
||||
result = -EIO;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
abort:
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct as7512_32x_sfp_data *as7512_32x_sfp_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct as7512_32x_sfp_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
int status = -1;
|
||||
int i = 0;
|
||||
u8 cpld_reg = 0x30 + (data->port/8);
|
||||
|
||||
data->valid = 0;
|
||||
|
||||
/* Read present status of the specified port number */
|
||||
data->is_present = 0;
|
||||
status = accton_i2c_cpld_read(0x60, cpld_reg);
|
||||
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "cpld(0x60) reg(0x%x) err %d\n", cpld_reg, status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->is_present = (status & (1 << (data->port % 8))) ? 0 : 1;
|
||||
|
||||
/* Read eeprom data based on port number */
|
||||
memset(data->eeprom, 0, sizeof(data->eeprom));
|
||||
|
||||
/* Check if the port is present */
|
||||
if (data->is_present) {
|
||||
/* read eeprom */
|
||||
for (i = 0; i < sizeof(data->eeprom)/I2C_SMBUS_BLOCK_MAX; i++) {
|
||||
status = as7512_32x_sfp_read_block(client, i*I2C_SMBUS_BLOCK_MAX,
|
||||
data->eeprom+(i*I2C_SMBUS_BLOCK_MAX),
|
||||
I2C_SMBUS_BLOCK_MAX);
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "unable to read eeprom from port(%d)\n", data->port);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int __init as7512_32x_sfp_init(void)
|
||||
{
|
||||
extern int platform_accton_as7512_32x(void);
|
||||
if (!platform_accton_as7512_32x()) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return i2c_add_driver(&as7512_32x_sfp_driver);
|
||||
}
|
||||
|
||||
static void __exit as7512_32x_sfp_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as7512_32x_sfp_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("accton as7512_32x_sfp driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(as7512_32x_sfp_init);
|
||||
module_exit(as7512_32x_sfp_exit);
|
||||
@@ -25,43 +25,16 @@
|
||||
***********************************************************/
|
||||
#include <onlp/platformi/sfpi.h>
|
||||
|
||||
#include <fcntl.h> /* For O_RDWR && open */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <onlplib/i2c.h>
|
||||
#include <onlplib/file.h>
|
||||
#include "platform_lib.h"
|
||||
|
||||
#define MAX_SFP_PATH 64
|
||||
static char sfp_node_path[MAX_SFP_PATH] = {0};
|
||||
#define FRONT_PORT_TO_CPLD_MUX_INDEX(port) (port+18)
|
||||
|
||||
static int
|
||||
as7512_32x_sfp_node_read_int(char *node_path, int *value, int data_len)
|
||||
{
|
||||
int ret = 0;
|
||||
char buf[8];
|
||||
*value = 0;
|
||||
#define PORT_BUS_INDEX(port) (port+18)
|
||||
#define PORT_EEPROM_FORMAT "/sys/bus/i2c/devices/%d-0050/eeprom"
|
||||
|
||||
ret = deviceNodeReadString(node_path, buf, sizeof(buf), data_len);
|
||||
|
||||
if (ret == 0) {
|
||||
*value = atoi(buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char*
|
||||
as7512_32x_sfp_get_port_path(int port, char *node_name)
|
||||
{
|
||||
sprintf(sfp_node_path, "/sys/bus/i2c/devices/%d-0050/%s",
|
||||
FRONT_PORT_TO_CPLD_MUX_INDEX(port),
|
||||
node_name);
|
||||
|
||||
return sfp_node_path;
|
||||
}
|
||||
#define MODULE_PRESENT_FORMAT "/sys/bus/i2c/devices/4-0060/module_present_%d"
|
||||
#define MODULE_PRESENT_ALL_ATTR "/sys/bus/i2c/devices/4-0060/module_present_all"
|
||||
|
||||
/************************************************************
|
||||
*
|
||||
@@ -100,13 +73,12 @@ onlp_sfpi_is_present(int port)
|
||||
* Return < 0 if error.
|
||||
*/
|
||||
int present;
|
||||
char* path = as7512_32x_sfp_get_port_path(port, "sfp_is_present");
|
||||
|
||||
if (as7512_32x_sfp_node_read_int(path, &present, 0) != 0) {
|
||||
if (onlp_file_read_int(&present, MODULE_PRESENT_FORMAT, (port+1)) < 0) {
|
||||
AIM_LOG_ERROR("Unable to read present status from port(%d)\r\n", port);
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
@@ -114,12 +86,10 @@ int
|
||||
onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
{
|
||||
uint32_t bytes[4];
|
||||
char* path;
|
||||
FILE* fp;
|
||||
|
||||
path = as7512_32x_sfp_get_port_path(0, "sfp_is_present_all");
|
||||
fp = fopen(path, "r");
|
||||
|
||||
fp = fopen(MODULE_PRESENT_ALL_ATTR, "r");
|
||||
|
||||
if(fp == NULL) {
|
||||
AIM_LOG_ERROR("Unable to open the sfp_is_present_all device file.");
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
@@ -154,33 +124,59 @@ onlp_sfpi_presence_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
return ONLP_STATUS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_rx_los_bitmap_get(onlp_sfp_bitmap_t* dst)
|
||||
{
|
||||
return ONLP_STATUS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_eeprom_read(int port, uint8_t data[256])
|
||||
{
|
||||
char* path = as7512_32x_sfp_get_port_path(port, "sfp_eeprom");
|
||||
|
||||
/*
|
||||
* Read the SFP eeprom into data[]
|
||||
*
|
||||
* Return MISSING if SFP is missing.
|
||||
* Return OK if eeprom is read
|
||||
*/
|
||||
int size = 0;
|
||||
memset(data, 0, 256);
|
||||
|
||||
if (deviceNodeReadBinary(path, (char*)data, 256, 256) != 0) {
|
||||
|
||||
if(onlp_file_read(data, 256, &size, PORT_EEPROM_FORMAT, PORT_BUS_INDEX(port)) != ONLP_STATUS_OK) {
|
||||
AIM_LOG_ERROR("Unable to read eeprom from port(%d)\r\n", port);
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
if (size != 256) {
|
||||
AIM_LOG_ERROR("Unable to read eeprom from port(%d), size is different!\r\n", port);
|
||||
return ONLP_STATUS_E_INTERNAL;
|
||||
}
|
||||
|
||||
return ONLP_STATUS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_dev_readb(int port, uint8_t devaddr, uint8_t addr)
|
||||
{
|
||||
int bus = PORT_BUS_INDEX(port);
|
||||
return onlp_i2c_readb(bus, devaddr, addr, ONLP_I2C_F_FORCE);
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_dev_writeb(int port, uint8_t devaddr, uint8_t addr, uint8_t value)
|
||||
{
|
||||
int bus = PORT_BUS_INDEX(port);
|
||||
return onlp_i2c_writeb(bus, devaddr, addr, value, ONLP_I2C_F_FORCE);
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_dev_readw(int port, uint8_t devaddr, uint8_t addr)
|
||||
{
|
||||
int bus = PORT_BUS_INDEX(port);
|
||||
return onlp_i2c_readw(bus, devaddr, addr, ONLP_I2C_F_FORCE);
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_dev_writew(int port, uint8_t devaddr, uint8_t addr, uint16_t value)
|
||||
{
|
||||
int bus = PORT_BUS_INDEX(port);
|
||||
return onlp_i2c_writew(bus, devaddr, addr, value, ONLP_I2C_F_FORCE);
|
||||
}
|
||||
|
||||
int
|
||||
onlp_sfpi_denit(void)
|
||||
{
|
||||
|
||||
@@ -8,10 +8,10 @@ class OnlPlatform_x86_64_accton_as7512_32x_r0(OnlPlatformAccton,
|
||||
SYS_OBJECT_ID=".7512.32"
|
||||
|
||||
def baseconfig(self):
|
||||
|
||||
self.insmod('optoe')
|
||||
self.insmod("ym2651y")
|
||||
self.insmod("accton_i2c_cpld")
|
||||
self.insmod_platform()
|
||||
for m in [ 'cpld', 'fan', 'psu', 'leds' ]:
|
||||
self.insmod("x86-64-accton-as7512-32x-%s" % m)
|
||||
|
||||
########### initialize I2C bus 0 ###########
|
||||
# initialize multiplexer (PCA9548)
|
||||
@@ -31,9 +31,9 @@ class OnlPlatform_x86_64_accton_as7512_32x_r0(OnlPlatformAccton,
|
||||
# initialize CPLD
|
||||
self.new_i2c_devices(
|
||||
[
|
||||
('accton_i2c_cpld', 0x60, 4),
|
||||
('accton_i2c_cpld', 0x62, 5),
|
||||
('accton_i2c_cpld', 0x64, 6),
|
||||
('as7512_32x_cpld1', 0x60, 4),
|
||||
('as7512_32x_cpld2', 0x62, 5),
|
||||
('as7512_32x_cpld3', 0x64, 6),
|
||||
]
|
||||
)
|
||||
########### initialize I2C bus 1 ###########
|
||||
@@ -65,7 +65,9 @@ class OnlPlatform_x86_64_accton_as7512_32x_r0(OnlPlatformAccton,
|
||||
)
|
||||
|
||||
# initialize QSFP port 1~32
|
||||
for p in range(1,33):
|
||||
self.new_i2c_device('as7512_32x_sfp%d' % p, 0x50, 17+p)
|
||||
# initialize QSFP devices
|
||||
for port in range(1, 33):
|
||||
self.new_i2c_device('optoe1', 0x50, port+17)
|
||||
subprocess.call('echo port%d > /sys/bus/i2c/devices/%d-0050/port_name' % (port, port+17), shell=True)
|
||||
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user