AS7716 Kernel Modules.

This commit is contained in:
Jeffrey Townsend
2017-01-03 19:45:49 +00:00
parent cfdaa56ad5
commit d043d6a1e1
8 changed files with 1552 additions and 0 deletions

View File

@@ -0,0 +1 @@
include $(ONL)/make/pkg.mk

View File

@@ -0,0 +1 @@
!include $ONL_TEMPLATES/platform-modules.yml PLATFORM=x86-64-accton-as7716-32x ARCH=amd64 KERNELS="onl-kernel-3.16-lts-x86-64-all:amd64"

View File

@@ -0,0 +1,5 @@
KERNELS := onl-kernel-3.16-lts-x86-64-all:amd64 onl-kernel-3.2-deb7-x86-64-all:amd64
KMODULES := $(wildcard *.c)
PLATFORM := x86-64-accton-as7716-32x
ARCH := x86_64
include $(ONL)/make/kmodule.mk

View File

@@ -0,0 +1,452 @@
/*
* A hwmon driver for the Accton as7716 32x fan
*
* Copyright (C) 2014 Accton Technology Corporation.
* Brandon Chuang <brandon_chuang@accton.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#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/dmi.h>
#define DRVNAME "as7716_32x_fan"
static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev);
static ssize_t fan_show_value(struct device *dev, struct device_attribute *da, char *buf);
static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da,
const char *buf, size_t count);
extern int 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
*/
static const u8 fan_reg[] = {
0x0F, /* fan 1-6 present status */
0x11, /* fan PWM(for all fan) */
0x12, /* front fan 1 speed(rpm) */
0x13, /* front fan 2 speed(rpm) */
0x14, /* front fan 3 speed(rpm) */
0x15, /* front fan 4 speed(rpm) */
0x16, /* front fan 5 speed(rpm) */
0x17, /* front fan 6 speed(rpm) */
0x22, /* rear fan 1 speed(rpm) */
0x23, /* rear fan 2 speed(rpm) */
0x24, /* rear fan 3 speed(rpm) */
0x25, /* rear fan 4 speed(rpm) */
0x26, /* rear fan 5 speed(rpm) */
0x27, /* rear fan 6 speed(rpm) */
};
/* Each client has this additional data */
struct as7716_32x_fan_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid; /* != 0 if registers are valid */
unsigned long last_updated; /* In jiffies */
u8 reg_val[ARRAY_SIZE(fan_reg)]; /* Register value */
};
enum fan_id {
FAN1_ID,
FAN2_ID,
FAN3_ID,
FAN4_ID,
FAN5_ID,
FAN6_ID
};
enum sysfs_fan_attributes {
FAN_PRESENT_REG,
FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */
FAN1_FRONT_SPEED_RPM,
FAN2_FRONT_SPEED_RPM,
FAN3_FRONT_SPEED_RPM,
FAN4_FRONT_SPEED_RPM,
FAN5_FRONT_SPEED_RPM,
FAN6_FRONT_SPEED_RPM,
FAN1_REAR_SPEED_RPM,
FAN2_REAR_SPEED_RPM,
FAN3_REAR_SPEED_RPM,
FAN4_REAR_SPEED_RPM,
FAN5_REAR_SPEED_RPM,
FAN6_REAR_SPEED_RPM,
FAN1_PRESENT,
FAN2_PRESENT,
FAN3_PRESENT,
FAN4_PRESENT,
FAN5_PRESENT,
FAN6_PRESENT,
FAN1_FAULT,
FAN2_FAULT,
FAN3_FAULT,
FAN4_FAULT,
FAN5_FAULT,
FAN6_FAULT
};
/* Define attributes
*/
#define DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, fan_show_value, NULL, FAN##index##_FAULT)
#define DECLARE_FAN_FAULT_ATTR(index) &sensor_dev_attr_fan##index##_fault.dev_attr.attr
#define DECLARE_FAN_DIRECTION_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_direction, S_IRUGO, fan_show_value, NULL, FAN##index##_DIRECTION)
#define DECLARE_FAN_DIRECTION_ATTR(index) &sensor_dev_attr_fan##index##_direction.dev_attr.attr
#define DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_duty_cycle_percentage, S_IWUSR | S_IRUGO, fan_show_value, set_duty_cycle, FAN##index##_DUTY_CYCLE_PERCENTAGE)
#define DECLARE_FAN_DUTY_CYCLE_ATTR(index) &sensor_dev_attr_fan##index##_duty_cycle_percentage.dev_attr.attr
#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, fan_show_value, NULL, FAN##index##_PRESENT)
#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr
#define DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_front_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_FRONT_SPEED_RPM);\
static SENSOR_DEVICE_ATTR(fan##index##_rear_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_REAR_SPEED_RPM)
#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_front_speed_rpm.dev_attr.attr, \
&sensor_dev_attr_fan##index##_rear_speed_rpm.dev_attr.attr
/* 6 fan fault attributes in this platform */
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(3);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(4);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(5);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(6);
/* 6 fan speed(rpm) attributes in this platform */
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(3);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(4);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(5);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(6);
/* 6 fan present attributes in this platform */
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1);
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2);
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3);
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4);
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(5);
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(6);
/* 1 fan duty cycle attribute in this platform */
DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR();
static struct attribute *as7716_32x_fan_attributes[] = {
/* fan related attributes */
DECLARE_FAN_FAULT_ATTR(1),
DECLARE_FAN_FAULT_ATTR(2),
DECLARE_FAN_FAULT_ATTR(3),
DECLARE_FAN_FAULT_ATTR(4),
DECLARE_FAN_FAULT_ATTR(5),
DECLARE_FAN_FAULT_ATTR(6),
DECLARE_FAN_SPEED_RPM_ATTR(1),
DECLARE_FAN_SPEED_RPM_ATTR(2),
DECLARE_FAN_SPEED_RPM_ATTR(3),
DECLARE_FAN_SPEED_RPM_ATTR(4),
DECLARE_FAN_SPEED_RPM_ATTR(5),
DECLARE_FAN_SPEED_RPM_ATTR(6),
DECLARE_FAN_PRESENT_ATTR(1),
DECLARE_FAN_PRESENT_ATTR(2),
DECLARE_FAN_PRESENT_ATTR(3),
DECLARE_FAN_PRESENT_ATTR(4),
DECLARE_FAN_PRESENT_ATTR(5),
DECLARE_FAN_PRESENT_ATTR(6),
DECLARE_FAN_DUTY_CYCLE_ATTR(),
NULL
};
#define FAN_DUTY_CYCLE_REG_MASK 0xF
#define FAN_MAX_DUTY_CYCLE 100
#define FAN_REG_VAL_TO_SPEED_RPM_STEP 100
static int as7716_32x_fan_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int as7716_32x_fan_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/* fan utility functions
*/
static u32 reg_val_to_duty_cycle(u8 reg_val)
{
reg_val &= FAN_DUTY_CYCLE_REG_MASK;
return ((u32)(reg_val+1) * 625 + 75)/ 100;
}
static u8 duty_cycle_to_reg_val(u8 duty_cycle)
{
return ((u32)duty_cycle * 100 / 625) - 1;
}
static u32 reg_val_to_speed_rpm(u8 reg_val)
{
return (u32)reg_val * FAN_REG_VAL_TO_SPEED_RPM_STEP;
}
static u8 reg_val_to_is_present(u8 reg_val, enum fan_id id)
{
u8 mask = (1 << id);
reg_val &= mask;
return reg_val ? 0 : 1;
}
static u8 is_fan_fault(struct as7716_32x_fan_data *data, enum fan_id id)
{
u8 ret = 1;
int front_fan_index = FAN1_FRONT_SPEED_RPM + id;
int rear_fan_index = FAN1_REAR_SPEED_RPM + id;
/* Check if the speed of front or rear fan is ZERO,
*/
if (reg_val_to_speed_rpm(data->reg_val[front_fan_index]) &&
reg_val_to_speed_rpm(data->reg_val[rear_fan_index])) {
ret = 0;
}
return ret;
}
static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int error, value;
struct i2c_client *client = to_i2c_client(dev);
error = kstrtoint(buf, 10, &value);
if (error)
return error;
if (value < 0 || value > FAN_MAX_DUTY_CYCLE)
return -EINVAL;
as7716_32x_fan_write_value(client, 0x33, 0); /* Disable fan speed watch dog */
as7716_32x_fan_write_value(client, fan_reg[FAN_DUTY_CYCLE_PERCENTAGE], duty_cycle_to_reg_val(value));
return count;
}
static ssize_t fan_show_value(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct as7716_32x_fan_data *data = as7716_32x_fan_update_device(dev);
ssize_t ret = 0;
if (data->valid) {
switch (attr->index) {
case FAN_DUTY_CYCLE_PERCENTAGE:
{
u32 duty_cycle = reg_val_to_duty_cycle(data->reg_val[FAN_DUTY_CYCLE_PERCENTAGE]);
ret = sprintf(buf, "%u\n", duty_cycle);
break;
}
case FAN1_FRONT_SPEED_RPM:
case FAN2_FRONT_SPEED_RPM:
case FAN3_FRONT_SPEED_RPM:
case FAN4_FRONT_SPEED_RPM:
case FAN5_FRONT_SPEED_RPM:
case FAN6_FRONT_SPEED_RPM:
case FAN1_REAR_SPEED_RPM:
case FAN2_REAR_SPEED_RPM:
case FAN3_REAR_SPEED_RPM:
case FAN4_REAR_SPEED_RPM:
case FAN5_REAR_SPEED_RPM:
case FAN6_REAR_SPEED_RPM:
ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index]));
break;
case FAN1_PRESENT:
case FAN2_PRESENT:
case FAN3_PRESENT:
case FAN4_PRESENT:
case FAN5_PRESENT:
case FAN6_PRESENT:
ret = sprintf(buf, "%d\n",
reg_val_to_is_present(data->reg_val[FAN_PRESENT_REG],
attr->index - FAN1_PRESENT));
break;
case FAN1_FAULT:
case FAN2_FAULT:
case FAN3_FAULT:
case FAN4_FAULT:
case FAN5_FAULT:
case FAN6_FAULT:
ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT));
break;
default:
break;
}
}
return ret;
}
static const struct attribute_group as7716_32x_fan_group = {
.attrs = as7716_32x_fan_attributes,
};
static struct as7716_32x_fan_data *as7716_32x_fan_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct as7716_32x_fan_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2) ||
!data->valid) {
int i;
dev_dbg(&client->dev, "Starting as7716_32x_fan update\n");
data->valid = 0;
/* Update fan data
*/
for (i = 0; i < ARRAY_SIZE(data->reg_val); i++) {
int status = as7716_32x_fan_read_value(client, fan_reg[i]);
if (status < 0) {
data->valid = 0;
mutex_unlock(&data->update_lock);
dev_dbg(&client->dev, "reg %d, err %d\n", fan_reg[i], status);
return data;
}
else {
data->reg_val[i] = status;
}
}
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static int as7716_32x_fan_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct as7716_32x_fan_data *data;
int status;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
status = -EIO;
goto exit;
}
data = kzalloc(sizeof(struct as7716_32x_fan_data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto exit;
}
i2c_set_clientdata(client, data);
data->valid = 0;
mutex_init(&data->update_lock);
dev_info(&client->dev, "chip found\n");
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &as7716_32x_fan_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: fan '%s'\n",
dev_name(data->hwmon_dev), client->name);
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group);
exit_free:
kfree(data);
exit:
return status;
}
static int as7716_32x_fan_remove(struct i2c_client *client)
{
struct as7716_32x_fan_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &as7716_32x_fan_group);
return 0;
}
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x66, I2C_CLIENT_END };
static const struct i2c_device_id as7716_32x_fan_id[] = {
{ "as7716_32x_fan", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, as7716_32x_fan_id);
static struct i2c_driver as7716_32x_fan_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = DRVNAME,
},
.probe = as7716_32x_fan_probe,
.remove = as7716_32x_fan_remove,
.id_table = as7716_32x_fan_id,
.address_list = normal_i2c,
};
static int __init as7716_32x_fan_init(void)
{
extern int platform_accton_as7716_32x(void);
if (!platform_accton_as7716_32x()) {
return -ENODEV;
}
return i2c_add_driver(&as7716_32x_fan_driver);
}
static void __exit as7716_32x_fan_exit(void)
{
i2c_del_driver(&as7716_32x_fan_driver);
}
module_init(as7716_32x_fan_init);
module_exit(as7716_32x_fan_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as7716_32x_fan driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,443 @@
/*
* A LED driver for the accton_as7716_32x_led
*
* Copyright (C) 2014 Accton Technology Corporation.
* Brandon Chuang <brandon_chuang@accton.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*#define DEBUG*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/leds.h>
#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 void led_classdev_unregister(struct led_classdev *led_cdev);
extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
extern void led_classdev_resume(struct led_classdev *led_cdev);
extern void led_classdev_suspend(struct led_classdev *led_cdev);
#define DRVNAME "accton_as7716_32x_led"
struct accton_as7716_32x_led_data {
struct platform_device *pdev;
struct mutex update_lock;
char valid; /* != 0 if registers are valid */
unsigned long last_updated; /* In jiffies */
u8 reg_val[1]; /* only 1 register*/
};
static struct accton_as7716_32x_led_data *ledctl = NULL;
/* LED related data
*/
#define LED_CNTRLER_I2C_ADDRESS (0x60)
#define LED_TYPE_DIAG_REG_MASK (0x3)
#define LED_MODE_DIAG_GREEN_VALUE (0x02)
#define LED_MODE_DIAG_RED_VALUE (0x01)
#define LED_MODE_DIAG_AMBER_VALUE (0x00) /*It's yellow actually. Green+Red=Yellow*/
#define LED_MODE_DIAG_OFF_VALUE (0x03)
#define LED_TYPE_LOC_REG_MASK (0x80)
#define LED_MODE_LOC_ON_VALUE (0)
#define LED_MODE_LOC_OFF_VALUE (0x80)
enum led_type {
LED_TYPE_DIAG,
LED_TYPE_LOC,
LED_TYPE_FAN,
LED_TYPE_PSU1,
LED_TYPE_PSU2
};
struct led_reg {
u32 types;
u8 reg_addr;
};
static const struct led_reg led_reg_map[] = {
{(1<<LED_TYPE_LOC) | (1<<LED_TYPE_DIAG), 0x41},
};
enum led_light_mode {
LED_MODE_OFF = 0,
LED_MODE_GREEN,
LED_MODE_AMBER,
LED_MODE_RED,
LED_MODE_BLUE,
LED_MODE_GREEN_BLINK,
LED_MODE_AMBER_BLINK,
LED_MODE_RED_BLINK,
LED_MODE_BLUE_BLINK,
LED_MODE_AUTO,
LED_MODE_UNKNOWN
};
struct led_type_mode {
enum led_type type;
enum led_light_mode mode;
int reg_bit_mask;
int mode_value;
};
static struct led_type_mode led_type_mode_data[] = {
{LED_TYPE_LOC, LED_MODE_OFF, LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_OFF_VALUE},
{LED_TYPE_LOC, LED_MODE_BLUE, LED_TYPE_LOC_REG_MASK, LED_MODE_LOC_ON_VALUE},
{LED_TYPE_DIAG, LED_MODE_OFF, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_OFF_VALUE},
{LED_TYPE_DIAG, LED_MODE_GREEN, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_GREEN_VALUE},
{LED_TYPE_DIAG, LED_MODE_RED, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_RED_VALUE},
{LED_TYPE_DIAG, LED_MODE_AMBER, LED_TYPE_DIAG_REG_MASK, LED_MODE_DIAG_AMBER_VALUE},
};
static void accton_as7716_32x_led_set(struct led_classdev *led_cdev,
enum led_brightness led_light_mode, enum led_type type);
static int accton_getLedReg(enum led_type type, u8 *reg)
{
int i;
for (i = 0; i < ARRAY_SIZE(led_reg_map); i++) {
if(led_reg_map[i].types & (type<<1)){
*reg = led_reg_map[i].reg_addr;
return 0;
}
}
return 1;
}
static int led_reg_val_to_light_mode(enum led_type type, u8 reg_val) {
int i;
for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) {
if (type != led_type_mode_data[i].type)
continue;
if ((led_type_mode_data[i].reg_bit_mask & reg_val) ==
led_type_mode_data[i].mode_value)
{
return led_type_mode_data[i].mode;
}
}
return 0;
}
static u8 led_light_mode_to_reg_val(enum led_type type,
enum led_light_mode mode, u8 reg_val) {
int i;
for (i = 0; i < ARRAY_SIZE(led_type_mode_data); i++) {
if (type != led_type_mode_data[i].type)
continue;
if (mode != led_type_mode_data[i].mode)
continue;
reg_val = led_type_mode_data[i].mode_value |
(reg_val & (~led_type_mode_data[i].reg_bit_mask));
break;
}
return reg_val;
}
static int accton_as7716_32x_led_read_value(u8 reg)
{
return accton_i2c_cpld_read(LED_CNTRLER_I2C_ADDRESS, reg);
}
static int accton_as7716_32x_led_write_value(u8 reg, u8 value)
{
return accton_i2c_cpld_write(LED_CNTRLER_I2C_ADDRESS, reg, value);
}
static void accton_as7716_32x_led_update(void)
{
mutex_lock(&ledctl->update_lock);
if (time_after(jiffies, ledctl->last_updated + HZ + HZ / 2)
|| !ledctl->valid) {
int i;
dev_dbg(&ledctl->pdev->dev, "Starting accton_as7716_32x_led update\n");
/* Update LED data
*/
for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) {
int status = accton_as7716_32x_led_read_value(led_reg_map[i].reg_addr);
if (status < 0) {
ledctl->valid = 0;
dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg_map[i].reg_addr, status);
goto exit;
}
else
{
ledctl->reg_val[i] = status;
}
}
ledctl->last_updated = jiffies;
ledctl->valid = 1;
}
exit:
mutex_unlock(&ledctl->update_lock);
}
static void accton_as7716_32x_led_set(struct led_classdev *led_cdev,
enum led_brightness led_light_mode,
enum led_type type)
{
int reg_val;
u8 reg ;
mutex_lock(&ledctl->update_lock);
if( !accton_getLedReg(type, &reg))
{
dev_dbg(&ledctl->pdev->dev, "Not match item for %d.\n", type);
}
reg_val = accton_as7716_32x_led_read_value(reg);
if (reg_val < 0) {
dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", reg, reg_val);
goto exit;
}
reg_val = led_light_mode_to_reg_val(type, led_light_mode, reg_val);
accton_as7716_32x_led_write_value(reg, reg_val);
/* to prevent the slow-update issue */
ledctl->valid = 0;
exit:
mutex_unlock(&ledctl->update_lock);
}
static void accton_as7716_32x_led_diag_set(struct led_classdev *led_cdev,
enum led_brightness led_light_mode)
{
accton_as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_DIAG);
}
static enum led_brightness accton_as7716_32x_led_diag_get(struct led_classdev *cdev)
{
accton_as7716_32x_led_update();
return led_reg_val_to_light_mode(LED_TYPE_DIAG, ledctl->reg_val[0]);
}
static void accton_as7716_32x_led_loc_set(struct led_classdev *led_cdev,
enum led_brightness led_light_mode)
{
accton_as7716_32x_led_set(led_cdev, led_light_mode, LED_TYPE_LOC);
}
static enum led_brightness accton_as7716_32x_led_loc_get(struct led_classdev *cdev)
{
accton_as7716_32x_led_update();
return led_reg_val_to_light_mode(LED_TYPE_LOC, ledctl->reg_val[0]);
}
static void accton_as7716_32x_led_auto_set(struct led_classdev *led_cdev,
enum led_brightness led_light_mode)
{
}
static enum led_brightness accton_as7716_32x_led_auto_get(struct led_classdev *cdev)
{
return LED_MODE_AUTO;
}
static struct led_classdev accton_as7716_32x_leds[] = {
[LED_TYPE_DIAG] = {
.name = "accton_as7716_32x_led::diag",
.default_trigger = "unused",
.brightness_set = accton_as7716_32x_led_diag_set,
.brightness_get = accton_as7716_32x_led_diag_get,
.flags = LED_CORE_SUSPENDRESUME,
.max_brightness = LED_MODE_RED,
},
[LED_TYPE_LOC] = {
.name = "accton_as7716_32x_led::loc",
.default_trigger = "unused",
.brightness_set = accton_as7716_32x_led_loc_set,
.brightness_get = accton_as7716_32x_led_loc_get,
.flags = LED_CORE_SUSPENDRESUME,
.max_brightness = LED_MODE_BLUE,
},
[LED_TYPE_FAN] = {
.name = "accton_as7716_32x_led::fan",
.default_trigger = "unused",
.brightness_set = accton_as7716_32x_led_auto_set,
.brightness_get = accton_as7716_32x_led_auto_get,
.flags = LED_CORE_SUSPENDRESUME,
.max_brightness = LED_MODE_AUTO,
},
[LED_TYPE_PSU1] = {
.name = "accton_as7716_32x_led::psu1",
.default_trigger = "unused",
.brightness_set = accton_as7716_32x_led_auto_set,
.brightness_get = accton_as7716_32x_led_auto_get,
.flags = LED_CORE_SUSPENDRESUME,
.max_brightness = LED_MODE_AUTO,
},
[LED_TYPE_PSU2] = {
.name = "accton_as7716_32x_led::psu2",
.default_trigger = "unused",
.brightness_set = accton_as7716_32x_led_auto_set,
.brightness_get = accton_as7716_32x_led_auto_get,
.flags = LED_CORE_SUSPENDRESUME,
.max_brightness = LED_MODE_AUTO,
},
};
static int accton_as7716_32x_led_suspend(struct platform_device *dev,
pm_message_t state)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) {
led_classdev_suspend(&accton_as7716_32x_leds[i]);
}
return 0;
}
static int accton_as7716_32x_led_resume(struct platform_device *dev)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) {
led_classdev_resume(&accton_as7716_32x_leds[i]);
}
return 0;
}
static int accton_as7716_32x_led_probe(struct platform_device *pdev)
{
int ret, i;
for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) {
ret = led_classdev_register(&pdev->dev, &accton_as7716_32x_leds[i]);
if (ret < 0)
break;
}
/* Check if all LEDs were successfully registered */
if (i != ARRAY_SIZE(accton_as7716_32x_leds)){
int j;
/* only unregister the LEDs that were successfully registered */
for (j = 0; j < i; j++) {
led_classdev_unregister(&accton_as7716_32x_leds[i]);
}
}
return ret;
}
static int accton_as7716_32x_led_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(accton_as7716_32x_leds); i++) {
led_classdev_unregister(&accton_as7716_32x_leds[i]);
}
return 0;
}
static struct platform_driver accton_as7716_32x_led_driver = {
.probe = accton_as7716_32x_led_probe,
.remove = accton_as7716_32x_led_remove,
.suspend = accton_as7716_32x_led_suspend,
.resume = accton_as7716_32x_led_resume,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
};
static int __init accton_as7716_32x_led_init(void)
{
int ret;
extern int platform_accton_as7716_32x(void);
if (!platform_accton_as7716_32x()) {
return -ENODEV;
}
ret = platform_driver_register(&accton_as7716_32x_led_driver);
if (ret < 0) {
goto exit;
}
ledctl = kzalloc(sizeof(struct accton_as7716_32x_led_data), GFP_KERNEL);
if (!ledctl) {
ret = -ENOMEM;
platform_driver_unregister(&accton_as7716_32x_led_driver);
goto exit;
}
mutex_init(&ledctl->update_lock);
ledctl->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
if (IS_ERR(ledctl->pdev)) {
ret = PTR_ERR(ledctl->pdev);
platform_driver_unregister(&accton_as7716_32x_led_driver);
kfree(ledctl);
goto exit;
}
exit:
return ret;
}
static void __exit accton_as7716_32x_led_exit(void)
{
platform_device_unregister(ledctl->pdev);
platform_driver_unregister(&accton_as7716_32x_led_driver);
kfree(ledctl);
}
module_init(accton_as7716_32x_led_init);
module_exit(accton_as7716_32x_led_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("accton_as7716_32x_led driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,293 @@
/*
* An hwmon driver for accton as7716_32x Power Module
*
* 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>
#include <linux/delay.h>
#include <linux/dmi.h>
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 as7716_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);
/* Addresses scanned
*/
static const unsigned short normal_i2c[] = { 0x50, 0x53, I2C_CLIENT_END };
/* Each client has this additional data
*/
struct as7716_32x_psu_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid; /* !=0 if registers are valid */
unsigned long last_updated; /* In jiffies */
u8 index; /* PSU index */
u8 status; /* Status(present/power_good) register read from CPLD */
char model_name[9]; /* Model name, read from eeprom */
};
static struct as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev);
enum as7716_32x_psu_sysfs_attributes {
PSU_PRESENT,
PSU_MODEL_NAME,
PSU_POWER_GOOD
};
/* sysfs attributes for hwmon
*/
static SENSOR_DEVICE_ATTR(psu_present, S_IRUGO, show_status, NULL, PSU_PRESENT);
static SENSOR_DEVICE_ATTR(psu_model_name, S_IRUGO, show_model_name,NULL, PSU_MODEL_NAME);
static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_status, NULL, PSU_POWER_GOOD);
static struct attribute *as7716_32x_psu_attributes[] = {
&sensor_dev_attr_psu_present.dev_attr.attr,
&sensor_dev_attr_psu_model_name.dev_attr.attr,
&sensor_dev_attr_psu_power_good.dev_attr.attr,
NULL
};
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 as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev);
u8 status = 0;
if (attr->index == PSU_PRESENT) {
status = !(data->status >> (1-data->index) & 0x1);
}
else { /* PSU_POWER_GOOD */
status = (data->status >> (3-data->index) & 0x1);
}
return sprintf(buf, "%d\n", status);
}
static ssize_t show_model_name(struct device *dev, struct device_attribute *da,
char *buf)
{
struct as7716_32x_psu_data *data = as7716_32x_psu_update_device(dev);
return sprintf(buf, "%s\n", data->model_name);
}
static const struct attribute_group as7716_32x_psu_group = {
.attrs = as7716_32x_psu_attributes,
};
static int as7716_32x_psu_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct as7716_32x_psu_data *data;
int status;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
status = -EIO;
goto exit;
}
data = kzalloc(sizeof(struct as7716_32x_psu_data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto exit;
}
i2c_set_clientdata(client, data);
data->valid = 0;
data->index = dev_id->driver_data;
mutex_init(&data->update_lock);
dev_info(&client->dev, "chip found\n");
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &as7716_32x_psu_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: psu '%s'\n",
dev_name(data->hwmon_dev), client->name);
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &as7716_32x_psu_group);
exit_free:
kfree(data);
exit:
return status;
}
static int as7716_32x_psu_remove(struct i2c_client *client)
{
struct as7716_32x_psu_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &as7716_32x_psu_group);
kfree(data);
return 0;
}
enum psu_index
{
as7716_32x_psu1,
as7716_32x_psu2
};
static const struct i2c_device_id as7716_32x_psu_id[] = {
{ "as7716_32x_psu1", as7716_32x_psu1 },
{ "as7716_32x_psu2", as7716_32x_psu2 },
{}
};
MODULE_DEVICE_TABLE(i2c, as7716_32x_psu_id);
static struct i2c_driver as7716_32x_psu_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "as7716_32x_psu",
},
.probe = as7716_32x_psu_probe,
.remove = as7716_32x_psu_remove,
.id_table = as7716_32x_psu_id,
.address_list = normal_i2c,
};
static int as7716_32x_psu_read_block(struct i2c_client *client, u8 command, u8 *data,
int data_len)
{
int result = 0;
int retry_count = 5;
while (retry_count) {
retry_count--;
result = i2c_smbus_read_i2c_block_data(client, command, data_len, data);
if (unlikely(result < 0)) {
msleep(10);
continue;
}
if (unlikely(result != data_len)) {
result = -EIO;
msleep(10);
continue;
}
result = 0;
break;
}
return result;
}
static struct as7716_32x_psu_data *as7716_32x_psu_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct as7716_32x_psu_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;
int power_good = 0;
dev_dbg(&client->dev, "Starting as7716_32x update\n");
/* Read psu status */
status = accton_i2c_cpld_read(0x60, 0x2);
if (status < 0) {
dev_dbg(&client->dev, "cpld reg 0x60 err %d\n", status);
}
else {
data->status = status;
}
/* Read model name */
memset(data->model_name, 0, sizeof(data->model_name));
power_good = (data->status >> (3-data->index) & 0x1);
if (power_good) {
status = as7716_32x_psu_read_block(client, 0x20, data->model_name,
ARRAY_SIZE(data->model_name)-1);
if (status < 0) {
data->model_name[0] = '\0';
dev_dbg(&client->dev, "unable to read model name from (0x%x)\n", client->addr);
}
else {
data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0';
}
}
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static int __init as7716_32x_psu_init(void)
{
extern int platform_accton_as7716_32x(void);
if (!platform_accton_as7716_32x()) {
return -ENODEV;
}
return i2c_add_driver(&as7716_32x_psu_driver);
}
static void __exit as7716_32x_psu_exit(void)
{
i2c_del_driver(&as7716_32x_psu_driver);
}
module_init(as7716_32x_psu_init);
module_exit(as7716_32x_psu_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as7716_32x_psu driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,356 @@
/*
* An hwmon driver for accton as7716_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 as7716_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 as7716_32x_sfp_data *as7716_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 as7716_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 *as7716_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 as7716_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 as7716_32x_sfp_data *data = as7716_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 as7716_32x_sfp_data *data = as7716_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 as7716_32x_sfp_group = {
.attrs = as7716_32x_sfp_attributes,
};
static int as7716_32x_sfp_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct as7716_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 as7716_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, &as7716_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, &as7716_32x_sfp_group);
exit_free:
kfree(data);
exit:
return status;
}
static int as7716_32x_sfp_remove(struct i2c_client *client)
{
struct as7716_32x_sfp_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &as7716_32x_sfp_group);
kfree(data);
return 0;
}
enum port_numbers {
as7716_32x_sfp1, as7716_32x_sfp2, as7716_32x_sfp3, as7716_32x_sfp4,
as7716_32x_sfp5, as7716_32x_sfp6, as7716_32x_sfp7, as7716_32x_sfp8,
as7716_32x_sfp9, as7716_32x_sfp10,as7716_32x_sfp11,as7716_32x_sfp12,
as7716_32x_sfp13,as7716_32x_sfp14,as7716_32x_sfp15,as7716_32x_sfp16,
as7716_32x_sfp17,as7716_32x_sfp18,as7716_32x_sfp19,as7716_32x_sfp20,
as7716_32x_sfp21,as7716_32x_sfp22,as7716_32x_sfp23,as7716_32x_sfp24,
as7716_32x_sfp25,as7716_32x_sfp26,as7716_32x_sfp27,as7716_32x_sfp28,
as7716_32x_sfp29,as7716_32x_sfp30,as7716_32x_sfp31,as7716_32x_sfp32
};
static const struct i2c_device_id as7716_32x_sfp_id[] = {
{ "as7716_32x_sfp1", as7716_32x_sfp1 }, { "as7716_32x_sfp2", as7716_32x_sfp2 },
{ "as7716_32x_sfp3", as7716_32x_sfp3 }, { "as7716_32x_sfp4", as7716_32x_sfp4 },
{ "as7716_32x_sfp5", as7716_32x_sfp5 }, { "as7716_32x_sfp6", as7716_32x_sfp6 },
{ "as7716_32x_sfp7", as7716_32x_sfp7 }, { "as7716_32x_sfp8", as7716_32x_sfp8 },
{ "as7716_32x_sfp9", as7716_32x_sfp9 }, { "as7716_32x_sfp10", as7716_32x_sfp10 },
{ "as7716_32x_sfp11", as7716_32x_sfp11 }, { "as7716_32x_sfp12", as7716_32x_sfp12 },
{ "as7716_32x_sfp13", as7716_32x_sfp13 }, { "as7716_32x_sfp14", as7716_32x_sfp14 },
{ "as7716_32x_sfp15", as7716_32x_sfp15 }, { "as7716_32x_sfp16", as7716_32x_sfp16 },
{ "as7716_32x_sfp17", as7716_32x_sfp17 }, { "as7716_32x_sfp18", as7716_32x_sfp18 },
{ "as7716_32x_sfp19", as7716_32x_sfp19 }, { "as7716_32x_sfp20", as7716_32x_sfp20 },
{ "as7716_32x_sfp21", as7716_32x_sfp21 }, { "as7716_32x_sfp22", as7716_32x_sfp22 },
{ "as7716_32x_sfp23", as7716_32x_sfp23 }, { "as7716_32x_sfp24", as7716_32x_sfp24 },
{ "as7716_32x_sfp25", as7716_32x_sfp25 }, { "as7716_32x_sfp26", as7716_32x_sfp26 },
{ "as7716_32x_sfp27", as7716_32x_sfp27 }, { "as7716_32x_sfp28", as7716_32x_sfp28 },
{ "as7716_32x_sfp29", as7716_32x_sfp29 }, { "as7716_32x_sfp30", as7716_32x_sfp30 },
{ "as7716_32x_sfp31", as7716_32x_sfp31 }, { "as7716_32x_sfp32", as7716_32x_sfp32 },
{}
};
MODULE_DEVICE_TABLE(i2c, as7716_32x_sfp_id);
static struct i2c_driver as7716_32x_sfp_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "as7716_32x_sfp",
},
.probe = as7716_32x_sfp_probe,
.remove = as7716_32x_sfp_remove,
.id_table = as7716_32x_sfp_id,
.address_list = normal_i2c,
};
static int as7716_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 as7716_32x_sfp_data *as7716_32x_sfp_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct as7716_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 = as7716_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 as7716_32x_sfp_init(void)
{
extern int platform_accton_as7716_32x(void);
if (!platform_accton_as7716_32x()) {
return -ENODEV;
}
return i2c_add_driver(&as7716_32x_sfp_driver);
}
static void __exit as7716_32x_sfp_exit(void)
{
i2c_del_driver(&as7716_32x_sfp_driver);
}
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("accton as7716_32x_sfp driver");
MODULE_LICENSE("GPL");
module_init(as7716_32x_sfp_init);
module_exit(as7716_32x_sfp_exit);