as5812-54t: modify onlp driver (from Brandon)

This commit is contained in:
Lewis Kang
2018-05-18 15:49:21 +08:00
parent 5fae078158
commit d2b19b32be
4 changed files with 477 additions and 39 deletions

View File

@@ -0,0 +1,464 @@
/*
* A hwmon driver for the as5812_54t_cpld
*
* Copyright (C) 2013 Accton Technology Corporation.
* Brandon Chuang <brandon_chuang@accton.com.tw>
*
* Based on ad7414.c
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/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>
#include <linux/delay.h>
#include <linux/list.h>
static LIST_HEAD(cpld_client_list);
static struct mutex list_lock;
struct cpld_client_node {
struct i2c_client *client;
struct list_head list;
};
#define I2C_RW_RETRY_COUNT 10
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
static ssize_t show_present(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 as5812_54t_cpld_read_internal(struct i2c_client *client, u8 reg);
static int as5812_54t_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value);
struct as5812_54t_cpld_data {
struct device *hwmon_dev;
struct mutex update_lock;
};
/* Addresses scanned for as5812_54t_cpld
*/
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index
enum as5812_54t_cpld_sysfs_attributes {
CPLD_VERSION,
ACCESS,
MODULE_PRESENT_ALL,
/* transceiver attributes */
TRANSCEIVER_PRESENT_ATTR_ID(49),
TRANSCEIVER_PRESENT_ATTR_ID(50),
TRANSCEIVER_PRESENT_ATTR_ID(51),
TRANSCEIVER_PRESENT_ATTR_ID(52),
TRANSCEIVER_PRESENT_ATTR_ID(53),
TRANSCEIVER_PRESENT_ATTR_ID(54),
};
/* sysfs attributes for hwmon
*/
/* transceiver attributes */
#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \
static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_present, NULL, MODULE_PRESENT_##index)
#define DECLARE_TRANSCEIVER_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_SENSOR_DEVICE_ATTR(49);
DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(50);
DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(51);
DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(52);
DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(53);
DECLARE_TRANSCEIVER_SENSOR_DEVICE_ATTR(54);
static struct attribute *as5812_54t_cpld_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_ATTR(49),
DECLARE_TRANSCEIVER_ATTR(50),
DECLARE_TRANSCEIVER_ATTR(51),
DECLARE_TRANSCEIVER_ATTR(52),
DECLARE_TRANSCEIVER_ATTR(53),
DECLARE_TRANSCEIVER_ATTR(54),
NULL
};
static const struct attribute_group as5812_54t_cpld_group = {
.attrs = as5812_54t_cpld_attributes,
};
static ssize_t show_present_all(struct device *dev, struct device_attribute *da,
char *buf)
{
int status;
u8 value = 0;
u8 reg = 0x22;
struct i2c_client *client = to_i2c_client(dev);
struct as5812_54t_cpld_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
status = as5812_54t_cpld_read_internal(client, reg);
if (status < 0) {
goto exit;
}
value = ~(u8)status;
value &= 0x3F;
mutex_unlock(&data->update_lock);
/* Return values 49 -> 54 in order */
return sprintf(buf, "%.2x\n", value);
exit:
mutex_unlock(&data->update_lock);
return status;
}
static ssize_t show_present(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 as5812_54t_cpld_data *data = i2c_get_clientdata(client);
int status = 0;
u8 reg = 0, mask = 0;
reg = 0x22;
mask = 0x1 << (attr->index - MODULE_PRESENT_49);
mutex_lock(&data->update_lock);
status = as5812_54t_cpld_read_internal(client, reg);
if (unlikely(status < 0)) {
goto exit;
}
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", !(status & mask));
exit:
mutex_unlock(&data->update_lock);
return status;
}
static ssize_t show_version(struct device *dev, struct device_attribute *da,
char *buf)
{
u8 reg = 0, mask = 0;
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct as5812_54t_cpld_data *data = i2c_get_clientdata(client);
int status = 0;
switch (attr->index) {
case CPLD_VERSION:
reg = 0x1;
mask = 0xFF;
break;
default:
break;
}
mutex_lock(&data->update_lock);
status = as5812_54t_cpld_read_internal(client, reg);
if (unlikely(status < 0)) {
goto exit;
}
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", (status & mask));
exit:
mutex_unlock(&data->update_lock);
return status;
}
static ssize_t access(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int status;
u32 addr, val;
struct i2c_client *client = to_i2c_client(dev);
struct as5812_54t_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 = as5812_54t_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 int as5812_54t_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 as5812_54t_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;
}
static void as5812_54t_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 as5812_54t_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 int as5812_54t_cpld_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int status;
struct as5812_54t_cpld_data *data = NULL;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_dbg(&client->dev, "i2c_check_functionality failed (0x%x)\n", client->addr);
status = -EIO;
goto exit;
}
data = kzalloc(sizeof(struct as5812_54t_cpld_data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto exit;
}
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
dev_info(&client->dev, "chip found\n");
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &as5812_54t_cpld_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;
}
as5812_54t_cpld_add_client(client);
/*
* Bring QSFPs out of reset,
* This is a temporary fix until the QSFP+_MOD_RST register
* can be exposed through the driver.
*/
as5812_54t_cpld_write_internal(client, 0x23, 0x3F);
dev_info(&client->dev, "%s: cpld '%s'\n",
dev_name(data->hwmon_dev), client->name);
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &as5812_54t_cpld_group);
exit_free:
kfree(data);
exit:
return status;
}
static int as5812_54t_cpld_remove(struct i2c_client *client)
{
struct as5812_54t_cpld_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &as5812_54t_cpld_group);
kfree(data);
as5812_54t_cpld_remove_client(client);
return 0;
}
int as5812_54t_cpld_read(unsigned short cpld_addr, u8 reg)
{
struct list_head *list_node = NULL;
struct cpld_client_node *cpld_node = NULL;
int ret = -EPERM;
mutex_lock(&list_lock);
list_for_each(list_node, &cpld_client_list)
{
cpld_node = list_entry(list_node, struct cpld_client_node, list);
if (cpld_node->client->addr == cpld_addr) {
ret = i2c_smbus_read_byte_data(cpld_node->client, reg);
break;
}
}
mutex_unlock(&list_lock);
return ret;
}
EXPORT_SYMBOL(as5812_54t_cpld_read);
int as5812_54t_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
{
struct list_head *list_node = NULL;
struct cpld_client_node *cpld_node = NULL;
int ret = -EIO;
mutex_lock(&list_lock);
list_for_each(list_node, &cpld_client_list)
{
cpld_node = list_entry(list_node, struct cpld_client_node, list);
if (cpld_node->client->addr == cpld_addr) {
ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value);
break;
}
}
mutex_unlock(&list_lock);
return ret;
}
EXPORT_SYMBOL(as5812_54t_cpld_write);
static const struct i2c_device_id as5812_54t_cpld_id[] = {
{ "as5812_54t_cpld", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, as5812_54t_cpld_id);
static struct i2c_driver as5812_54t_cpld_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "as5812_54t_cpld",
},
.probe = as5812_54t_cpld_probe,
.remove = as5812_54t_cpld_remove,
.id_table = as5812_54t_cpld_id,
.address_list = normal_i2c,
};
static int __init as5812_54t_cpld_init(void)
{
mutex_init(&list_lock);
return i2c_add_driver(&as5812_54t_cpld_driver);
}
static void __exit as5812_54t_cpld_exit(void)
{
i2c_del_driver(&as5812_54t_cpld_driver);
}
module_init(as5812_54t_cpld_init);
module_exit(as5812_54t_cpld_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as5812_54t_cpld driver");
MODULE_LICENSE("GPL");

View File

@@ -131,8 +131,8 @@ static ssize_t fan_set_duty_cycle(struct device *dev,
static ssize_t fan_show_value(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);
extern int as5812_54t_cpld_read(unsigned short cpld_addr, u8 reg);
extern int as5812_54t_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
/*******************/
@@ -258,12 +258,12 @@ static const struct attribute_group accton_as5812_54t_fan_group = {
static int accton_as5812_54t_fan_read_value(u8 reg)
{
return accton_i2c_cpld_read(0x60, reg);
return as5812_54t_cpld_read(0x60, reg);
}
static int accton_as5812_54t_fan_write_value(u8 reg, u8 value)
{
return accton_i2c_cpld_write(0x60, reg, value);
return as5812_54t_cpld_write(0x60, reg, value);
}
static void accton_as5812_54t_fan_update_device(struct device *dev)
@@ -394,12 +394,7 @@ static int __init accton_as5812_54t_fan_init(void)
{
int ret;
extern int platform_accton_as5812_54t(void);
if (!platform_accton_as5812_54t()) {
return -ENODEV;
}
ret = platform_driver_register(&accton_as5812_54t_fan_driver);
ret = platform_driver_register(&accton_as5812_54t_fan_driver);
if (ret < 0) {
goto exit;
}

View File

@@ -29,8 +29,8 @@
#include <linux/leds.h>
#include <linux/slab.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 as5812_54t_cpld_read(unsigned short cpld_addr, u8 reg);
extern int as5812_54t_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
#define DRVNAME "as5812_54t_led"
@@ -218,12 +218,12 @@ static u8 led_light_mode_to_reg_val(enum led_type type,
static int accton_as5812_54t_led_read_value(u8 reg)
{
return accton_i2c_cpld_read(0x60, reg);
return as5812_54t_cpld_read(0x60, reg);
}
static int accton_as5812_54t_led_write_value(u8 reg, u8 value)
{
return accton_i2c_cpld_write(0x60, reg, value);
return as5812_54t_cpld_write(0x60, reg, value);
}
static void accton_as5812_54t_led_update(void)
@@ -550,12 +550,7 @@ static int __init accton_as5812_54t_led_init(void)
{
int ret;
extern int platform_accton_as5812_54t(void);
if (!platform_accton_as5812_54t()) {
return -ENODEV;
}
ret = platform_driver_register(&accton_as5812_54t_led_driver);
ret = platform_driver_register(&accton_as5812_54t_led_driver);
if (ret < 0) {
goto exit;
}

View File

@@ -43,7 +43,7 @@ static ssize_t show_index(struct device *dev, struct device_attribute *da, char
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 as5812_54t_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 as5812_54t_cpld_read(unsigned short cpld_addr, u8 reg);
static int as5812_54t_psu_model_name_get(struct device *dev);
/* Addresses scanned
@@ -328,7 +328,7 @@ static struct as5812_54t_psu_data *as5812_54t_psu_update_device(struct device *d
data->valid = 0;
/* Read psu status */
status = accton_i2c_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET);
status = as5812_54t_cpld_read(PSU_STATUS_I2C_ADDR, PSU_STATUS_I2C_REG_OFFSET);
if (status < 0) {
dev_dbg(&client->dev, "cpld reg (0x%x) err %d\n", PSU_STATUS_I2C_ADDR, status);
@@ -348,25 +348,9 @@ exit:
return data;
}
static int __init as5812_54t_psu_init(void)
{
extern int platform_accton_as5812_54t(void);
if (!platform_accton_as5812_54t()) {
return -ENODEV;
}
return i2c_add_driver(&as5812_54t_psu_driver);
}
static void __exit as5812_54t_psu_exit(void)
{
i2c_del_driver(&as5812_54t_psu_driver);
}
module_i2c_driver(as5812_54t_psu_driver);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("accton as5812_54t_psu driver");
MODULE_LICENSE("GPL");
module_init(as5812_54t_psu_init);
module_exit(as5812_54t_psu_exit);