Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[device/celestica-blackstone]: Update switch CPLD driver follow FPGA i2c buses #1779

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
obj-m := baseboard-lpc.o mc24lc64t.o cls-fpga.o xcvr-cls.o switch_cpld.o cls-i2c-ocores.o
obj-m := baseboard-lpc.o mc24lc64t.o cls-fpga.o xcvr-cls.o misc_cpld.o cls-i2c-ocores.o
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/*
* switch_cpld.c - i2c driver for Blackstone DP switchboard CPLD1/CPLD2
* misc_cpld.c - i2c driver for Blackstone MISC CPLD1/CPLD2
* provides sysfs interfaces to access CPLD register and control port LEDs
*
* Author: Budsakol Sirirattanasakul
*
* Copyright (C) 2021 Celestica Corp.
*
* This program is free software; you can redistribute it and/or modify
Expand All @@ -17,27 +15,24 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>

#define CPLD1_ADDR 0x30
#define CPLD2_ADDR 0x31
// #define CPLD2_ADDR 0x31
mudsut4ke marked this conversation as resolved.
Show resolved Hide resolved

#define SCRATCH_ADDR 0x01
#define LED_OPMODE 0x09
#define LED_TEST 0x0A

struct switch_cpld_data {
struct mutex lock;
struct misc_cpld_data {
struct i2c_client *client;
struct i2c_client *client2;
uint8_t read_addr;
};

static ssize_t getreg_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int value;

Expand All @@ -53,7 +48,7 @@ static ssize_t getreg_store(struct device *dev, struct device_attribute *attr,
{
uint8_t value;
ssize_t status;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);

status = kstrtou8(buf, 0, &value);
if (status != 0)
Expand All @@ -69,7 +64,7 @@ static ssize_t setreg_store(struct device *dev, struct device_attribute *attr,
{
uint8_t addr, value;
ssize_t status;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
char *tok;

Expand All @@ -96,7 +91,7 @@ static ssize_t setreg_store(struct device *dev, struct device_attribute *attr,
static ssize_t scratch_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int value;

Expand All @@ -112,7 +107,7 @@ static ssize_t scratch_store(struct device *dev, struct device_attribute *attr,
{
uint8_t value;
ssize_t status;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;

status = kstrtou8(buf, 0, &value);
Expand All @@ -128,30 +123,25 @@ DEVICE_ATTR_RW(getreg);
DEVICE_ATTR_WO(setreg);
DEVICE_ATTR_RW(scratch);

static struct attribute *switch_cpld_attrs[] = {
static struct attribute *misc_cpld_attrs[] = {
&dev_attr_getreg.attr,
&dev_attr_setreg.attr,
&dev_attr_scratch.attr,
NULL,
};
ATTRIBUTE_GROUPS(switch_cpld);
ATTRIBUTE_GROUPS(misc_cpld);

static ssize_t port_led_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int led_mode_1, led_mode_2;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client1 = data->client;
struct i2c_client *client2 = data->client2;

led_mode_1 = i2c_smbus_read_byte_data(client1, LED_OPMODE);
if (led_mode_1 < 0)
return led_mode_1;

led_mode_2 = i2c_smbus_read_byte_data(client2, LED_OPMODE);
if (led_mode_2 < 0)
return led_mode_2;

return sprintf(buf, "%s %s\n",
led_mode_1 ? "test" : "normal",
led_mode_2 ? "test" : "normal");
Expand All @@ -163,9 +153,8 @@ static ssize_t port_led_mode_store(struct device *dev,
{
int status;
uint8_t led_mode;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client1 = data->client;
struct i2c_client *client2 = data->client2;

if (sysfs_streq(buf, "test"))
led_mode = 0x01;
Expand All @@ -179,30 +168,20 @@ static ssize_t port_led_mode_store(struct device *dev,
return status;
}

status = i2c_smbus_write_byte_data(client2, LED_OPMODE, led_mode);
if (status != 0) {
return status;
}

return size;
}

static ssize_t port_led_color_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int led_color1, led_color2;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client1 = data->client;
struct i2c_client *client2 = data->client2;

led_color1 = i2c_smbus_read_byte_data(client1, LED_TEST);
if (led_color1 < 0)
return led_color1;

led_color2 = i2c_smbus_read_byte_data(client2, LED_TEST);
if (led_color2 < 0)
return led_color2;

return sprintf(buf, "%s %s\n",
led_color1 == 0x02 ? "green" :
led_color1 == 0x01 ? "amber" : "off",
Expand All @@ -222,9 +201,8 @@ static ssize_t port_led_color_store(struct device *dev,
{
int status;
uint8_t led_color1, led_color2;
struct switch_cpld_data *data = dev_get_drvdata(dev);
struct misc_cpld_data *data = dev_get_drvdata(dev);
struct i2c_client *client1 = data->client;
struct i2c_client *client2 = data->client2;

if (sysfs_streq(buf, "off")) {
led_color1 = 0x07;
Expand Down Expand Up @@ -259,11 +237,6 @@ static ssize_t port_led_color_store(struct device *dev,
return status;
}

status = i2c_smbus_write_byte_data(client2, LED_TEST, led_color2);
if (status != 0) {
return status;
}

return size;
}

Expand All @@ -280,149 +253,88 @@ static struct attribute_group sff_led_groups = {
.attrs = sff_led_attrs,
};

static int switch_cpld_probe(struct i2c_client *client,
static int misc_cpld_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct switch_cpld_data *drvdata1, *drvdata2;
struct device *hwmon_dev1, *hwmon_dev2;
struct i2c_client *client2;

if (client->addr != CPLD1_ADDR) {
dev_err(&client->dev, "probe, bad i2c addr: 0x%x\n",
client->addr);
err = -EINVAL;
goto exit;
}
struct misc_cpld_data *drvdata1;
struct device *hwmon_dev1;

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -EPFNOSUPPORT;

/* CPLD1 */
mudsut4ke marked this conversation as resolved.
Show resolved Hide resolved
drvdata1 = devm_kzalloc(&client->dev,
sizeof(struct switch_cpld_data), GFP_KERNEL);
sizeof(struct misc_cpld_data), GFP_KERNEL);

if (!drvdata1) {
err = -ENOMEM;
goto exit;
}

mutex_init(&drvdata1->lock);
drvdata1->client = client;
drvdata1->read_addr = 0x00;
i2c_set_clientdata(client, drvdata1);
hwmon_dev1 = devm_hwmon_device_register_with_groups(&client->dev,
"CPLD1",
"MISC_CPLD",
drvdata1,
switch_cpld_groups);
misc_cpld_groups);

if (IS_ERR(hwmon_dev1)) {
err = PTR_ERR(hwmon_dev1);
goto exit;
}

err = sysfs_create_link(&client->dev.kobj, &hwmon_dev1->kobj, "CPLD1");
err = sysfs_create_link(&client->dev.kobj, &hwmon_dev1->kobj, "MISC_CPLD");
if (err) {
goto exit;
}

/* CPLD2 */
drvdata2 = devm_kzalloc(&client->dev,
sizeof(struct switch_cpld_data), GFP_KERNEL);

if (!drvdata2) {
err = -ENOMEM;
goto err_link;
}

client2 = i2c_new_dummy(client->adapter, CPLD2_ADDR);
if (!client2) {
dev_err(&client->dev, "address 0x%02x unavailable\n",
CPLD2_ADDR);
err = -EADDRINUSE;
goto err_link;
}

mutex_init(&drvdata2->lock);
drvdata2->read_addr = 0x00;
drvdata2->client = client2;
i2c_set_clientdata(client2, drvdata2);

/* attach client2 to be client2 of CPLD1
for later use on port led sysfs */
drvdata1->client2 = client2;

hwmon_dev2 = devm_hwmon_device_register_with_groups(&client2->dev,
"CPLD2",
drvdata2,
switch_cpld_groups);

if (IS_ERR(hwmon_dev2)) {
err = PTR_ERR(hwmon_dev2);
goto err_client2;
}

err = sysfs_create_link(&client->dev.kobj, &hwmon_dev2->kobj, "CPLD2");
if (err) {
goto err_client2;
}

//port led
err = sysfs_create_group(&client->dev.kobj, &sff_led_groups);
if (err) {
dev_err(&client->dev,
"failed to create sysfs attribute group.\n");
goto err_link2;
goto err_link;
}

return 0;

err_link2:
sysfs_remove_link(&client->dev.kobj, "CPLD2");

err_client2:
if (client2)
i2c_unregister_device(client2);

err_link:
sysfs_remove_link(&client->dev.kobj, "CPLD1");
sysfs_remove_link(&client->dev.kobj, "MISC_CPLD");

exit:
dev_err(&client->dev, "probe error %d\n", err);
return err;
}

static int switch_cpld_remove(struct i2c_client *client)
static int misc_cpld_remove(struct i2c_client *client)
{
struct switch_cpld_data *data = i2c_get_clientdata(client);

sysfs_remove_group(&client->dev.kobj, &sff_led_groups);
sysfs_remove_link(&data->client->dev.kobj, "CPLD2");
sysfs_remove_link(&client->dev.kobj, "CPLD1");
i2c_unregister_device(data->client2);
sysfs_remove_link(&client->dev.kobj, "MISC_CPLD");
return 0;
}

static const struct i2c_device_id switch_cpld_ids[] = {
{ "switch_cpld", 0x30 },
static const struct i2c_device_id misc_cpld_ids[] = {
{ "misc_cpld", CPLD1_ADDR },
{ }
};

MODULE_DEVICE_TABLE(i2c, switch_cpld_ids);
MODULE_DEVICE_TABLE(i2c, misc_cpld_ids);

static struct i2c_driver switch_cpld_driver = {
static struct i2c_driver misc_cpld_driver = {
.driver = {
.name = "switch_cpld",
.name = "misc_cpld",
.owner = THIS_MODULE,
},
.probe = switch_cpld_probe,
.remove = switch_cpld_remove,
.id_table = switch_cpld_ids,
.probe = misc_cpld_probe,
.remove = misc_cpld_remove,
.id_table = misc_cpld_ids,
};

module_i2c_driver(switch_cpld_driver);
module_i2c_driver(misc_cpld_driver);

MODULE_AUTHOR("Budsakol Sirirattanasakul<bsirir@celestica.com>");
MODULE_DESCRIPTION("Celestica Blackstone Switchboard CPLD driver");
MODULE_AUTHOR("Wirut Getbamrung<wgetbumr@celestica.com>");
MODULE_DESCRIPTION("Celestica Blackstone MISC_CPLD driver");
MODULE_VERSION("1.0.0");
MODULE_LICENSE("GPL");
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ start)
modprobe cls-i2c-ocores
modprobe cls-fpga
modprobe xcvr-cls
modprobe switch_cpld
modprobe misc_cpld
modprobe i2c-mux-pca954x force_deselect_on_exit=1

# Instantiate TLV EEPROM device on I801 bus
Expand All @@ -37,7 +37,8 @@ start)
decode-syseeprom --init 2> /dev/null &

# Attach switchboard CPLD i2c device
echo switch_cpld 0x30 > /sys/bus/i2c/devices/i2c-3/new_device
echo misc_cpld 0x30 > /sys/bus/i2c/devices/i2c-2/new_device
echo misc_cpld 0x31 > /sys/bus/i2c/devices/i2c-3/new_device

# Attach QSFP-DD i2c devices
echo pca9548 0x70 > /sys/bus/i2c/devices/i2c-1/new_device
Expand Down