Initial common quanta module package.

This commit is contained in:
Jeffrey Townsend
2017-01-08 23:04:06 +00:00
parent e6df746ff0
commit 1b46831bae
6 changed files with 1360 additions and 1 deletions

View File

@@ -1 +1 @@
!include $ONL_TEMPLATES/no-arch-vendor-modules.yml ARCH=amd64 VENDOR=quanta
!include $ONL_TEMPLATES/arch-vendor-modules.yml ARCH=amd64 VENDOR=quanta KERNELS="onl-kernel-3.16-lts-x86-64-all:amd64"

View File

@@ -0,0 +1 @@
lib

View File

@@ -0,0 +1,6 @@
KERNELS := onl-kernel-3.16-lts-x86-64-all:amd64
KMODULES := $(wildcard *.c)
VENDOR := quanta
BASENAME := common
ARCH := x86_64
include $(ONL)/make/kmodule.mk

View File

@@ -0,0 +1,603 @@
/*
* A CPLD driver for monitor QSFP28 module I/O
*
* The CPLD is customize by Quanta for controlling QSFP28 module signals,
* they are RESET , INTERREPT , Module_Present, LPMODE
* Each CPLD control 16 modules, each module use 4 bits in register.
*
* Copyright (C) 2015 Quanta Inc.
*
* Author: Luffy Cheng <luffy.cheng@quantatw.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
#include <linux/ctype.h>
#include <linux/string.h>
static DEFINE_IDA(cpld_ida);
enum platform_type {
SFP = 0,
QSFP,
QSFP28,
NONE
};
static struct class *cpld_class = NULL;
struct sfp_data {
struct i2c_client *cpld_client;
char name[8];
char type[8];
u8 port_id;
u8 cpld_port;
};
struct cpld_data {
struct mutex lock;
struct device *port_dev[16];
struct sfp_data *port_data[16];
};
static int cpld_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int cpld_remove(struct i2c_client *client);
static const struct i2c_device_id cpld_id[] = {
{ "CPLD-SFP", SFP },
{ "CPLD-QSFP", QSFP },
{ "CPLD-QSFP28", QSFP28 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cpld_id);
static struct i2c_driver cpld_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "qci_cpld",
},
.probe = cpld_probe,
.remove = cpld_remove,
.id_table = cpld_id,
// .address_list = normal_i2c,
};
#define CPLD_ID_PREFIX "port-"
#define CPLD_ID_FORMAT CPLD_ID_PREFIX "%d"
#define RESET_MASK 0x08
#define INTERRUPT_MASK 0x04
#define MODULE_PRESENT_MASK 0x02
#define LPMODE_MASK 0x01
//#define I2C_MONITOR_MASK 0x01
static inline u8 get_group_cmd(u8 group)
{
//FIXME: if group cmd change
return (group + 1);
}
static inline u8 port_remapping(u8 phy_port)
{
/* FIXME: implement by hardware design */
/* The CPLD register port mapping is weird :
* MSB -------- LSB (word data)
* P3 P4 P1 P2 (per port 4 bits)
* For easy coding bit shift, we treat it as hw port swap
*/
return (phy_port % 2) ? (phy_port - 1) : (phy_port + 1);
}
static ssize_t get_reset(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value >>= (group_port * 4);
value &= RESET_MASK;
return sprintf(buf, "%d\n", value ? 1 : 0);
}
static ssize_t get_interrupt(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value >>= (group_port * 4);
value &= INTERRUPT_MASK;
return sprintf(buf, "%d\n", value ? 1 : 0);
}
static ssize_t get_module_present(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value >>= (group_port * 4);
value &= MODULE_PRESENT_MASK;
//FIXME: if present is not low active
return sprintf(buf, "%d\n", value ? 0 : 1);
}
static ssize_t get_lpmode(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value >>= (group_port * 4);
value &= LPMODE_MASK;
return sprintf(buf, "%d\n", value ? 1 : 0);
}
static ssize_t set_reset(struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
long disable;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
if (kstrtol(buf, 0, &disable))
return -EINVAL;
if ((disable != 1) && (disable != 0))
return -EINVAL;
// mutex_lock(&data->lock);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value &= ~(RESET_MASK << (group_port * 4));
if (disable)
value |= (RESET_MASK << (group_port * 4));
dev_dbg(&client->dev, "write group%d value= %x\n", group + 1, value);
i2c_smbus_write_word_data(client, get_group_cmd(group), (u16)value);
// mutex_unlock(&data->lock);
return count;
}
static ssize_t set_lpmode(struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = (u8)(data->cpld_port / 4);
u8 group_port = data->cpld_port % 4;
s32 value;
long disable;
dev_dbg(&client->dev, "port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group + 1, group_port + 1);
if (kstrtol(buf, 0, &disable))
return -EINVAL;
if ((disable != 1) && (disable != 0))
return -EINVAL;
// mutex_lock(&data->lock);
value = i2c_smbus_read_word_data(client, get_group_cmd(group));
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group + 1, value);
value &= ~(LPMODE_MASK << (group_port * 4));
if (disable)
value |= (LPMODE_MASK << (group_port * 4));
dev_dbg(&client->dev, "write group%d value= %x\n", group + 1, value);
i2c_smbus_write_word_data(client, get_group_cmd(group), (u16)value);
// mutex_unlock(&data->lock);
return count;
}
#if 0
static ssize_t get_led_enable(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = 3;
u8 group_port = data->cpld_port;
s32 value;
dev_dbg(&client->dev, "get_led_enable port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group, group_port + 1);
value = i2c_smbus_read_word_data(client, group);
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group, value);
value >>= (group_port);
value &= LED_ENABLE_MASK;
return sprintf(buf, "%d\n", value ? 1 : 0);
}
static ssize_t set_led_enable(struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
struct sfp_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = 3;
u8 group_port = data->cpld_port;
s32 value;
long disable;
dev_dbg(&client->dev, "set_led_enable port_id %d => cpld_port %d, group %d(%d)\n", data->port_id,
data->cpld_port + 1, group, group_port + 1);
if (kstrtol(buf, 0, &disable))
return -EINVAL;
if ((disable != 1) && (disable != 0))
return -EINVAL;
// mutex_lock(&data->lock);
value = i2c_smbus_read_word_data(client, group);
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group, value);
value &= ~(LED_ENABLE_MASK << group_port);
if (disable)
value |= (LED_ENABLE_MASK << group_port);
dev_dbg(&client->dev, "write group%d value= %x\n", group, value);
i2c_smbus_write_word_data(client, group, (u16)value);
// mutex_unlock(&data->lock);
return count;
}
#endif
#if 0
static ssize_t get_monitor_enable(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct i2c_monitor_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = 4;
s32 value;
value = i2c_smbus_read_word_data(client, group);
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group, value);
value &= I2C_MONITOR_MASK;
return sprintf(buf, "%d\n", value ? 1 : 0);
}
static ssize_t set_monitor_enable(struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
struct i2c_monitor_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->cpld_client;
u8 group = 4;
s32 value;
long disable;
if (kstrtol(buf, 0, &disable))
return -EINVAL;
if ((disable != 1) && (disable != 0))
return -EINVAL;
// mutex_lock(&data->lock);
value = i2c_smbus_read_word_data(client, group);
if (value < 0)
return -ENODEV;
dev_dbg(&client->dev, "read group%d value= %x\n", group, value);
value &= ~(I2C_MONITOR_MASK);
if (disable)
value |= (I2C_MONITOR_MASK);
dev_dbg(&client->dev, "write group%d value= %x\n", group, value);
i2c_smbus_write_word_data(client, group, (u16)value);
// mutex_unlock(&data->lock);
return count;
}
#endif
static DEVICE_ATTR(reset, S_IWUSR | S_IRUGO, get_reset, set_reset);
static DEVICE_ATTR(lpmode, S_IWUSR | S_IRUGO, get_lpmode, set_lpmode);
static DEVICE_ATTR(module_present, S_IRUGO, get_module_present, NULL);
static DEVICE_ATTR(interrupt, S_IRUGO, get_interrupt, NULL);
//static DEVICE_ATTR(led_enable, S_IWUSR | S_IRUGO, get_led_enable, set_led_enable);
//static DEVICE_ATTR(monitor_enable, S_IWUSR | S_IRUGO, get_monitor_enable, set_monitor_enable);
static const struct attribute *sfp_attrs[] = {
&dev_attr_reset.attr,
&dev_attr_lpmode.attr,
&dev_attr_module_present.attr,
&dev_attr_interrupt.attr,
// &dev_attr_led_enable.attr,
NULL,
};
static const struct attribute_group sfp_attr_group = {
.attrs = (struct attribute **) sfp_attrs,
};
#if 0
static const struct attribute *i2c_monitor_attrs[] = {
&dev_attr_monitor_enable.attr,
NULL,
};
static const struct attribute_group i2c_monitor_attr_group = {
.attrs = (struct attribute **) i2c_monitor_attrs,
};
#endif
static int cpld_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cpld_data *data;
struct sfp_data *port_data;
// struct i2c_monitor_data *monitor_data;
struct device *port_dev;
// struct device *i2c_dev;
int port_nr, i=0, err, max_port_num;
char name[I2C_NAME_SIZE], type[I2C_NAME_SIZE];
printk("cpld cpld_probe\n");
while(id->name[i])
{
name[i]=tolower(id->name[i]);
i++;
}
name[i]='\0';
strncpy(type,name+5,strlen(name)-5);
type[strlen(name)-5]='\0';
if (!cpld_class)
{
cpld_class = class_create(THIS_MODULE, name);
if (IS_ERR(cpld_class)) {
pr_err("couldn't create sysfs class\n");
return PTR_ERR(cpld_class);
}
}
data = devm_kzalloc(&client->dev, sizeof(struct cpld_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
if(!strcmp(client->name, "CPLD-QSFP28")){
max_port_num = 16;
}
else{
max_port_num = 4;
}
/* register sfp port data to sysfs */
for (i = 0; i < max_port_num; i++)
{
port_nr = ida_simple_get(&cpld_ida, 1, 99, GFP_KERNEL);
if (port_nr < 0)
return ERR_PTR(port_nr);
port_data = kzalloc(sizeof(struct sfp_data), GFP_KERNEL);
port_dev = device_create(cpld_class, &client->dev, MKDEV(0,0), port_data, CPLD_ID_FORMAT, port_nr);
if (IS_ERR(port_dev)) {
err = PTR_ERR(port_dev);
printk("err_status\n");
}
data->port_dev[i] = port_dev;
data->port_data[i] = port_data;
strcpy(port_data->type, type);
dev_info(&client->dev, "Register %s port-%d\n", port_data->type , port_nr);
/* FIXME: implement Logical/Physical port remapping */
//port_data->cpld_port = i;
port_data->cpld_port = port_remapping(i);
sprintf(port_data->name, "port-%d", port_nr);
port_data->port_id = port_nr;
dev_set_drvdata(port_dev, port_data);
port_dev->init_name = port_data->name;
port_data->cpld_client = client;
err = sysfs_create_group(&port_dev->kobj, &sfp_attr_group);
// if (status) printk("err status\n");
}
#if 0
/* register i2c monitor to sysfs */
monitor_data = kzalloc(sizeof(struct i2c_monitor_data), GFP_KERNEL);
i2c_dev = device_create(cpld_class, &client->dev, MKDEV(0, 0), monitor_data, "i2c_monitor");
if (IS_ERR(i2c_dev)) {
err = PTR_ERR(i2c_dev);
printk("i2c_dev err_status\n");
}
monitor_data->cpld_client = client;
err = sysfs_create_group(&i2c_dev->kobj, &i2c_monitor_attr_group);
if (err)
printk("err sysfs\n");
#endif
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
dev_info(&client->dev, "%s device found\n", client->name);
return 0;
//FIXME: implement error check
//exit_remove:
// sysfs_remove_group(&client->dev.kobj, &data->attrs);
return err;
}
/* FIXME: for older kernel doesn't with idr_is_empty function, implement here */
#if 1
static int idr_has_entry(int id, void *p, void *data)
{
return 1;
}
static bool cpld_idr_is_empty(struct idr *idp)
{
return !idr_for_each(idp, idr_has_entry, NULL);
}
#endif
static int cpld_remove(struct i2c_client *client)
{
struct cpld_data *data = i2c_get_clientdata(client);
int i, max_port_num;
// int id;
if(!strcmp(client->name, "CPLD-QSFP28")){
max_port_num = 16;
}
else{
max_port_num = 4;
}
for (i = (max_port_num - 1); i >= 0; i--)
{
dev_info(data->port_dev[i], "Remove %s port-%d\n", data->port_data[i]->type , data->port_data[i]->port_id);
device_unregister(data->port_dev[i]);
ida_simple_remove(&cpld_ida, data->port_data[i]->port_id);
kfree(data->port_data[i]);
}
if (cpld_idr_is_empty(&cpld_ida.idr))
class_destroy(cpld_class);
return 0;
}
module_i2c_driver(cpld_driver);
MODULE_AUTHOR("Luffy Cheng <luffy.cheng@quantatw.com>");
MODULE_DESCRIPTION("Quanta Switch QSFP28 CPLD driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,250 @@
/*
* <bsn.cl fy=2013 v=gpl>
*
* Copyright 2013, 2014 BigSwitch Networks, Inc.
*
* This program is free software; you can redistribute it
* and/or modify it under the terms ofthe GNU General Public License as
* published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
*
* </bsn.cl>
*
* A hwmon driver for the Quanta LY6
*/
#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/delay.h>
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
static const unsigned short normal_i2c[] = { 0x4E, I2C_CLIENT_END };
#define QUANTA_HWMON_REG_TEMP_INPUT_BASE 0x20
#define QUANTA_HWMON_REG_FAN_MODE 0x33
#define QUANTA_HWMON_REG_FAN_DIR 0x56
#define QUANTA_HWMON_REG_FAN_PWM_BASE 0x3C
#define QUANTA_HWMON_REG_FAN_INPUT_BASE 0x40
#define QUANTA_HWMON_FAN_MANUAL_MODE 1
#define QUANTA_HWMON_FAN_SMART_MODE 3
#define QUANTA_HWMON_NUM_FANS 6
struct quanta_hwmon_data {
struct device *hwmon_dev;
struct attribute_group attrs;
struct mutex lock;
};
enum quanta_hwmon_s {
quanta_ly6_hwmon,
quanta_ly6f_hwmon,
quanta_ly8_hwmon,
quanta_ly9_hwmon,
};
static int quanta_hwmon_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int quanta_hwmon_remove(struct i2c_client *client);
static const struct i2c_device_id quanta_hwmon_id[] = {
{ "quanta_ly6_hwmon", quanta_ly6_hwmon },
{ "quanta_ly6f_hwmon", quanta_ly6f_hwmon },
{ "quanta_ly8_hwmon", quanta_ly8_hwmon },
{ "quanta_ly9_hwmon", quanta_ly9_hwmon },
{ }
};
MODULE_DEVICE_TABLE(i2c, quanta_hwmon_id);
static struct i2c_driver quanta_hwmon_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "quanta_hwmon",
},
.probe = quanta_hwmon_probe,
.remove = quanta_hwmon_remove,
.id_table = quanta_hwmon_id,
.address_list = normal_i2c,
};
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct quanta_hwmon_data *data = i2c_get_clientdata(client);
int temp;
mutex_lock(&data->lock);
temp = i2c_smbus_read_byte_data(client,
QUANTA_HWMON_REG_TEMP_INPUT_BASE
+ attr->index);
mutex_unlock(&data->lock);
return sprintf(buf, "%d\n", 1000 * temp);
}
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct quanta_hwmon_data *data = i2c_get_clientdata(client);
int fan;
mutex_lock(&data->lock);
fan = i2c_smbus_read_word_swapped(client,
QUANTA_HWMON_REG_FAN_INPUT_BASE
+ 2 * attr->index);
mutex_unlock(&data->lock);
return sprintf(buf, "%d\n", fan);
}
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct quanta_hwmon_data *data = i2c_get_clientdata(client);
int pwm;
mutex_lock(&data->lock);
pwm = i2c_smbus_read_word_swapped(client,
QUANTA_HWMON_REG_FAN_PWM_BASE
+ 2 * attr->index);
mutex_unlock(&data->lock);
return sprintf(buf, "%d\n", pwm * 255 / 10000);
}
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct quanta_hwmon_data *data = i2c_get_clientdata(client);
long val;
int ret;
ret = kstrtol(buf, 10, &val);
if (ret)
return ret;
mutex_lock(&data->lock);
i2c_smbus_write_word_swapped(client,
QUANTA_HWMON_REG_FAN_PWM_BASE
+ 2 * attr->index, val * 10000 / 255);
mutex_unlock(&data->lock);
return count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);//For LY9
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5);
static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
static struct attribute *quanta_hwmon_attr[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan5_input.dev_attr.attr,
&sensor_dev_attr_fan6_input.dev_attr.attr,
&sensor_dev_attr_fan7_input.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
NULL
};
static struct attribute *quanta_hwmon_attr_6temps_3fans[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan5_input.dev_attr.attr,
&sensor_dev_attr_fan6_input.dev_attr.attr,
&sensor_dev_attr_fan7_input.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
NULL
};
static int quanta_hwmon_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct quanta_hwmon_data *data;
int err;
data = devm_kzalloc(&client->dev, sizeof(struct quanta_hwmon_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
dev_info(&client->dev, "%s chip found\n", client->name);
if(!strcmp(client->name, "quanta_ly9_hwmon")){
data->attrs.attrs = quanta_hwmon_attr_6temps_3fans;
}
else{
data->attrs.attrs = quanta_hwmon_attr;
}
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
if (err)
return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
}
i2c_smbus_write_byte_data(client,
QUANTA_HWMON_REG_FAN_MODE,
QUANTA_HWMON_FAN_SMART_MODE);
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &data->attrs);
return err;
}
static int quanta_hwmon_remove(struct i2c_client *client)
{
struct quanta_hwmon_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &data->attrs);
return 0;
}
module_i2c_driver(quanta_hwmon_driver);
MODULE_AUTHOR("QCT Technical <support@quantaqct.com>");
MODULE_DESCRIPTION("Quanta LY6 hardware monitor driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,499 @@
/*
* Quanta Switch platform driver
*
*
* Copyright (C) 2014 Quanta Computer inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca953x.h>
#include <linux/i2c/pca954x.h>
#include <linux/gpio.h>
#include <linux/i2c-mux-gpio.h>
#include <linux/platform_device.h>
#include <linux/dmi.h>
#define DRIVER_NAME "quanta-switch"
#define VENDOR_NAME "Quanta"
#define PRODUCT_NAME_LY6 "Quanta LY6"
#define PRODUCT_NAME_LY8 "Quanta LY8"
#define PRODUCT_NAME_LY9 "Quanta LY9"
#define MAX_I2C_CLIENTS 512
#define I2C_GPIO_BASE 0x80
#define XSTR(x) STR(X)
#define STR(x) #x
enum i2c_types {
i2c_type_spd,
i2c_type_rtc,
i2c_type_pca9546,
i2c_type_pca9548,
i2c_type_pca9554,
i2c_type_pca9555,
i2c_type_pca9698,
i2c_type_quanta_ly6_i2c_mux,
i2c_type_qci_cpld_4_ports,
i2c_type_24c02,
i2c_type_pmbus,
i2c_type_emerson700,
i2c_type_quanta_ly6_hwmon,
i2c_type_quanta_ly8_hwmon,
i2c_type_quanta_ly9_hwmon,
};
char *i2c_type_names[] = {
"spd",
"ds1339",
"pca9546",
"pca9548",
"pca9554",
"pca9555",
"pca9698",
"quanta_ly6_i2c_mux",
"CPLD-QSFP",
"24c02",
"pmbus",
"emerson700",
"quanta_ly6_hwmon",
"quanta_ly8_hwmon",
"quanta_ly9_hwmon",
};
struct i2c_init_data {
int parent_bus;
int type;
int addr;
int busno;
int gpio_base;
char name[I2C_NAME_SIZE];
};
static struct i2c_init_data quanta_ly6_i2c_init_data[] = {
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x71, .busno = 0x02, .name = "PCA9546(CPU)\0" },
{ .parent_bus = (0x02 + 0), .type = i2c_type_pca9555, .addr = 0x20, .gpio_base = 0x40, .name = "PCA9555_1(CPU)\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_quanta_ly6_hwmon, .addr = 0x4e, .name = "PSoc\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_spd, .addr = 0x52, .name = "SPD\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_rtc, .addr = 0x68, .name = "RTC\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9548, .addr = 0x77, .busno = 0x10, .name = "PCA9548_1\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9548, .addr = 0x73, .busno = 0x20, .name = "PCA9548(SFP_1-8)\0" },
{ .parent_bus = (0x10 + 1), .type = i2c_type_pca9548, .addr = 0x74, .busno = 0x28, .name = "PCA9548(SFP_9-16)\0" },
{ .parent_bus = (0x10 + 2), .type = i2c_type_pca9548, .addr = 0x75, .busno = 0x30, .name = "PCA9548(SFP_17-24)\0" },
{ .parent_bus = (0x10 + 3), .type = i2c_type_pca9548, .addr = 0x76, .busno = 0x38, .name = "PCA9548(SFP_25-32)\0" },
{ .parent_bus = (0x10 + 4), .type = i2c_type_quanta_ly6_i2c_mux, .addr = 0x3a, .name = "quanta_ly6_i2c_mux1\0" },
{ .parent_bus = (0x10 + 5), .type = i2c_type_quanta_ly6_i2c_mux, .addr = 0x3b, .name = "quanta_ly6_i2c_mux2\0" },
{ .parent_bus = (0x10 + 6), .type = i2c_type_pca9555, .addr = 0x24, .name = "PCA9555_3(FAN)\0" },
{ .parent_bus = (0x10 + 7), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555_4(QSFP_EN)\0" },
{ .parent_bus = (0x20 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_1_EEPROM\0" },
{ .parent_bus = (0x20 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_2_EEPROM\0" },
{ .parent_bus = (0x20 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_3_EEPROM\0" },
{ .parent_bus = (0x20 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_4_EEPROM\0" },
{ .parent_bus = (0x20 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_5_EEPROM\0" },
{ .parent_bus = (0x20 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_6_EEPROM\0" },
{ .parent_bus = (0x20 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_7_EEPROM\0" },
{ .parent_bus = (0x20 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_8_EEPROM\0" },
{ .parent_bus = (0x28 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_9_EEPROM\0" },
{ .parent_bus = (0x28 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_10_EEPROM\0" },
{ .parent_bus = (0x28 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_11_EEPROM\0" },
{ .parent_bus = (0x28 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_12_EEPROM\0" },
{ .parent_bus = (0x28 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_13_EEPROM\0" },
{ .parent_bus = (0x28 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_14_EEPROM\0" },
{ .parent_bus = (0x28 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_15_EEPROM\0" },
{ .parent_bus = (0x28 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_16_EEPROM\0" },
{ .parent_bus = (0x30 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_17_EEPROM\0" },
{ .parent_bus = (0x30 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_18_EEPROM\0" },
{ .parent_bus = (0x30 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_19_EEPROM\0" },
{ .parent_bus = (0x30 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_20_EEPROM\0" },
{ .parent_bus = (0x30 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_21_EEPROM\0" },
{ .parent_bus = (0x30 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_22_EEPROM\0" },
{ .parent_bus = (0x30 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_23_EEPROM\0" },
{ .parent_bus = (0x30 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_24_EEPROM\0" },
{ .parent_bus = (0x38 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_25_EEPROM\0" },
{ .parent_bus = (0x38 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_26_EEPROM\0" },
{ .parent_bus = (0x38 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_27_EEPROM\0" },
{ .parent_bus = (0x38 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_28_EEPROM\0" },
{ .parent_bus = (0x38 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_29_EEPROM\0" },
{ .parent_bus = (0x38 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_30_EEPROM\0" },
{ .parent_bus = (0x38 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_31_EEPROM\0" },
{ .parent_bus = (0x38 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_32_EEPROM\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x72, .busno = 0x18, .name = "PCA9546\0" },
{ .parent_bus = (0x18 + 0), .type = i2c_type_pmbus, .addr = 0x58, .name = "PSU_1\0" }, /* RPSU 1 */
{ .parent_bus = (0x18 + 1), .type = i2c_type_pmbus, .addr = 0x59, .name = "PSU_2\0" }, /* RPSU 2 */
{ .parent_bus = (0x18 + 2), .type = i2c_type_pca9555, .addr = 0x26, .name = "PCA9555_PSU\0" },
{ .parent_bus = (0x18 + 3), .type = i2c_type_24c02, .addr = 0x54, .name = "Board_EEPROM\0" },
};
static struct i2c_init_data quanta_ly8_i2c_init_data[] = {
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x71, .busno = 0x02, .name = "PCA9546(CPU)\0" },
{ .parent_bus = (0x02 + 0), .type = i2c_type_pca9555, .addr = 0x20, .gpio_base = 0x40, .name = "PCA9555_1(CPU)\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_quanta_ly8_hwmon, .addr = 0x4e, .name = "PSoc\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_spd, .addr = 0x52, .name = "SPD\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_rtc, .addr = 0x68, .name = "RTC\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9548, .addr = 0x77, .busno = 0x10, .name = "PCA9548_1\0" },
{ .parent_bus = (0x10 + 1), .type = i2c_type_pca9548, .addr = 0x73, .busno = 0x20, .name = "PCA9548(SFP_1-8)\0" },
{ .parent_bus = (0x10 + 1), .type = i2c_type_pca9548, .addr = 0x74, .busno = 0x28, .name = "PCA9548(SFP_9-16)\0" },
{ .parent_bus = (0x10 + 1), .type = i2c_type_pca9548, .addr = 0x76, .busno = 0x30, .name = "PCA9548(SFP_17-24)\0" },
{ .parent_bus = (0x10 + 2), .type = i2c_type_pca9548, .addr = 0x73, .busno = 0x38, .name = "PCA9548(SFP_25-32)\0" },
{ .parent_bus = (0x10 + 2), .type = i2c_type_pca9548, .addr = 0x74, .busno = 0x40, .name = "PCA9548(SFP_33-40)\0" },
{ .parent_bus = (0x10 + 2), .type = i2c_type_pca9548, .addr = 0x75, .busno = 0x48, .name = "PCA9548(SFP_41-48)\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9554, .addr = 0x25, .name = "PCA9554(PCA9698INT)\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9555, .addr = 0x24, .name = "PCA9555_3(FAN)\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555_4(QSFP_EN)\0" },
{ .parent_bus = (0x20 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_1_EEPROM\0" },
{ .parent_bus = (0x20 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_2_EEPROM\0" },
{ .parent_bus = (0x20 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_3_EEPROM\0" },
{ .parent_bus = (0x20 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_4_EEPROM\0" },
{ .parent_bus = (0x20 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_5_EEPROM\0" },
{ .parent_bus = (0x20 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_6_EEPROM\0" },
{ .parent_bus = (0x20 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_7_EEPROM\0" },
{ .parent_bus = (0x20 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_8_EEPROM\0" },
{ .parent_bus = (0x28 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_9_EEPROM\0" },
{ .parent_bus = (0x28 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_10_EEPROM\0" },
{ .parent_bus = (0x28 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_11_EEPROM\0" },
{ .parent_bus = (0x28 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_12_EEPROM\0" },
{ .parent_bus = (0x28 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_13_EEPROM\0" },
{ .parent_bus = (0x28 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_14_EEPROM\0" },
{ .parent_bus = (0x28 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_15_EEPROM\0" },
{ .parent_bus = (0x28 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_16_EEPROM\0" },
{ .parent_bus = (0x30 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_17_EEPROM\0" },
{ .parent_bus = (0x30 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_18_EEPROM\0" },
{ .parent_bus = (0x30 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_19_EEPROM\0" },
{ .parent_bus = (0x30 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_20_EEPROM\0" },
{ .parent_bus = (0x30 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_21_EEPROM\0" },
{ .parent_bus = (0x30 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_22_EEPROM\0" },
{ .parent_bus = (0x30 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_23_EEPROM\0" },
{ .parent_bus = (0x30 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_24_EEPROM\0" },
{ .parent_bus = (0x38 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_25_EEPROM\0" },
{ .parent_bus = (0x38 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_26_EEPROM\0" },
{ .parent_bus = (0x38 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_27_EEPROM\0" },
{ .parent_bus = (0x38 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_28_EEPROM\0" },
{ .parent_bus = (0x38 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_29_EEPROM\0" },
{ .parent_bus = (0x38 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_30_EEPROM\0" },
{ .parent_bus = (0x38 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_31_EEPROM\0" },
{ .parent_bus = (0x38 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_32_EEPROM\0" },
{ .parent_bus = (0x40 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_33_EEPROM\0" },
{ .parent_bus = (0x40 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_34_EEPROM\0" },
{ .parent_bus = (0x40 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_35_EEPROM\0" },
{ .parent_bus = (0x40 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_36_EEPROM\0" },
{ .parent_bus = (0x40 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_37_EEPROM\0" },
{ .parent_bus = (0x40 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_38_EEPROM\0" },
{ .parent_bus = (0x40 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_39_EEPROM\0" },
{ .parent_bus = (0x40 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_40_EEPROM\0" },
{ .parent_bus = (0x48 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_41_EEPROM\0" },
{ .parent_bus = (0x48 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_42_EEPROM\0" },
{ .parent_bus = (0x48 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_43_EEPROM\0" },
{ .parent_bus = (0x48 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_44_EEPROM\0" },
{ .parent_bus = (0x48 + 4), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_45_EEPROM\0" },
{ .parent_bus = (0x48 + 5), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_46_EEPROM\0" },
{ .parent_bus = (0x48 + 6), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_47_EEPROM\0" },
{ .parent_bus = (0x48 + 7), .type = i2c_type_24c02, .addr = 0x50, .name = "SFP_48_EEPROM\0" },
{ .parent_bus = (0x10 + 3), .type = i2c_type_pca9698, .addr = 0x23, .name = "PCA9698(SFP_1-8)\0" },
{ .parent_bus = (0x10 + 3), .type = i2c_type_pca9698, .addr = 0x21, .name = "PCA9698(SFP_9-16)\0" },
{ .parent_bus = (0x10 + 3), .type = i2c_type_pca9698, .addr = 0x22, .name = "PCA9698(SFP_17-24)\0" },
{ .parent_bus = (0x10 + 4), .type = i2c_type_pca9698, .addr = 0x23, .name = "PCA9698(SFP_25-32)\0" },
{ .parent_bus = (0x10 + 4), .type = i2c_type_pca9698, .addr = 0x24, .name = "PCA9698(SFP_33-40)\0" },
{ .parent_bus = (0x10 + 4), .type = i2c_type_pca9698, .addr = 0x25, .name = "PCA9698(SFP_41-48)\0" },
{ .parent_bus = (0x10 + 5), .type = i2c_type_pca9548, .addr = 0x76, .busno = 0x50, .name = "PCA9548_8\0" },
{ .parent_bus = (0x50 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_1_EEPROM\0" },
{ .parent_bus = (0x50 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_2_EEPROM\0" },
{ .parent_bus = (0x50 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_3_EEPROM\0" },
{ .parent_bus = (0x50 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_4_EEPROM\0" },
{ .parent_bus = (0x10 + 5), .type = i2c_type_pca9555, .addr = 0x24, .name = "PCA9555_1(LED)\0" },
{ .parent_bus = (0x10 + 6), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555_2(QSFP)\0" },
/* QSFP+ DB */
{ .parent_bus = (0x10 + 7), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555(QDB)\0" },
{ .parent_bus = (0x10 + 7), .type = i2c_type_pca9546, .addr = 0x76, .busno = 0x58, .name = "PCA9546(QDB)\0" },
{ .parent_bus = (0x58 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QDB_QSFP_1_EEPROM\0" },
{ .parent_bus = (0x58 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QDB_QSFP_2_EEPROM\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x72, .busno = 0x18, .name = "PCA9546\0" },
{ .parent_bus = (0x18 + 0), .type = i2c_type_emerson700, .addr = 0x6f, .name = "PSU_1\0" }, /* RPSU 1 */
{ .parent_bus = (0x18 + 1), .type = i2c_type_emerson700, .addr = 0x69, .name = "PSU_2\0" }, /* RPSU 2 */
{ .parent_bus = (0x18 + 2), .type = i2c_type_pca9555, .addr = 0x26, .name = "PCA9555_5(PSU)\0" },
{ .parent_bus = (0x18 + 3), .type = i2c_type_24c02, .addr = 0x54, .name = "Board_EEPROM\0" },
};
static struct i2c_init_data quanta_ly9_i2c_init_data[] = {
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x71, .busno = 0x02, .name = "PCA9546(CPU)\0" },
{ .parent_bus = (0x02 + 0), .type = i2c_type_pca9555, .addr = 0x20, .gpio_base = 0x40, .name = "PCA9555_1(CPU)\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_quanta_ly9_hwmon, .addr = 0x4e, .name = "PSoc\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_spd, .addr = 0x52, .name = "SPD\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_rtc, .addr = 0x68, .name = "RTC\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9548, .addr = 0x77, .busno = 0x10, .name = "PCA9548APW1\0" },
{ .parent_bus = (0x10 + 5), .type = i2c_type_pca9548, .addr = 0x76, .busno = 0x20, .name = "PCA9548APW2\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_qci_cpld_4_ports, .addr = 0x3a, .name = "quanta_ly9_cpld\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9555, .addr = 0x24, .name = "PCA9555_3(FAN)\0" },
{ .parent_bus = (0x10 + 0), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555_4(QSFP_EN)\0" },
{ .parent_bus = (0x20 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_49_EEPROM\0" },
{ .parent_bus = (0x20 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_50_EEPROM\0" },
{ .parent_bus = (0x20 + 2), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_51_EEPROM\0" },
{ .parent_bus = (0x20 + 3), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_52_EEPROM\0" },
/* QSFP+ DB */
{ .parent_bus = (0x10 + 7), .type = i2c_type_pca9555, .addr = 0x23, .name = "PCA9555(QDB)\0" },
{ .parent_bus = (0x10 + 7), .type = i2c_type_pca9546, .addr = 0x76, .busno = 0x30, .name = "PCA9546(QDB)\0" },
{ .parent_bus = (0x30 + 0), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_53_EEPROM\0" },
{ .parent_bus = (0x30 + 1), .type = i2c_type_24c02, .addr = 0x50, .name = "QSFP_54_EEPROM\0" },
{ .parent_bus = (0x00 + 0), .type = i2c_type_pca9546, .addr = 0x72, .busno = 0x18, .name = "PCA9546\0" },
{ .parent_bus = (0x18 + 0), .type = i2c_type_emerson700, .addr = 0x6f, .name = "PSU_1\0" }, /* RPSU 1 */
{ .parent_bus = (0x18 + 1), .type = i2c_type_emerson700, .addr = 0x69, .name = "PSU_2\0" }, /* RPSU 2 */
{ .parent_bus = (0x18 + 2), .type = i2c_type_pca9555, .addr = 0x26, .name = "PCA9555_1(PSU)\0" },
{ .parent_bus = (0x18 + 3), .type = i2c_type_24c02, .addr = 0x54, .name = "Board_EEPROM\0" },
};
static inline struct pca954x_platform_data *pca954x_platform_data_get(int type, int busno) {
static struct pca954x_platform_mode platform_modes[8];
static struct pca954x_platform_data platform_data;
int num_modes, i;
switch(type) {
case i2c_type_pca9546:
num_modes = 4;
break;
case i2c_type_pca9548:
num_modes = 8;
break;
default:
return (struct pca954x_platform_data *) NULL;
break;
}
for(i=0;i<num_modes;i++) {
platform_modes[i] = (struct pca954x_platform_mode) {
.adap_id = (busno + i),
.deselect_on_exit = 1,
};
}
platform_data = (struct pca954x_platform_data) {
.modes = platform_modes,
.num_modes = num_modes,
};
return &platform_data;
}
static int base_gpio_num = I2C_GPIO_BASE;
static inline struct pca953x_platform_data *pca953x_platform_data_get(int type, int gpio_base) {
static struct pca953x_platform_data platform_data;
int num_gpios, num_gpio;
switch(type) {
case i2c_type_pca9554:
num_gpios = 0x8;
break;
case i2c_type_pca9555:
num_gpios = 0x10;
break;
case i2c_type_pca9698:
num_gpios = 0x28;
break;
case i2c_type_quanta_ly6_i2c_mux:
num_gpios = 0x40;
break;
default:
return (struct pca953x_platform_data *) NULL;
break;
}
if(gpio_base == 0) {
num_gpio = base_gpio_num;
base_gpio_num += num_gpios;
}
else {
num_gpio = gpio_base;
}
platform_data = (struct pca953x_platform_data) {
.gpio_base = num_gpio,
};
return &platform_data;
}
static inline struct i2c_board_info *i2c_board_info_get(struct i2c_init_data data) {
struct pca954x_platform_data *mux_platform_data;
struct pca953x_platform_data *gpio_platform_data;
static struct i2c_board_info board_info;
switch(data.type) {
case i2c_type_pca9546:
case i2c_type_pca9548:
mux_platform_data = pca954x_platform_data_get(data.type, data.busno);
if(mux_platform_data == NULL) {
return (struct i2c_board_info *) NULL;
}
board_info = (struct i2c_board_info) {
.platform_data = mux_platform_data,
};
break;
case i2c_type_pca9554:
case i2c_type_pca9555:
case i2c_type_pca9698:
case i2c_type_quanta_ly6_i2c_mux:
gpio_platform_data = pca953x_platform_data_get(data.type, data.gpio_base);
if(gpio_platform_data == NULL) {
return (struct i2c_board_info *) NULL;
}
board_info = (struct i2c_board_info) {
.platform_data = gpio_platform_data,
};
break;
case i2c_type_rtc:
case i2c_type_spd:
case i2c_type_24c02:
case i2c_type_pmbus:
case i2c_type_emerson700:
case i2c_type_quanta_ly6_hwmon:
case i2c_type_quanta_ly8_hwmon:
case i2c_type_quanta_ly9_hwmon:
case i2c_type_qci_cpld_4_ports:
board_info = (struct i2c_board_info) {
.platform_data = (void *) NULL,
};
break;
default:
return (struct i2c_board_info *) NULL;
break;
}
board_info.addr = data.addr;
strcpy(board_info.type, i2c_type_names[data.type]);
return &board_info;
}
static struct platform_driver quanta_switch_platform_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static struct i2c_init_data *init_data;
static int init_data_size;
static struct i2c_client *registered_i2c_clients[MAX_I2C_CLIENTS];
static char* platform = "None.";
module_param(platform, charp, 0000);
MODULE_PARM_DESC(platform, "Set platform.");
static int __init quanta_switch_init(void)
{
char const *vendor, *product;
struct i2c_adapter *adapter;
struct i2c_board_info *board_info;
int i;
int ret = 0;
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
product = dmi_get_system_info(DMI_PRODUCT_NAME);
if((!strcmp(vendor, VENDOR_NAME) &&
(strstr(product, PRODUCT_NAME_LY6) != NULL)) ||
(!strcmp(platform, "x86-64-quanta-ly6-rangeley"))) {
init_data = quanta_ly6_i2c_init_data;
init_data_size = ARRAY_SIZE(quanta_ly6_i2c_init_data);
}
else if((!strcmp(vendor, VENDOR_NAME) &&
(strstr(product, PRODUCT_NAME_LY8) != NULL)) ||
(!strcmp(platform, "x86-64-quanta-ly8-rangeley"))) {
init_data = quanta_ly8_i2c_init_data;
init_data_size = ARRAY_SIZE(quanta_ly8_i2c_init_data);
}
else if((!strcmp(vendor, VENDOR_NAME) &&
(strstr(product, PRODUCT_NAME_LY9) != NULL)) ||
(!strcmp(platform, "x86-64-quanta-ly9-rangeley"))) {
init_data = quanta_ly9_i2c_init_data;
init_data_size = ARRAY_SIZE(quanta_ly9_i2c_init_data);
}
else {
return -ENODEV;
}
ret = platform_driver_register(&quanta_switch_platform_driver);
if (ret < 0)
return ret;
/**
* Register I2C devices on new buses
*/
for(i = 0; i < init_data_size; i++) {
adapter = i2c_get_adapter(init_data[i].parent_bus);
board_info = i2c_board_info_get(init_data[i]);
pr_info("register i2c_new_device\n\t%s for bus 0x%x:0x%x. ",
init_data[i].name, init_data[i].parent_bus, init_data[i].addr);
if((registered_i2c_clients[i] = i2c_new_device(adapter, board_info)) == NULL) {
pr_err("%s: i2c_new_device for bus 0x%x:0x%x failed.",
__FUNCTION__, init_data[i].parent_bus, init_data[i].addr);
}
}
return 0;
}
static void __exit quanta_switch_cleanup(void)
{
int i;
/**
* Unregister I2C devices
*/
for(i = 0; i < init_data_size; i++) {
if(registered_i2c_clients[i] != NULL) {
i2c_unregister_device(registered_i2c_clients[init_data_size-(i+1)]);
}
}
platform_driver_unregister(&quanta_switch_platform_driver);
}
module_init(quanta_switch_init);
module_exit(quanta_switch_cleanup);
MODULE_AUTHOR("Audi Hsu (audi.hsu@quantatw.com)");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("Quanta Switch");
MODULE_LICENSE("GPL");