AS4610-54 Kernel Module builds. The source should be migrated to common for 54/30 after all of this is sorted.

This commit is contained in:
Jeffrey Townsend
2018-10-26 00:05:55 +00:00
parent ea149f0f3d
commit 1f7629399c
13 changed files with 2981 additions and 7 deletions

View File

@@ -1 +1 @@
!include $ONL_TEMPLATES/no-platform-modules.yml ARCH=armel VENDOR=accton BASENAME=arm-accton-as4610-54
!include $ONL_TEMPLATES/platform-modules.yml ARCH=armel VENDOR=accton BASENAME=arm-accton-as4610-54 KERNELS="onl-kernel-4.14-lts-arm-iproc-all:armel"

View File

@@ -0,0 +1,7 @@
KERNELS := onl-kernel-4.14-lts-arm-iproc-all:armel
KMODULES := $(wildcard *.c)
KINCLUDES := $(wildcard *.h)
VENDOR := accton
BASENAME := arm-accton-as4610-54
ARCH := arm
include $(ONL)/make/kmodule.mk

View File

@@ -0,0 +1,344 @@
/*
* A hwmon driver for the Accton as4610 fan
*
* Copyright (C) 2016 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/platform_device.h>
#include "accton_i2c_cpld.h"
#define DRVNAME "as4610_fan"
static struct as4610_fan_data *as4610_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);
/* fan related data, the index should match sysfs_fan_attributes
*/
static const u8 fan_reg[] = {
0x2B, /* fan PWM(for all fan) */
0x2D, /* fan 1 speed(rpm) */
0x2C, /* fan 2 speed(rpm) */
0x11, /* fan1-2 operating status */
};
static struct as4610_fan_data *fan_data = NULL;
/* Each client has this additional data */
struct as4610_fan_data {
struct platform_device *pdev;
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
};
enum sysfs_fan_attributes {
FAN_DUTY_CYCLE_PERCENTAGE, /* Only one CPLD register to control duty cycle for all fans */
FAN1_SPEED_RPM,
FAN2_SPEED_RPM,
FAN_FAULT,
FAN1_FAULT,
FAN2_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_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_SPEED_RPM_SENSOR_DEV_ATTR(index) \
static SENSOR_DEVICE_ATTR(fan##index##_speed_rpm, S_IRUGO, fan_show_value, NULL, FAN##index##_SPEED_RPM)
#define DECLARE_FAN_SPEED_RPM_ATTR(index) &sensor_dev_attr_fan##index##_speed_rpm.dev_attr.attr
/* fan fault attributes in this platform */
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(1);
DECLARE_FAN_FAULT_SENSOR_DEV_ATTR(2);
/* fan speed(rpm) attributes in this platform */
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(1);
DECLARE_FAN_SPEED_RPM_SENSOR_DEV_ATTR(2);
/* 1 fan duty cycle attribute in this platform */
DECLARE_FAN_DUTY_CYCLE_SENSOR_DEV_ATTR();
static struct attribute *as4610_fan_attributes[] = {
/* fan related attributes */
DECLARE_FAN_FAULT_ATTR(1),
DECLARE_FAN_FAULT_ATTR(2),
DECLARE_FAN_SPEED_RPM_ATTR(1),
DECLARE_FAN_SPEED_RPM_ATTR(2),
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 as4610_fan_read_value(u8 reg)
{
return accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, reg);
}
static int as4610_fan_write_value(u8 reg, u8 value)
{
return accton_i2c_cpld_write(AS4610_CPLD_SLAVE_ADDR, 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 * 125 + 5)/10);
}
static u8 duty_cycle_to_reg_val(u8 duty_cycle)
{
return ((u32)duty_cycle * 10 / 125);
}
static u32 reg_val_to_speed_rpm(u8 reg_val)
{
/* Count Frequency is 1.515KHz= 0.66ms
* Count Period = 400 cycle = 400*0.66ms = 264ms
* R.P.M value = read value x3.79*60/2
* 3.79 = 1000ms/264ms
* 60 = 1min =60s
* 2 = 1 rotation of fan has two pulses.
*/
return (u32)reg_val * 379 * 60 / 2 / 100;
}
static u8 is_fan_fault(struct as4610_fan_data *data, enum fan_id id)
{
u8 mask = (id == FAN1_ID) ? 0x20 : 0x10;
return !(data->reg_val[FAN_FAULT] & mask);
}
static ssize_t set_duty_cycle(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int error, value;
error = kstrtoint(buf, 10, &value);
if (error)
return error;
if (value < 0 || value > FAN_MAX_DUTY_CYCLE)
return -EINVAL;
as4610_fan_write_value(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 as4610_fan_data *data = as4610_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[attr->index]);
ret = sprintf(buf, "%u\n", duty_cycle);
break;
}
case FAN1_SPEED_RPM:
case FAN2_SPEED_RPM:
ret = sprintf(buf, "%u\n", reg_val_to_speed_rpm(data->reg_val[attr->index]));
break;
case FAN1_FAULT:
case FAN2_FAULT:
ret = sprintf(buf, "%d\n", is_fan_fault(data, attr->index - FAN1_FAULT));
break;
default:
break;
}
}
return ret;
}
static const struct attribute_group as4610_fan_group = {
.attrs = as4610_fan_attributes,
};
static struct as4610_fan_data *as4610_fan_update_device(struct device *dev)
{
mutex_lock(&fan_data->update_lock);
if (time_after(jiffies, fan_data->last_updated + HZ + HZ / 2) ||
!fan_data->valid) {
int i;
dev_dbg(fan_data->hwmon_dev, "Starting as4610_fan update\n");
fan_data->valid = 0;
/* Update fan data
*/
for (i = 0; i < ARRAY_SIZE(fan_data->reg_val); i++) {
int status = as4610_fan_read_value(fan_reg[i]);
if (status < 0) {
fan_data->valid = 0;
mutex_unlock(&fan_data->update_lock);
dev_dbg(fan_data->hwmon_dev, "reg %d, err %d\n", fan_reg[i], status);
return fan_data;
}
else {
fan_data->reg_val[i] = status;
}
}
fan_data->last_updated = jiffies;
fan_data->valid = 1;
}
mutex_unlock(&fan_data->update_lock);
return fan_data;
}
static int as4610_fan_probe(struct platform_device *pdev)
{
int status = -1;
/* Register sysfs hooks */
status = sysfs_create_group(&pdev->dev.kobj, &as4610_fan_group);
if (status) {
goto exit;
}
fan_data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(fan_data->hwmon_dev)) {
status = PTR_ERR(fan_data->hwmon_dev);
goto exit_remove;
}
dev_info(&pdev->dev, "accton_as4610_fan\n");
return 0;
exit_remove:
sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group);
exit:
return status;
}
static int as4610_fan_remove(struct platform_device *pdev)
{
hwmon_device_unregister(fan_data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &as4610_fan_group);
return 0;
}
static const struct i2c_device_id as4610_fan_id[] = {
{ "as4610_fan", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, as4610_fan_id);
static struct platform_driver as4610_fan_driver = {
.probe = as4610_fan_probe,
.remove = as4610_fan_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
};
static int __init as4610_fan_init(void)
{
int ret;
if (as4610_number_of_system_fan() == 0) {
return -ENODEV;
}
ret = platform_driver_register(&as4610_fan_driver);
if (ret < 0) {
goto exit;
}
fan_data = kzalloc(sizeof(struct as4610_fan_data), GFP_KERNEL);
if (!fan_data) {
ret = -ENOMEM;
platform_driver_unregister(&as4610_fan_driver);
goto exit;
}
mutex_init(&fan_data->update_lock);
fan_data->valid = 0;
fan_data->pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
if (IS_ERR(fan_data->pdev)) {
ret = PTR_ERR(fan_data->pdev);
platform_driver_unregister(&as4610_fan_driver);
kfree(fan_data);
goto exit;
}
exit:
return ret;
}
static void __exit as4610_fan_exit(void)
{
if (!fan_data) {
return;
}
platform_device_unregister(fan_data->pdev);
platform_driver_unregister(&as4610_fan_driver);
kfree(fan_data);
}
late_initcall(as4610_fan_init);
module_exit(as4610_fan_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as4610_fan driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,674 @@
/*
* A LED driver for the accton_as4610_led
*
* Copyright (C) 2016 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/bitops.h>
#include "accton_i2c_cpld.h"
//extern void led_classdev_unregister(struct led_classdev *led_cdev);
//extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
#define DRVNAME "as4610_led"
struct as4610_led_data {
struct platform_device *pdev;
struct mutex update_lock;
char valid; /* != 0 if registers are valid */
unsigned long last_updated; /* In jiffies */
int led_map;
u8 reg_val[5]; /* Register value, 0 = (0x1A) Blinking function
1 = (0x30) 7-seg 2
2 = (0x31) 7-seg 1
3 = (0x32) SYS/PRI/PSU1-2 LED
4 = (0x33) STK1-2/Fan/PoE/Alarm LED */
};
static struct as4610_led_data *ledctl = NULL;
/* LED related data
*/
#define LED_7SEG_REG_MASK 0x0F
#define LED_7SEG_POINT_REG_MASK 0x10
#define LED_NORMAL_MASK 0x03
#define LED_NORMAL_GREEN_VALUE 0x02
#define LED_NORMAL_AMBER_VALUE 0x01
#define LED_NORMAL_OFF_VALUE 0x00
#define LED_TYPE_SYS_REG_MASK 0xC0
#define LED_MODE_SYS_BLINK_MASK 0x80
#define LED_TYPE_PRI_REG_MASK 0x30
#define LED_MODE_PRI_BLINK_MASK 0x40
#define LED_TYPE_PSU1_REG_MASK 0x0C
#define LED_MODE_PSU1_BLINK_MASK 0x20
#define LED_TYPE_PSU2_REG_MASK 0x03
#define LED_MODE_PSU2_BLINK_MASK 0x10
#define LED_TYPE_STK1_REG_MASK 0xC0
#define LED_MODE_STK1_BLINK_MASK 0x08
#define LED_TYPE_STK2_REG_MASK 0x30
#define LED_MODE_STK2_BLINK_MASK 0x04
#define LED_TYPE_FAN_REG_MASK 0x0C
#define LED_MODE_FAN_BLINK_MASK 0x02
#define LED_TYPE_POE_ALARM_REG_MASK 0x03
#define LED_MODE_POE_ALARM_BLINK_MASK 0x01
static const u8 led_reg[] = {
0x1A, /* Blinking function */
0x30, /* 7-seg 1 */
0x31, /* 7-seg 2 */
0x32, /* SYS/PRI/PSU1-2 LED */
0x33, /* STK1-2/Fan/PoE/Alarm LED */
};
enum led_type {
LED_TYPE_SYS,
LED_TYPE_PRI,
LED_TYPE_PSU1,
LED_TYPE_PSU2,
LED_TYPE_STK1,
LED_TYPE_STK2,
LED_TYPE_7SEG_TENS,
LED_TYPE_7SEG_TENS_POINT,
LED_TYPE_7SEG_DIGITS,
LED_TYPE_7SEG_DIGITS_POINT,
LED_TYPE_FAN,
LED_TYPE_POE,
LED_TYPE_ALARM,
NUM_OF_LED
};
#define AS4610_COMMON_LED_MAP (BIT(LED_TYPE_SYS) | BIT(LED_TYPE_PRI) | BIT(LED_TYPE_PSU1) | \
BIT(LED_TYPE_PSU2)| BIT(LED_TYPE_STK1)| BIT(LED_TYPE_STK2))
#define AS4610_NPOE_LED_MAP (AS4610_COMMON_LED_MAP | BIT(LED_TYPE_7SEG_TENS) | \
BIT(LED_TYPE_7SEG_TENS_POINT) | BIT(LED_TYPE_7SEG_DIGITS) | \
BIT(LED_TYPE_7SEG_DIGITS_POINT))
#define AS4610_POE_LED_MAP (AS4610_NPOE_LED_MAP | BIT(LED_TYPE_FAN) | BIT(LED_TYPE_POE))
#define AS4610_54T_B_LED_MAP (AS4610_COMMON_LED_MAP | BIT(LED_TYPE_FAN) | BIT(LED_TYPE_ALARM))
static int as4610_ledmaps[] = {
[PID_AS4610_30T] = AS4610_NPOE_LED_MAP,
[PID_AS4610_30P] = AS4610_POE_LED_MAP,
[PID_AS4610_54T] = AS4610_NPOE_LED_MAP,
[PID_AS4610_54P] = AS4610_POE_LED_MAP,
[PID_AS4610_54T_B] = AS4610_54T_B_LED_MAP,
[PID_RESERVED] = 0,
};
enum led_light_mode {
LED_MODE_OFF = 0,
LED_MODE_GREEN,
LED_MODE_GREEN_BLINK,
LED_MODE_AMBER,
LED_MODE_AMBER_BLINK,
LED_MODE_RED,
LED_MODE_RED_BLINK,
LED_MODE_BLUE,
LED_MODE_BLUE_BLINK,
LED_MODE_AUTO,
LED_MODE_AUTO_BLINKING,
LED_MODE_UNKNOWN,
LED_MODE_SEVEN_SEGMENT_MAX = 9,
};
static int as4610_led_read_value(u8 reg)
{
return accton_i2c_cpld_read(0x30, reg);
}
static int as4610_led_write_value(u8 reg, u8 value)
{
return accton_i2c_cpld_write(0x30, reg, value);
}
static void as4610_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 as4610_led update\n");
/* Update LED data
*/
for (i = 0; i < ARRAY_SIZE(ledctl->reg_val); i++) {
int status = as4610_led_read_value(led_reg[i]);
if (status < 0) {
ledctl->valid = 0;
dev_dbg(&ledctl->pdev->dev, "reg %d, err %d\n", led_reg[i], status);
goto exit;
}
else
{
ledctl->reg_val[i] = status;
}
}
ledctl->last_updated = jiffies;
ledctl->valid = 1;
}
exit:
mutex_unlock(&ledctl->update_lock);
}
static enum led_brightness seven_segment_get(struct led_classdev *cdev, u8 reg_id)
{
as4610_led_update();
return (ledctl->reg_val[reg_id] & LED_7SEG_REG_MASK);
}
static void seven_segment_set(struct led_classdev *cdev, enum led_brightness mode, u8 reg_id)
{
if (mode > cdev->max_brightness) {
return;
}
ledctl->reg_val[reg_id] &= 0xF0;
ledctl->reg_val[reg_id] |= mode;
as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]);
}
static enum led_brightness seven_segment_digits_get(struct led_classdev *cdev)
{
return seven_segment_get(cdev, 1);
}
static void seven_segment_digits_set(struct led_classdev *cdev, enum led_brightness mode)
{
seven_segment_set(cdev, mode, 1);
}
static enum led_brightness seven_segment_tens_get(struct led_classdev *cdev)
{
return seven_segment_get(cdev, 2);
}
static void seven_segment_tens_set(struct led_classdev *cdev, enum led_brightness mode)
{
seven_segment_set(cdev, mode, 2);
}
static enum led_brightness seven_segment_point_get(struct led_classdev *cdev, u8 reg_id)
{
as4610_led_update();
return (ledctl->reg_val[reg_id] & LED_7SEG_POINT_REG_MASK) ? LED_MODE_GREEN : LED_MODE_OFF;
}
static void seven_segment_point_set(struct led_classdev *cdev,
enum led_brightness mode, u8 reg_id)
{
/* Validate brightness */
if ((int)mode < LED_MODE_OFF || mode > cdev->max_brightness) {
return;
}
if ((int)mode == (int)LED_MODE_OFF) {
ledctl->reg_val[reg_id] &= ~LED_7SEG_POINT_REG_MASK;
}
else { /* LED_MODE_GREEN */
ledctl->reg_val[reg_id] |= LED_7SEG_POINT_REG_MASK;
}
as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]);
}
static enum led_brightness seven_segment_tens_point_get(struct led_classdev *cdev)
{
return seven_segment_point_get(cdev, 2);
}
static void seven_segment_tens_point_set(struct led_classdev *cdev,
enum led_brightness mode)
{
seven_segment_point_set(cdev, mode, 2);
}
static enum led_brightness seven_segment_digits_point_get(struct led_classdev *cdev)
{
return seven_segment_point_get(cdev, 1);
}
static void seven_segment_digits_point_set(struct led_classdev *cdev,
enum led_brightness mode)
{
seven_segment_point_set(cdev, mode, 1);
}
static u8 led_is_blinking_mode(enum led_brightness mode)
{
return ((int)mode == (int)LED_MODE_GREEN_BLINK ||
(int)mode == (int)LED_MODE_AMBER_BLINK ||
(int)mode == (int)LED_MODE_AUTO_BLINKING);
}
static enum led_brightness as4610_led_auto_get(u8 blink_mask)
{
as4610_led_update();
return (ledctl->reg_val[0] & blink_mask) ? LED_MODE_AUTO_BLINKING : LED_MODE_AUTO;
}
static void as4610_led_auto_set(struct led_classdev *cdev,
enum led_brightness mode, u8 blink_mask)
{
/* Validate brightness */
if ((int)mode < (int)LED_MODE_AUTO || mode > cdev->max_brightness) {
return;
}
/* Set blinking */
if (led_is_blinking_mode(mode)) {
ledctl->reg_val[0] |= blink_mask;
}
else {
ledctl->reg_val[0] &= ~blink_mask;
}
as4610_led_write_value(led_reg[0], ledctl->reg_val[0]);
}
static enum led_brightness as4610_led_psu1_get(struct led_classdev *cdev)
{
return as4610_led_auto_get(LED_MODE_PSU1_BLINK_MASK);
}
static void as4610_led_psu1_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_auto_set(cdev, mode, LED_MODE_PSU1_BLINK_MASK);
}
static enum led_brightness as4610_led_psu2_get(struct led_classdev *cdev)
{
return as4610_led_auto_get(LED_MODE_PSU2_BLINK_MASK);
}
static void as4610_led_psu2_set(struct led_classdev *led_cdev,
enum led_brightness mode)
{
as4610_led_auto_set(led_cdev, mode, LED_MODE_PSU2_BLINK_MASK);
}
static enum led_brightness as4610_led_fan_get(struct led_classdev *cdev)
{
return as4610_led_auto_get(LED_MODE_FAN_BLINK_MASK);
}
static void as4610_led_fan_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_auto_set(cdev, mode, LED_MODE_FAN_BLINK_MASK);
}
static u8 led_normal_light_mode_to_reg_val(enum led_brightness mode)
{
if (led_is_blinking_mode(mode)) {
mode -= 1; /* convert blinking mode to non-blinking mode */
}
if ((int)mode == (int)LED_MODE_GREEN) {
return LED_NORMAL_GREEN_VALUE;
}
else if ((int)mode == (int)LED_MODE_AMBER) {
return LED_NORMAL_AMBER_VALUE;
}
return LED_NORMAL_OFF_VALUE;
}
static enum led_brightness led_normal_reg_val_to_light_mode(u8 reg_val)
{
reg_val &= LED_NORMAL_MASK;
if (reg_val & LED_NORMAL_GREEN_VALUE) {
return LED_MODE_GREEN;
}
else if (reg_val & LED_NORMAL_AMBER_VALUE) {
return LED_MODE_AMBER;
}
return LED_MODE_OFF;
}
static void as4610_led_normal_set(struct led_classdev *cdev,
enum led_brightness mode, u8 blink_mask, u8 reg_id, u8 reg_mask, u8 shift)
{
/* Validate brightness */
if (mode > cdev->max_brightness) {
return;
}
/* Set blinking */
if (led_is_blinking_mode(mode)) {
ledctl->reg_val[0] |= blink_mask;
}
else {
ledctl->reg_val[0] &= ~blink_mask;
}
as4610_led_write_value(led_reg[0], ledctl->reg_val[0]);
/* Set color */
ledctl->reg_val[reg_id] &= ~reg_mask;
ledctl->reg_val[reg_id] |= (led_normal_light_mode_to_reg_val(mode) << shift);
as4610_led_write_value(led_reg[reg_id], ledctl->reg_val[reg_id]);
}
static enum led_brightness as4610_led_normal_get(u8 reg_id, u8 blink_mask, u8 shift)
{
u8 blinking = 0;
enum led_brightness mode;
as4610_led_update();
mode = led_normal_reg_val_to_light_mode(ledctl->reg_val[reg_id] >> shift);
if ((int)mode == (int)LED_MODE_OFF) {
return mode;
}
/* Checking blinking */
if (ledctl->reg_val[0] & blink_mask) {
blinking = 1;
}
return blinking ? (mode+1) : mode;
}
static void as4610_led_sys_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_normal_set(cdev, mode, LED_MODE_SYS_BLINK_MASK,
3, LED_TYPE_SYS_REG_MASK, 6);
}
static enum led_brightness as4610_led_sys_get(struct led_classdev *cdev)
{
return as4610_led_normal_get(3, LED_MODE_SYS_BLINK_MASK, 6);
}
static void as4610_led_pri_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_normal_set(cdev, mode, LED_MODE_PRI_BLINK_MASK,
3, LED_TYPE_PRI_REG_MASK, 4);
}
static enum led_brightness as4610_led_pri_get(struct led_classdev *cdev)
{
return as4610_led_normal_get(3, LED_MODE_PRI_BLINK_MASK, 4);
}
static void as4610_led_poe_alarm_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_normal_set(cdev, mode, LED_MODE_POE_ALARM_BLINK_MASK,
4, LED_TYPE_POE_ALARM_REG_MASK, 0);
}
static enum led_brightness as4610_led_poe_alarm_get(struct led_classdev *cdev)
{
return as4610_led_normal_get(4, LED_MODE_POE_ALARM_BLINK_MASK, 0);
}
static void as4610_led_stk1_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_normal_set(cdev, mode, LED_MODE_STK1_BLINK_MASK,
4, LED_TYPE_STK1_REG_MASK, 6);
}
static enum led_brightness as4610_led_stk1_get(struct led_classdev *cdev)
{
return as4610_led_normal_get(4, LED_MODE_STK1_BLINK_MASK, 6);
}
static void as4610_led_stk2_set(struct led_classdev *cdev,
enum led_brightness mode)
{
as4610_led_normal_set(cdev, mode, LED_MODE_STK2_BLINK_MASK,
4, LED_TYPE_STK2_REG_MASK, 4);
}
static enum led_brightness as4610_led_stk2_get(struct led_classdev *cdev)
{
return as4610_led_normal_get(4, LED_MODE_STK2_BLINK_MASK, 4);
}
static struct led_classdev as4610_leds[] = {
[LED_TYPE_SYS] = {
.name = "as4610::sys",
.default_trigger = "unused",
.brightness_set = as4610_led_sys_set,
.brightness_get = as4610_led_sys_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
[LED_TYPE_PRI] = {
.name = "as4610::pri",
.default_trigger = "unused",
.brightness_set = as4610_led_pri_set,
.brightness_get = as4610_led_pri_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
[LED_TYPE_PSU1] = {
.name = "as4610::psu1",
.default_trigger = "unused",
.brightness_set = as4610_led_psu1_set,
.brightness_get = as4610_led_psu1_get,
.max_brightness = LED_MODE_AUTO_BLINKING,
},
[LED_TYPE_PSU2] = {
.name = "as4610::psu2",
.default_trigger = "unused",
.brightness_set = as4610_led_psu2_set,
.brightness_get = as4610_led_psu2_get,
.max_brightness = LED_MODE_AUTO_BLINKING,
},
[LED_TYPE_STK1] = {
.name = "as4610::stk1",
.default_trigger = "unused",
.brightness_set = as4610_led_stk1_set,
.brightness_get = as4610_led_stk1_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
[LED_TYPE_STK2] = {
.name = "as4610::stk2",
.default_trigger = "unused",
.brightness_set = as4610_led_stk2_set,
.brightness_get = as4610_led_stk2_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
[LED_TYPE_7SEG_TENS] = {
.name = "as4610::7seg_tens",
.default_trigger = "unused",
.brightness_set = seven_segment_tens_set,
.brightness_get = seven_segment_tens_get,
.max_brightness = LED_MODE_SEVEN_SEGMENT_MAX,
},
[LED_TYPE_7SEG_TENS_POINT] = {
.name = "as4610::7seg_tens_point",
.default_trigger = "unused",
.brightness_set = seven_segment_tens_point_set,
.brightness_get = seven_segment_tens_point_get,
.max_brightness = LED_MODE_GREEN,
},
[LED_TYPE_7SEG_DIGITS] = {
.name = "as4610::7seg_digits",
.default_trigger = "unused",
.brightness_set = seven_segment_digits_set,
.brightness_get = seven_segment_digits_get,
.max_brightness = LED_MODE_SEVEN_SEGMENT_MAX,
},
[LED_TYPE_7SEG_DIGITS_POINT] = {
.name = "as4610::7seg_digits_point",
.default_trigger = "unused",
.brightness_set = seven_segment_digits_point_set,
.brightness_get = seven_segment_digits_point_get,
.max_brightness = LED_MODE_GREEN,
},
[LED_TYPE_FAN] = {
.name = "as4610::fan",
.default_trigger = "unused",
.brightness_set = as4610_led_fan_set,
.brightness_get = as4610_led_fan_get,
.max_brightness = LED_MODE_AUTO_BLINKING,
},
[LED_TYPE_POE] = {
.name = "as4610::poe",
.default_trigger = "unused",
.brightness_set = as4610_led_poe_alarm_set,
.brightness_get = as4610_led_poe_alarm_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
[LED_TYPE_ALARM] = {
.name = "as4610::alarm",
.default_trigger = "unused",
.brightness_set = as4610_led_poe_alarm_set,
.brightness_get = as4610_led_poe_alarm_get,
.max_brightness = LED_MODE_AMBER_BLINK,
},
};
static int as4610_led_probe(struct platform_device *pdev)
{
int ret = 0, i;
for (i = 0; i < NUM_OF_LED; i++) {
if (!(ledctl->led_map & BIT(i))) {
continue;
}
ret = led_classdev_register(&pdev->dev, &as4610_leds[i]);
if (ret < 0) {
goto error;
}
}
return 0;
error:
for (i = i-1; i >= 0; i--) {
/* only unregister the LEDs that were successfully registered */
if (!(ledctl->led_map & BIT(i))) {
continue;
}
led_classdev_unregister(&as4610_leds[i]);
}
return ret;
}
static int as4610_led_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < NUM_OF_LED; i++) {
if (!(ledctl->led_map & BIT(i))) {
continue;
}
led_classdev_unregister(&as4610_leds[i]);
}
return 0;
}
static struct platform_driver as4610_led_driver = {
.probe = as4610_led_probe,
.remove = as4610_led_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
};
static int __init as4610_led_init(void)
{
int ret, pid;
if (as4610_product_id() == PID_UNKNOWN) {
return -ENODEV;
}
ret = platform_driver_register(&as4610_led_driver);
if (ret < 0) {
goto exit;
}
ledctl = kzalloc(sizeof(struct as4610_led_data), GFP_KERNEL);
if (!ledctl) {
ret = -ENOMEM;
platform_driver_unregister(&as4610_led_driver);
goto exit;
}
pid = as4610_product_id();
if (pid == PID_UNKNOWN) {
return -ENODEV;
}
ledctl->led_map = as4610_ledmaps[pid];
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(&as4610_led_driver);
kfree(ledctl);
goto exit;
}
exit:
return ret;
}
static void __exit as4610_led_exit(void)
{
if (!ledctl) {
return;
}
platform_device_unregister(ledctl->pdev);
platform_driver_unregister(&as4610_led_driver);
kfree(ledctl);
}
late_initcall(as4610_led_init);
module_exit(as4610_led_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as4610_led driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,286 @@
/*
* An hwmon driver for accton as4610 Power Module
*
* Copyright (C) 2016 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 as4610_psu_read_data(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 as4610_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 as4610_psu_data *as4610_psu_update_device(struct device *dev);
enum as4610_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 *as4610_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 as4610_psu_data *data = as4610_psu_update_device(dev);
u8 status = 0;
if (attr->index == PSU_PRESENT) {
status = (data->status >> (data->index*2) & 0x1);
}
else { /* PSU_POWER_GOOD */
status = (data->status >> (data->index*2 + 1) & 0x1);
}
return sprintf(buf, "%d\n", status);
}
static ssize_t show_model_name(struct device *dev, struct device_attribute *da,
char *buf)
{
struct as4610_psu_data *data = as4610_psu_update_device(dev);
return sprintf(buf, "%s\n", data->model_name);
}
static const struct attribute_group as4610_psu_group = {
.attrs = as4610_psu_attributes,
};
static int as4610_psu_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct as4610_psu_data *data;
int status;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
status = -EIO;
goto exit;
}
data = kzalloc(sizeof(struct as4610_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, &as4610_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, &as4610_psu_group);
exit_free:
kfree(data);
exit:
return status;
}
static int as4610_psu_remove(struct i2c_client *client)
{
struct as4610_psu_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &as4610_psu_group);
kfree(data);
return 0;
}
enum psu_index
{
as4610_psu1,
as4610_psu2
};
static const struct i2c_device_id as4610_psu_id[] = {
{ "as4610_psu1", as4610_psu1 },
{ "as4610_psu2", as4610_psu2 },
{}
};
MODULE_DEVICE_TABLE(i2c, as4610_psu_id);
static struct i2c_driver as4610_psu_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "as4610_psu",
},
.probe = as4610_psu_probe,
.remove = as4610_psu_remove,
.id_table = as4610_psu_id,
.address_list = normal_i2c,
};
static int as4610_psu_read_data(struct i2c_client *client, u8 command, u8 *data,
int count)
{
int status = 0;
while (count) {
status = i2c_smbus_read_byte_data(client, command);
if (unlikely(status < 0)) {
break;
}
*data = (u8)status;
data += 1;
command += 1;
count -= 1;
}
return status;
}
static struct as4610_psu_data *as4610_psu_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct as4610_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 present = 0;
data->valid = 0;
data->status = 0;
dev_dbg(&client->dev, "Starting as4610 update\n");
/* Read psu status */
status = accton_i2c_cpld_read(0x30, 0x11);
if (status < 0) {
dev_dbg(&client->dev, "cpld reg 0x30 err %d\n", status);
goto exit;
}
else {
data->status = status;
}
/* Read model name */
memset(data->model_name, 0, sizeof(data->model_name));
present = (data->status >> (data->index*2) & 0x1);
if (present) {
int len = ARRAY_SIZE(data->model_name)-1;
status = as4610_psu_read_data(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);
goto exit;
}
else {
data->model_name[ARRAY_SIZE(data->model_name)-1] = '\0';
}
}
data->last_updated = jiffies;
data->valid = 1;
}
exit:
mutex_unlock(&data->update_lock);
return data;
}
static int __init as4610_psu_init(void)
{
return i2c_add_driver(&as4610_psu_driver);
}
static void __exit as4610_psu_exit(void)
{
i2c_del_driver(&as4610_psu_driver);
}
module_init(as4610_psu_init);
module_exit(as4610_psu_exit);
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("as4610_psu driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,284 @@
/*
* A hwmon driver for the accton_i2c_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/i2c.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/dmi.h>
#include "accton_i2c_cpld.h"
static LIST_HEAD(cpld_client_list);
static struct mutex list_lock;
enum cpld_device_id {
as4610_30_cpld,
as4610_54_cpld
};
struct cpld_data {
enum cpld_device_id id;
};
struct cpld_client_node {
struct i2c_client *client;
struct list_head list;
};
/* Addresses scanned for accton_i2c_cpld
*/
static const unsigned short normal_i2c[] = { 0x31, 0x35, 0x60, 0x61, 0x62, I2C_CLIENT_END };
static u8 cpld_product_id_offset(enum cpld_device_id id)
{
switch (id) {
case as4610_30_cpld:
case as4610_54_cpld:
return 0x1;
}
return 0;
}
static int cpld_has_product_id(const struct i2c_device_id *dev_id)
{
return (dev_id->driver_data == as4610_30_cpld) ||
(dev_id->driver_data == as4610_54_cpld);
}
static ssize_t show_cpld_product_id(struct device *dev, struct device_attribute *attr, char *buf)
{
int val = 0;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
val = i2c_smbus_read_byte_data(client, cpld_product_id_offset(data->id));
if (val < 0) {
dev_dbg(&client->dev, "cpld(0x%x) reg(0x%x) err %d\n", client->addr, cpld_product_id_offset(data->id), val);
}
return sprintf(buf, "%d\n", (val & 0xF));
}
static u8 cpld_version_offset(enum cpld_device_id id)
{
switch (id) {
case as4610_30_cpld:
case as4610_54_cpld:
return 0xB;
}
return 0;
}
static ssize_t show_cpld_version(struct device *dev, struct device_attribute *attr, char *buf)
{
int val = 0;
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
val = i2c_smbus_read_byte_data(client, cpld_version_offset(data->id));
if (val < 0) {
dev_dbg(&client->dev, "cpld(0x%x) reg(0xB) err %d\n", client->addr, val);
}
return sprintf(buf, "%d\n", val);
}
static void accton_i2c_cpld_add_client(struct i2c_client *client, enum cpld_device_id id)
{
struct cpld_client_node *node = kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
struct cpld_data *data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL);
if (!node) {
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n", client->addr);
return;
}
if (!data) {
dev_dbg(&client->dev, "Can't allocate cpld_client_data (0x%x)\n", client->addr);
return;
}
data->id = id;
i2c_set_clientdata(client, data);
node->client = client;
mutex_lock(&list_lock);
list_add(&node->list, &cpld_client_list);
mutex_unlock(&list_lock);
}
static void accton_i2c_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 struct device_attribute ver = __ATTR(version, 0600, show_cpld_version, NULL);
static struct device_attribute pid = __ATTR(product_id, 0600, show_cpld_product_id, NULL);
static int accton_i2c_cpld_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int status;
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;
}
status = sysfs_create_file(&client->dev.kobj, &ver.attr);
if (status) {
goto exit;
}
if (cpld_has_product_id(dev_id)) {
status = sysfs_create_file(&client->dev.kobj, &pid.attr);
if (status) {
goto exit;
}
}
dev_info(&client->dev, "chip found\n");
accton_i2c_cpld_add_client(client, (enum cpld_device_id)dev_id->driver_data);
return 0;
exit:
return status;
}
static int accton_i2c_cpld_remove(struct i2c_client *client)
{
sysfs_remove_file(&client->dev.kobj, &ver.attr);
accton_i2c_cpld_remove_client(client);
return 0;
}
static const struct i2c_device_id accton_i2c_cpld_id[] = {
{ "as4610_30_cpld", as4610_30_cpld },
{ "as4610_54_cpld", as4610_54_cpld },
{ /* LIST END */}
};
MODULE_DEVICE_TABLE(i2c, accton_i2c_cpld_id);
static struct i2c_driver accton_i2c_cpld_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "accton_i2c_cpld",
},
.probe = accton_i2c_cpld_probe,
.remove = accton_i2c_cpld_remove,
.id_table = accton_i2c_cpld_id,
.address_list = normal_i2c,
};
int accton_i2c_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(accton_i2c_cpld_read);
int accton_i2c_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(accton_i2c_cpld_write);
static int __init accton_i2c_cpld_init(void)
{
mutex_init(&list_lock);
return i2c_add_driver(&accton_i2c_cpld_driver);
}
static void __exit accton_i2c_cpld_exit(void)
{
i2c_del_driver(&accton_i2c_cpld_driver);
}
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
MODULE_DESCRIPTION("accton_i2c_cpld driver");
MODULE_LICENSE("GPL");
module_init(accton_i2c_cpld_init);
module_exit(accton_i2c_cpld_exit);

View File

@@ -0,0 +1,76 @@
/*
* A hwmon driver for the accton_i2c_cpld
*
* Copyright (C) 2016 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.
*/
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);
#define AS4610_CPLD_SLAVE_ADDR 0x30
#define AS4610_CPLD_PID_OFFSET 0x01 /* Product ID offset */
enum as4610_product_id_e {
PID_AS4610_30T,
PID_AS4610_30P,
PID_AS4610_54T,
PID_AS4610_54P,
PID_RESERVED,
PID_AS4610_54T_B,
PID_UNKNOWN
};
static inline int as4610_product_id(void)
{
int pid = accton_i2c_cpld_read(AS4610_CPLD_SLAVE_ADDR, AS4610_CPLD_PID_OFFSET);
pid &= 0xF;
if (pid < PID_AS4610_30T || pid > PID_AS4610_54T_B || pid == PID_RESERVED) {
return PID_UNKNOWN;
}
return pid;
}
static inline int as4610_is_poe_system(void)
{
int pid = as4610_product_id();
return (pid == PID_AS4610_30P || pid == PID_AS4610_54P);
}
static inline int as4610_number_of_system_fan(void)
{
int nFan = 0;
int pid = as4610_product_id();
switch (pid) {
case PID_AS4610_30P:
case PID_AS4610_54P:
nFan = 1;
break;
case PID_AS4610_54T_B:
nFan = 2;
break;
default:
nFan = 0;
break;
}
return nFan;
}

View File

@@ -1,10 +1,15 @@
INCLUDES=include .
KERNEL := onl-kernel-4.14-lts-arm-iproc-all:armel
DTS_LIST := arm-accton-as4610.dts
VPATH := ../../../../../src
include $(ONL)/make/dtbs.mk
#
# The 4610 DTS relies on the common arm devices tree includes. These are linked here from the kernel package.
#
setup::
onlpm --link-dir onl-kernel-3.2-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-3.2-lts-arm-iproc-all/mbuilds/arch/arm/boot/dts dts
onlpm --link-dir onl-kernel-4.14-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-4.14-lts-arm-iproc-all/mbuilds/arch/arm/boot/dts dts
onlpm --link-dir onl-kernel-4.14-lts-arm-iproc-all:armel /usr/share/onl/packages/armel/onl-kernel-4.14-lts-arm-iproc-all/mbuilds/include include
clean::
rm -rf fsl
setup-clean::
#rm -rf dts include

View File

@@ -2,16 +2,16 @@
######################################################################
#
# platform-config for AS4610
# platform-config for AS4610-54
#
######################################################################
arm-accton-as4610-54-r0:
flat_image_tree:
kernel:
<<: *arm-iproc-kernel
<<: *arm-iproc-4-14-kernel
dtb:
=: arm-accton-as4610-54-r0.dtb
=: arm-accton-as4610.dtb
package: onl-platform-build-arm-accton-as4610-54-r0:armel
itb:

View File

@@ -6,3 +6,30 @@ class OnlPlatform_arm_accton_as4610_54_r0(OnlPlatformAccton,
PLATFORM='arm-accton-as4610-54-r0'
MODEL="AS4610-54"
SYS_OBJECT_ID=".4610.54"
def baseconfig(self):
self.insmod("accton_i2c_cpld")
self.new_i2c_device("as4610_54_cpld", 0x30, 0)
self.insmod("accton_as4610_sfp")
self.insmod("accton_as4610_psu")
self.insmod("accton_as4610_fan")
self.insmod("ym2651y")
self.new_i2c_devices(
[
("pca9548", 0x70, 1),
("as4610_sfp1", 0x50, 2),
("as4610_sfp2", 0x50, 3),
("as4610_sfp3", 0x50, 4),
("as4610_sfp4", 0x50, 5),
("as4610_sfp5", 0x50, 6),
("as4610_sfp6", 0x50, 7),
("as4610_psu1", 0x50, 8),
("as4610_psu2", 0x51, 8),
("ym1921", 0x58, 8),
("ym1921", 0x59, 8),
("lm77", 0x48, 9),
# ("ds1307", 0x68, 9),
("24c04", 0x50, 9),
]
)
return True