988 lines
28 KiB
C
988 lines
28 KiB
C
|
/*
|
||
|
* Copyright (C) Brandon Chuang <brandon_chuang@accton.com.tw>
|
||
|
*
|
||
|
* This module supports the accton cpld that hold the channel select
|
||
|
* mechanism for other i2c slave devices, such as SFP.
|
||
|
* This includes the:
|
||
|
* Accton as9726_32d CPU-CPLD/CPLD2/CPLD3
|
||
|
*
|
||
|
* Based on:
|
||
|
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
|
||
|
* Copyright (C) 2006
|
||
|
*
|
||
|
* Based on:
|
||
|
* pca954x.c from Ken Harrenstien
|
||
|
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
|
||
|
*
|
||
|
* Based on:
|
||
|
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
|
||
|
* and
|
||
|
* pca9540.c from Jean Delvare <khali@linux-fr.org>.
|
||
|
*
|
||
|
* This file is licensed under the terms of the GNU General Public
|
||
|
* License version 2. This program is licensed "as is" without any
|
||
|
* warranty of any kind, whether express or implied.
|
||
|
*/
|
||
|
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/version.h>
|
||
|
#include <linux/stat.h>
|
||
|
#include <linux/hwmon-sysfs.h>
|
||
|
#include <linux/delay.h>
|
||
|
|
||
|
#define I2C_RW_RETRY_COUNT 10
|
||
|
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
|
||
|
|
||
|
static LIST_HEAD(cpld_client_list);
|
||
|
static struct mutex list_lock;
|
||
|
|
||
|
struct cpld_client_node {
|
||
|
struct i2c_client *client;
|
||
|
struct list_head list;
|
||
|
};
|
||
|
|
||
|
enum cpld_type {
|
||
|
as9726_32d_fpga,
|
||
|
as9726_32d_cpld2,
|
||
|
as9726_32d_cpld3,
|
||
|
as9726_32d_cpld_cpu
|
||
|
};
|
||
|
|
||
|
struct as9726_32d_cpld_data {
|
||
|
enum cpld_type type;
|
||
|
struct device *hwmon_dev;
|
||
|
struct mutex update_lock;
|
||
|
};
|
||
|
|
||
|
static const struct i2c_device_id as9726_32d_cpld_id[] = {
|
||
|
{ "as9726_32d_fpga", as9726_32d_fpga },
|
||
|
{ "as9726_32d_cpld2", as9726_32d_cpld2 },
|
||
|
{ "as9726_32d_cpld3", as9726_32d_cpld3 },
|
||
|
{ "as9726_32d_cpld_cpu", as9726_32d_cpld_cpu },
|
||
|
{ }
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(i2c, as9726_32d_cpld_id);
|
||
|
|
||
|
#define TRANSCEIVER_PRESENT_ATTR_ID(index) MODULE_PRESENT_##index
|
||
|
#define TRANSCEIVER_TXDISABLE_ATTR_ID(index) MODULE_TXDISABLE_##index
|
||
|
#define TRANSCEIVER_RXLOS_ATTR_ID(index) MODULE_RXLOS_##index
|
||
|
#define TRANSCEIVER_TXFAULT_ATTR_ID(index) MODULE_TXFAULT_##index
|
||
|
#define TRANSCEIVER_RESET_ATTR_ID(index) MODULE_RESET_##index
|
||
|
#define CPLD_INTR_ATTR_ID(index) CPLD_INTR_##index
|
||
|
|
||
|
enum as9726_32d_cpld_sysfs_attributes {
|
||
|
CPLD_VERSION,
|
||
|
ACCESS,
|
||
|
/* transceiver attributes */
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(1),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(2),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(3),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(4),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(5),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(6),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(7),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(8),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(9),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(10),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(11),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(12),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(13),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(14),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(15),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(16),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(17),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(18),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(19),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(20),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(21),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(22),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(23),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(24),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(25),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(26),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(27),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(28),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(29),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(30),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(31),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(32),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(33),
|
||
|
TRANSCEIVER_PRESENT_ATTR_ID(34),
|
||
|
TRANSCEIVER_TXDISABLE_ATTR_ID(33),
|
||
|
TRANSCEIVER_TXDISABLE_ATTR_ID(34),
|
||
|
TRANSCEIVER_RXLOS_ATTR_ID(33),
|
||
|
TRANSCEIVER_RXLOS_ATTR_ID(34),
|
||
|
TRANSCEIVER_TXFAULT_ATTR_ID(33),
|
||
|
TRANSCEIVER_TXFAULT_ATTR_ID(34),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(1),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(2),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(3),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(4),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(5),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(6),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(7),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(8),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(9),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(10),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(11),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(12),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(13),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(14),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(15),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(16),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(17),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(18),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(19),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(20),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(21),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(22),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(23),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(24),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(25),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(26),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(27),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(28),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(29),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(30),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(31),
|
||
|
TRANSCEIVER_RESET_ATTR_ID(32),
|
||
|
CPLD_INTR_ATTR_ID(1),
|
||
|
CPLD_INTR_ATTR_ID(2),
|
||
|
CPLD_INTR_ATTR_ID(3),
|
||
|
CPLD_INTR_ATTR_ID(4),
|
||
|
|
||
|
};
|
||
|
|
||
|
/* sysfs attributes for hwmon
|
||
|
*/
|
||
|
static ssize_t show_interrupt(struct device *dev, struct device_attribute *da,
|
||
|
char *buf);
|
||
|
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||
|
char *buf);
|
||
|
static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da,
|
||
|
const char *buf, size_t count);
|
||
|
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 ssize_t get_mode_reset(struct device *dev, struct device_attribute *da,
|
||
|
char *buf);
|
||
|
static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da,
|
||
|
const char *buf, size_t count);
|
||
|
static int as9726_32d_cpld_read_internal(struct i2c_client *client, u8 reg);
|
||
|
static int as9726_32d_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value);
|
||
|
|
||
|
/* transceiver attributes */
|
||
|
#define DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(index) \
|
||
|
static SENSOR_DEVICE_ATTR(module_present_##index, S_IRUGO, show_status, NULL, MODULE_PRESENT_##index)
|
||
|
#define DECLARE_TRANSCEIVER_PRESENT_ATTR(index) &sensor_dev_attr_module_present_##index.dev_attr.attr
|
||
|
|
||
|
#define DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(index) \
|
||
|
static SENSOR_DEVICE_ATTR(module_tx_disable_##index, S_IRUGO | S_IWUSR, show_status, set_tx_disable, MODULE_TXDISABLE_##index); \
|
||
|
static SENSOR_DEVICE_ATTR(module_rx_los_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index); \
|
||
|
static SENSOR_DEVICE_ATTR(module_tx_fault_##index, S_IRUGO, show_status, NULL, MODULE_RXLOS_##index);
|
||
|
|
||
|
#define DECLARE_SFP_TRANSCEIVER_ATTR(index) \
|
||
|
&sensor_dev_attr_module_tx_disable_##index.dev_attr.attr, \
|
||
|
&sensor_dev_attr_module_rx_los_##index.dev_attr.attr, \
|
||
|
&sensor_dev_attr_module_tx_fault_##index.dev_attr.attr
|
||
|
|
||
|
/*reset*/
|
||
|
#define DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(index) \
|
||
|
static SENSOR_DEVICE_ATTR(module_reset_##index, S_IWUSR | S_IRUGO, get_mode_reset, set_mode_reset, MODULE_RESET_##index)
|
||
|
#define DECLARE_TRANSCEIVER_RESET_ATTR(index) &sensor_dev_attr_module_reset_##index.dev_attr.attr
|
||
|
|
||
|
/*cpld interrupt*/
|
||
|
#define DECLARE_CPLD_DEVICE_INTR_ATTR(index) \
|
||
|
static SENSOR_DEVICE_ATTR(cpld_intr_##index, S_IRUGO, show_interrupt, NULL, CPLD_INTR_##index)
|
||
|
#define DECLARE_CPLD_INTR_ATTR(index) &sensor_dev_attr_cpld_intr_##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 */
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(1);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(2);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(3);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(4);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(5);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(6);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(7);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(8);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(9);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(10);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(11);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(12);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(13);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(14);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(15);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(16);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(17);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(18);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(19);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(20);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(21);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(22);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(23);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(24);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(25);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(26);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(27);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(28);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(29);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(30);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(31);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(32);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(33);
|
||
|
DECLARE_TRANSCEIVER_PRESENT_SENSOR_DEVICE_ATTR(34);
|
||
|
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(33);
|
||
|
DECLARE_SFP_TRANSCEIVER_SENSOR_DEVICE_ATTR(34);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(1);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(2);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(3);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(4);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(5);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(6);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(7);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(8);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(9);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(10);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(11);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(12);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(13);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(14);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(15);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(16);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(17);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(18);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(19);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(20);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(21);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(22);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(23);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(24);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(25);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(26);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(27);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(28);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(29);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(30);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(31);
|
||
|
DECLARE_TRANSCEIVER_SENSOR_DEVICE_RESET_ATTR(32);
|
||
|
DECLARE_CPLD_DEVICE_INTR_ATTR(1);
|
||
|
DECLARE_CPLD_DEVICE_INTR_ATTR(2);
|
||
|
DECLARE_CPLD_DEVICE_INTR_ATTR(3);
|
||
|
DECLARE_CPLD_DEVICE_INTR_ATTR(4);
|
||
|
|
||
|
|
||
|
|
||
|
static struct attribute *as9726_32d_fpga_attributes[] = {
|
||
|
&sensor_dev_attr_version.dev_attr.attr,
|
||
|
&sensor_dev_attr_access.dev_attr.attr,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static const struct attribute_group as9726_32d_fpga_group = {
|
||
|
.attrs = as9726_32d_fpga_attributes,
|
||
|
};
|
||
|
|
||
|
static struct attribute *as9726_32d_cpld2_attributes[] = {
|
||
|
&sensor_dev_attr_version.dev_attr.attr,
|
||
|
&sensor_dev_attr_access.dev_attr.attr,
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(1),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(2),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(3),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(4),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(5),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(6),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(7),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(8),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(9),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(10),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(11),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(12),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(13),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(14),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(15),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(16),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(1),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(2),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(3),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(4),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(5),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(6),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(7),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(8),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(9),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(10),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(11),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(12),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(13),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(14),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(15),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(16),
|
||
|
DECLARE_CPLD_INTR_ATTR(1),
|
||
|
DECLARE_CPLD_INTR_ATTR(2),
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static const struct attribute_group as9726_32d_cpld2_group = {
|
||
|
.attrs = as9726_32d_cpld2_attributes,
|
||
|
};
|
||
|
|
||
|
static struct attribute *as9726_32d_cpld3_attributes[] = {
|
||
|
&sensor_dev_attr_version.dev_attr.attr,
|
||
|
&sensor_dev_attr_access.dev_attr.attr,
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(17),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(18),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(19),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(20),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(21),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(22),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(23),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(24),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(25),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(26),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(27),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(28),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(29),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(30),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(31),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(32),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(33),
|
||
|
DECLARE_TRANSCEIVER_PRESENT_ATTR(34),
|
||
|
DECLARE_SFP_TRANSCEIVER_ATTR(33),
|
||
|
DECLARE_SFP_TRANSCEIVER_ATTR(34),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(17),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(18),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(19),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(20),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(21),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(22),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(23),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(24),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(25),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(26),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(27),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(28),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(29),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(30),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(31),
|
||
|
DECLARE_TRANSCEIVER_RESET_ATTR(32),
|
||
|
DECLARE_CPLD_INTR_ATTR(3),
|
||
|
DECLARE_CPLD_INTR_ATTR(4),
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static const struct attribute_group as9726_32d_cpld3_group = {
|
||
|
.attrs = as9726_32d_cpld3_attributes,
|
||
|
};
|
||
|
|
||
|
static struct attribute *as9726_32d_cpld_cpu_attributes[] = {
|
||
|
&sensor_dev_attr_version.dev_attr.attr,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static const struct attribute_group as9726_32d_cpld_cpu_group = {
|
||
|
.attrs = as9726_32d_cpld_cpu_attributes,
|
||
|
};
|
||
|
|
||
|
static ssize_t show_interrupt(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 as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
int status = 0;
|
||
|
u8 reg = 0;
|
||
|
|
||
|
switch (attr->index)
|
||
|
{
|
||
|
case CPLD_INTR_1:
|
||
|
reg = 0x10;
|
||
|
break;
|
||
|
case CPLD_INTR_3:
|
||
|
reg = 0x10;
|
||
|
break;
|
||
|
case CPLD_INTR_2:
|
||
|
reg = 0x11;
|
||
|
break;
|
||
|
case CPLD_INTR_4:
|
||
|
reg = 0x11;
|
||
|
break;
|
||
|
default:
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
mutex_lock(&data->update_lock);
|
||
|
status = as9726_32d_cpld_read_internal(client, reg);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
|
||
|
return sprintf(buf, "0x%x\n", status);
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
static ssize_t show_status(struct device *dev, struct device_attribute *da,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
struct as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
int status = 0;
|
||
|
u8 reg = 0, mask = 0, revert = 0;
|
||
|
|
||
|
switch (attr->index) {
|
||
|
case MODULE_PRESENT_1 ... MODULE_PRESENT_8:
|
||
|
reg = 0x12;
|
||
|
mask = 0x1 << (attr->index - MODULE_PRESENT_1);
|
||
|
break;
|
||
|
case MODULE_PRESENT_9 ... MODULE_PRESENT_16:
|
||
|
reg = 0x13;
|
||
|
mask = 0x1 << (attr->index - MODULE_PRESENT_9);
|
||
|
break;
|
||
|
case MODULE_PRESENT_17 ... MODULE_PRESENT_24:
|
||
|
reg = 0x12;
|
||
|
mask = 0x1 << (attr->index - MODULE_PRESENT_17);
|
||
|
break;
|
||
|
case MODULE_PRESENT_25 ... MODULE_PRESENT_32:
|
||
|
reg = 0x13;
|
||
|
mask = 0x1 << (attr->index - MODULE_PRESENT_25);
|
||
|
break;
|
||
|
case MODULE_PRESENT_33:
|
||
|
reg = 0x20;
|
||
|
mask = 0x1;
|
||
|
break;
|
||
|
case MODULE_PRESENT_34:
|
||
|
reg = 0x20;
|
||
|
mask = 0x2;
|
||
|
break;
|
||
|
case MODULE_RXLOS_33:
|
||
|
reg = 0x26;
|
||
|
mask = 0x1;
|
||
|
break;
|
||
|
case MODULE_RXLOS_34:
|
||
|
reg = 0x26;
|
||
|
mask = 0x2;
|
||
|
break;
|
||
|
case MODULE_TXDISABLE_33:
|
||
|
reg = 0x21;
|
||
|
mask = 0x1;
|
||
|
break;
|
||
|
case MODULE_TXDISABLE_34:
|
||
|
reg = 0x21;
|
||
|
mask = 0x2;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (attr->index >= MODULE_PRESENT_1 && attr->index <= MODULE_PRESENT_34) {
|
||
|
revert = 1;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&data->update_lock);
|
||
|
status = as9726_32d_cpld_read_internal(client, reg);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
|
||
|
return sprintf(buf, "%d\n", revert ? !(status & mask) : !!(status & mask));
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static ssize_t set_tx_disable(struct device *dev, struct device_attribute *da,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
struct as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
long disable;
|
||
|
int status;
|
||
|
u8 reg = 0, mask = 0;
|
||
|
|
||
|
status = kstrtol(buf, 10, &disable);
|
||
|
if (status) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
switch (attr->index) {
|
||
|
case MODULE_TXDISABLE_33:
|
||
|
reg = 0x21;
|
||
|
mask = 0x1;
|
||
|
break;
|
||
|
case MODULE_TXDISABLE_34:
|
||
|
reg = 0x21;
|
||
|
mask = 0x2;
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Read current status */
|
||
|
mutex_lock(&data->update_lock);
|
||
|
status = as9726_32d_cpld_read_internal(client, reg);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/* Update tx_disable status */
|
||
|
if (disable) {
|
||
|
status |= mask;
|
||
|
}
|
||
|
else {
|
||
|
status &= ~mask;
|
||
|
}
|
||
|
|
||
|
status = as9726_32d_cpld_write_internal(client, reg, status);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return count;
|
||
|
|
||
|
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 as9726_32d_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 = as9726_32d_cpld_write_internal(client, addr, val);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return count;
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static void as9726_32d_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 as9726_32d_cpld_remove_client(struct i2c_client *client)
|
||
|
{
|
||
|
struct list_head *list_node = NULL;
|
||
|
struct cpld_client_node *cpld_node = NULL;
|
||
|
int found = 0;
|
||
|
|
||
|
mutex_lock(&list_lock);
|
||
|
|
||
|
list_for_each(list_node, &cpld_client_list)
|
||
|
{
|
||
|
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||
|
|
||
|
if (cpld_node->client == client) {
|
||
|
found = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (found) {
|
||
|
list_del(list_node);
|
||
|
kfree(cpld_node);
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&list_lock);
|
||
|
}
|
||
|
|
||
|
static ssize_t show_version(struct device *dev, struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
int val = 0;
|
||
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
|
||
|
val = i2c_smbus_read_byte_data(client, 0x1);
|
||
|
|
||
|
if (val < 0) {
|
||
|
dev_dbg(&client->dev, "cpld(0x%x) reg(0x1) err %d\n", client->addr, val);
|
||
|
}
|
||
|
|
||
|
return sprintf(buf, "0x%x\n", val);
|
||
|
}
|
||
|
|
||
|
static ssize_t get_mode_reset(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 as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
int status = 0;
|
||
|
u8 reg = 0, mask = 0;
|
||
|
|
||
|
switch (attr->index) {
|
||
|
case MODULE_RESET_1 ... MODULE_RESET_8:
|
||
|
reg = 0x14;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_1);
|
||
|
break;
|
||
|
case MODULE_RESET_9 ... MODULE_RESET_16:
|
||
|
reg = 0x15;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_9);
|
||
|
break;
|
||
|
case MODULE_RESET_17 ... MODULE_RESET_24:
|
||
|
reg = 0x14;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_17);
|
||
|
break;
|
||
|
case MODULE_RESET_25 ... MODULE_RESET_32:
|
||
|
reg = 0x15;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_25);
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
mutex_lock(&data->update_lock);
|
||
|
status = as9726_32d_cpld_read_internal(client, reg);
|
||
|
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
|
||
|
return sprintf(buf, "%d\r\n", !(status & mask));
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static ssize_t set_mode_reset(struct device *dev, struct device_attribute *da,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||
|
struct i2c_client *client = to_i2c_client(dev);
|
||
|
struct as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
long reset;
|
||
|
int status=0, val, error;
|
||
|
u8 reg = 0, mask = 0;
|
||
|
|
||
|
|
||
|
error = kstrtol(buf, 10, &reset);
|
||
|
if (error) {
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
switch (attr->index) {
|
||
|
case MODULE_RESET_1 ... MODULE_RESET_8:
|
||
|
reg = 0x14;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_1);
|
||
|
break;
|
||
|
case MODULE_RESET_9 ... MODULE_RESET_16:
|
||
|
reg = 0x15;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_9);
|
||
|
break;
|
||
|
case MODULE_RESET_17 ... MODULE_RESET_24:
|
||
|
reg = 0x14;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_17);
|
||
|
break;
|
||
|
case MODULE_RESET_25 ... MODULE_RESET_32:
|
||
|
reg = 0x15;
|
||
|
mask = 0x1 << (attr->index - MODULE_RESET_25);
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
mutex_lock(&data->update_lock);
|
||
|
|
||
|
status = as9726_32d_cpld_read_internal(client, reg);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/* Update lp_mode status */
|
||
|
if (reset)
|
||
|
{
|
||
|
val = status&(~mask);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
val =status | (mask);
|
||
|
}
|
||
|
|
||
|
status = as9726_32d_cpld_write_internal(client, reg, val);
|
||
|
if (unlikely(status < 0)) {
|
||
|
goto exit;
|
||
|
}
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return count;
|
||
|
|
||
|
exit:
|
||
|
mutex_unlock(&data->update_lock);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* I2C init/probing/exit functions
|
||
|
*/
|
||
|
static int as9726_32d_cpld_probe(struct i2c_client *client,
|
||
|
const struct i2c_device_id *id)
|
||
|
{
|
||
|
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||
|
struct as9726_32d_cpld_data *data;
|
||
|
int ret = -ENODEV;
|
||
|
int status;
|
||
|
const struct attribute_group *group = NULL;
|
||
|
|
||
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||
|
goto exit;
|
||
|
|
||
|
data = kzalloc(sizeof(struct as9726_32d_cpld_data), GFP_KERNEL);
|
||
|
if (!data) {
|
||
|
ret = -ENOMEM;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
i2c_set_clientdata(client, data);
|
||
|
mutex_init(&data->update_lock);
|
||
|
data->type = id->driver_data;
|
||
|
|
||
|
|
||
|
/* Register sysfs hooks */
|
||
|
switch (data->type) {
|
||
|
case as9726_32d_fpga:
|
||
|
group = &as9726_32d_fpga_group;
|
||
|
break;
|
||
|
case as9726_32d_cpld2:
|
||
|
group = &as9726_32d_cpld2_group;
|
||
|
/*Set interrupt mask to 0, and then can get intr from 0x8*/
|
||
|
status=as9726_32d_cpld_write_internal(client, 0x9, 0x0);
|
||
|
if (status < 0)
|
||
|
{
|
||
|
dev_dbg(&client->dev, "cpld2 reg 0x9 err %d\n", status);
|
||
|
}
|
||
|
break;
|
||
|
case as9726_32d_cpld3:
|
||
|
group = &as9726_32d_cpld3_group;
|
||
|
/*Set interrupt mask to 0, and then can get intr from 0x8*/
|
||
|
status=as9726_32d_cpld_write_internal(client, 0x9, 0x0);
|
||
|
if (status < 0)
|
||
|
{
|
||
|
dev_dbg(&client->dev, "cpld3 reg 0x9 err %d\n", status);
|
||
|
}
|
||
|
break;
|
||
|
case as9726_32d_cpld_cpu:
|
||
|
group = &as9726_32d_cpld_cpu_group;
|
||
|
/* Disable CPLD reset to avoid DUT will be reset.
|
||
|
*/
|
||
|
// status=as9726_32d_cpld_write_internal(client, 0x3, 0x0);
|
||
|
// if (status < 0)
|
||
|
// {
|
||
|
// dev_dbg(&client->dev, "cpu_cpld reg 0x65 err %d\n", status);
|
||
|
// }
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (group) {
|
||
|
ret = sysfs_create_group(&client->dev.kobj, group);
|
||
|
if (ret) {
|
||
|
goto exit_free;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
as9726_32d_cpld_add_client(client);
|
||
|
return 0;
|
||
|
|
||
|
exit_free:
|
||
|
kfree(data);
|
||
|
exit:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int as9726_32d_cpld_remove(struct i2c_client *client)
|
||
|
{
|
||
|
struct as9726_32d_cpld_data *data = i2c_get_clientdata(client);
|
||
|
const struct attribute_group *group = NULL;
|
||
|
|
||
|
as9726_32d_cpld_remove_client(client);
|
||
|
|
||
|
/* Remove sysfs hooks */
|
||
|
switch (data->type) {
|
||
|
case as9726_32d_fpga:
|
||
|
group = &as9726_32d_fpga_group;
|
||
|
break;
|
||
|
case as9726_32d_cpld2:
|
||
|
group = &as9726_32d_cpld2_group;
|
||
|
break;
|
||
|
case as9726_32d_cpld3:
|
||
|
group = &as9726_32d_cpld3_group;
|
||
|
break;
|
||
|
case as9726_32d_cpld_cpu:
|
||
|
group = &as9726_32d_cpld_cpu_group;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (group) {
|
||
|
sysfs_remove_group(&client->dev.kobj, group);
|
||
|
}
|
||
|
|
||
|
kfree(data);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int as9726_32d_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 as9726_32d_cpld_write_internal(struct i2c_client *client, u8 reg, u8 value)
|
||
|
{
|
||
|
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||
|
|
||
|
while (retry) {
|
||
|
status = i2c_smbus_write_byte_data(client, reg, value);
|
||
|
if (unlikely(status < 0)) {
|
||
|
msleep(I2C_RW_RETRY_INTERVAL);
|
||
|
retry--;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
int as9726_32d_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 = as9726_32d_cpld_read_internal(cpld_node->client, reg);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&list_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(as9726_32d_cpld_read);
|
||
|
|
||
|
int as9726_32d_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 = as9726_32d_cpld_write_internal(cpld_node->client, reg, value);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&list_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(as9726_32d_cpld_write);
|
||
|
|
||
|
static struct i2c_driver as9726_32d_cpld_driver = {
|
||
|
.driver = {
|
||
|
.name = "as9726_32d_cpld",
|
||
|
.owner = THIS_MODULE,
|
||
|
},
|
||
|
.probe = as9726_32d_cpld_probe,
|
||
|
.remove = as9726_32d_cpld_remove,
|
||
|
.id_table = as9726_32d_cpld_id,
|
||
|
};
|
||
|
|
||
|
static int __init as9726_32d_cpld_init(void)
|
||
|
{
|
||
|
mutex_init(&list_lock);
|
||
|
return i2c_add_driver(&as9726_32d_cpld_driver);
|
||
|
}
|
||
|
|
||
|
static void __exit as9726_32d_cpld_exit(void)
|
||
|
{
|
||
|
i2c_del_driver(&as9726_32d_cpld_driver);
|
||
|
}
|
||
|
|
||
|
MODULE_AUTHOR("Michael Shih <michael_shih@edge-core.com>");
|
||
|
MODULE_DESCRIPTION("Accton I2C CPLD driver");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
|
||
|
module_init(as9726_32d_cpld_init);
|
||
|
module_exit(as9726_32d_cpld_exit);
|
||
|
|