From d2b19b32beaefd84a9b6d217c286c83066bf8c36 Mon Sep 17 00:00:00 2001 From: Lewis Kang Date: Fri, 18 May 2018 15:49:21 +0800 Subject: [PATCH] as5812-54t: modify onlp driver (from Brandon) --- .../builds/x86-64-accton-as5812-54t-cpld.c | 464 ++++++++++++++++++ .../builds/x86-64-accton-as5812-54t-fan.c | 15 +- .../builds/x86-64-accton-as5812-54t-leds.c | 15 +- .../builds/x86-64-accton-as5812-54t-psu.c | 22 +- 4 files changed, 477 insertions(+), 39 deletions(-) create mode 100644 packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-cpld.c diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-cpld.c b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-cpld.c new file mode 100644 index 00000000..ac4c3e06 --- /dev/null +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-cpld.c @@ -0,0 +1,464 @@ +/* + * A hwmon driver for the as5812_54t_cpld + * + * Copyright (C) 2013 Accton Technology Corporation. + * Brandon Chuang + * + * Based on ad7414.c + * Copyright 2006 Stefan Roese , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("as5812_54t_cpld driver"); +MODULE_LICENSE("GPL"); + diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-fan.c b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-fan.c index bad9245e..d6554637 100644 --- a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-fan.c +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-fan.c @@ -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; } diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-leds.c b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-leds.c index ec0c8ff0..cf00e2e3 100644 --- a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-leds.c +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-leds.c @@ -29,8 +29,8 @@ #include #include -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; } diff --git a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-psu.c b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-psu.c index a77014e8..75a2d823 100644 --- a/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-psu.c +++ b/packages/platforms/accton/x86-64/x86-64-accton-as5812-54t/modules/builds/x86-64-accton-as5812-54t-psu.c @@ -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 "); MODULE_DESCRIPTION("accton as5812_54t_psu driver"); MODULE_LICENSE("GPL"); -module_init(as5812_54t_psu_init); -module_exit(as5812_54t_psu_exit); -