[Inventec]: Add Inventec D7054 platform support and update D7032 platform support (#1052)

This commit is contained in:
Balaselvi 2017-10-19 13:06:03 -07:00 committed by Shuotian Cheng
parent 2a56479187
commit 1ee8ed9216
43 changed files with 9242 additions and 263 deletions

View File

@ -18,5 +18,5 @@ except ImportError, e:
class board(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self, name, path, cpld_root, ro):
self.eeprom_path = "/tmp/eeprom"
self.eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0053/eeprom"
super(board, self).__init__(self.eeprom_path, 0, '', True)

View File

@ -1,19 +1,23 @@
#!/usr/bin/env python
# sfputil.py
#
# Platform-specific SFP transceiver interface for SONiC
#
try:
from sonic_sfp.sfputilbase import sfputilbase
except ImportError, e:
raise ImportError (str(e) + "- required module not found")
import time
from sonic_sfp.sfputilbase import SfpUtilBase
except ImportError as e:
raise ImportError("%s - required module not found" % str(e))
class sfputil(sfputilbase):
"""Platform specific sfputil class"""
class SfpUtil(SfpUtilBase):
"""Platform-specific SfpUtil class"""
port_start = 0
port_end = 31
ports_in_block = 32
PORT_START = 0
PORT_END = 31
PORTS_IN_BLOCK = 32
port_to_eeprom_mapping = {}
_port_to_eeprom_mapping = {}
port_to_i2c_mapping = {
0: 22,
1: 23,
@ -49,12 +53,117 @@ class sfputil(sfputilbase):
31: 21
}
_qsfp_ports = range(0, ports_in_block + 1)
@property
def port_start(self):
return self.PORT_START
def __init__(self, port_num):
# Override port_to_eeprom_mapping for class initialization
eeprom_path = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'
for x in range(self.port_start, self.port_end + 1):
@property
def port_end(self):
return self.PORT_END
@property
def qsfp_ports(self):
return range(0, self.PORTS_IN_BLOCK + 1)
@property
def port_to_eeprom_mapping(self):
return self._port_to_eeprom_mapping
def __init__(self):
eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
for x in range(0, self.port_end + 1):
port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x])
self.port_to_eeprom_mapping[x] = port_eeprom_path
sfputilbase.__init__(self, port_num)
SfpUtilBase.__init__(self)
def get_presence(self, port_num):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/present")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = int(reg_file.readline().rstrip())
if reg_value == 0:
return True
return False
def get_low_power_mode(self, port_num):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/lpmod")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
reg_value = int(reg_file.readline().rstrip())
if reg_value == 0:
return False
return True
def set_low_power_mode(self, port_num, lpmode):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/lpmod", "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = int(reg_file.readline().rstrip())
# LPMode is active high; set or clear the bit accordingly
if lpmode is True:
reg_value = 1
else:
reg_value = 0
reg_file.write(hex(reg_value))
reg_file.close()
return True
def reset(self, port_num):
QSFP_RESET_REGISTER_DEVICE_FILE = "/sys/class/swps/port"+str(port_num)+"/reset"
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
try:
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = 0
reg_file.write(hex(reg_value))
reg_file.close()
# Sleep 2 second to allow it to settle
time.sleep(2)
# Flip the value back write back to the register to take port out of reset
try:
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = 1
reg_file.write(hex(reg_value))
reg_file.close()
return True

View File

@ -0,0 +1,56 @@
# 48x25G + 6x100G
# name lanes alias
Ethernet0 2 Ethernet0
Ethernet4 1 Ethernet4
Ethernet8 4 Ethernet8
Ethernet12 3 Ethernet12
Ethernet16 6 Ethernet16
Ethernet20 5 Ethernet20
Ethernet24 8 Ethernet24
Ethernet28 7 Ethernet28
Ethernet32 10 Ethernet32
Ethernet36 9 Ethernet36
Ethernet40 12 Ethernet40
Ethernet44 11 Ethernet44
Ethernet48 22 Ethernet48
Ethernet52 21 Ethernet52
Ethernet56 24 Ethernet56
Ethernet60 23 Ethernet60
Ethernet64 34 Ethernet64
Ethernet68 33 Ethernet68
Ethernet72 36 Ethernet72
Ethernet76 35 Ethernet76
Ethernet80 38 Ethernet80
Ethernet84 37 Ethernet84
Ethernet88 40 Ethernet88
Ethernet92 39 Ethernet92
Ethernet96 42 Ethernet96
Ethernet100 41 Ethernet100
Ethernet104 44 Ethernet104
Ethernet108 43 Ethernet108
Ethernet112 50 Ethernet112
Ethernet116 49 Ethernet116
Ethernet120 52 Ethernet120
Ethernet124 51 Ethernet124
Ethernet128 54 Ethernet128
Ethernet132 53 Ethernet132
Ethernet136 56 Ethernet136
Ethernet140 55 Ethernet140
Ethernet144 66 Ethernet144
Ethernet148 65 Ethernet148
Ethernet152 68 Ethernet152
Ethernet156 67 Ethernet156
Ethernet160 70 Ethernet160
Ethernet164 69 Ethernet164
Ethernet168 72 Ethernet168
Ethernet172 71 Ethernet172
Ethernet176 82 Ethernet176
Ethernet180 81 Ethernet180
Ethernet184 84 Ethernet184
Ethernet188 83 Ethernet188
Ethernet192 85,86,87,88 Ethernet192
Ethernet196 97,98,99,100 Ethernet196
Ethernet200 105,106,107,108 Ethernet200
Ethernet204 101,102,103,104 Ethernet204
Ethernet208 117,118,119,120 Ethernet208
Ethernet212 109,110,111,112 Ethernet212

View File

@ -0,0 +1,2 @@
SAI_INIT_CONFIG_FILE=/etc/bcm/th-d7054q28b-48x10g-6x100g.config.bcm
SAI_NUM_ECMP_MEMBERS=32

View File

@ -0,0 +1,4 @@
CONSOLE_PORT=0x3f8
CONSOLE_DEV=0
CONSOLE_SPEED=115200
VAR_LOG_SIZE=1024

View File

@ -0,0 +1,146 @@
<DeviceMiniGraph xmlns="Microsoft.Search.Autopilot.Evolution" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CpgDec>
<IsisRouters xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
<PeeringSessions>
<BGPSession>
<StartRouter>OCPSCH0104001MS</StartRouter>
<StartPeer>10.10.1.26</StartPeer>
<EndRouter>SONiC-Inventec-d7054</EndRouter>
<EndPeer>10.10.1.25</EndPeer>
<Multihop>1</Multihop>
<HoldTime>10</HoldTime>
<KeepAliveTime>3</KeepAliveTime>
</BGPSession>
<BGPSession>
<StartRouter>OCPSCH0104002MS</StartRouter>
<StartPeer>10.10.2.26</StartPeer>
<EndRouter>SONiC-Inventec-d7054</EndRouter>
<EndPeer>10.10.2.25</EndPeer>
<Multihop>1</Multihop>
<HoldTime>10</HoldTime>
<KeepAliveTime>3</KeepAliveTime>
</BGPSession>
</PeeringSessions>
<Routers xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:BGPRouterDeclaration>
<a:ASN>64536</a:ASN>
<a:Hostname>SONiC-Inventec-d7054</a:Hostname>
<a:Peers>
<BGPPeer>
<Address>10.10.1.26</Address>
<RouteMapIn i:nil="true"/>
<RouteMapOut i:nil="true"/>
</BGPPeer>
<BGPPeer>
<Address>10.10.2.26</Address>
<RouteMapIn i:nil="true"/>
<RouteMapOut i:nil="true"/>
</BGPPeer>
</a:Peers>
<a:RouteMaps/>
</a:BGPRouterDeclaration>
<a:BGPRouterDeclaration>
<a:ASN>64542</a:ASN>
<a:Hostname>OCPSCH0104001MS</a:Hostname>
<a:RouteMaps/>
</a:BGPRouterDeclaration>
<a:BGPRouterDeclaration>
<a:ASN>64543</a:ASN>
<a:Hostname>OCPSCH0104002MS</a:Hostname>
<a:RouteMaps/>
</a:BGPRouterDeclaration>
</Routers>
</CpgDec>
<DpgDec>
<DeviceDataPlaneInfo>
<IPSecTunnels/>
<LoopbackIPInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:LoopbackIPInterface>
<Name>HostIP</Name>
<AttachTo>Loopback0</AttachTo>
<a:Prefix xmlns:b="Microsoft.Search.Autopilot.NetMux">
<b:IPPrefix>100.0.0.9/32</b:IPPrefix>
</a:Prefix>
<a:PrefixStr>100.0.0.9/32</a:PrefixStr>
</a:LoopbackIPInterface>
</LoopbackIPInterfaces>
<ManagementIPInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
</ManagementIPInterfaces>
<MplsInterfaces/>
<MplsTeInterfaces/>
<RsvpInterfaces/>
<Hostname>SONiC-Inventec-d7054</Hostname>
<PortChannelInterfaces/>
<VlanInterfaces/>
<IPInterfaces>
<IPInterface>
<Name i:nil="true"/>
<AttachTo>Ethernet0</AttachTo>
<Prefix>10.10.1.25/30</Prefix>
</IPInterface>
<IPInterface>
<Name i:nil="true"/>
<AttachTo>Ethernet4</AttachTo>
<Prefix>10.10.2.25/30</Prefix>
</IPInterface>
</IPInterfaces>
<DataAcls/>
<AclInterfaces/>
<DownstreamSummaries/>
<DownstreamSummarySet xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</DeviceDataPlaneInfo>
</DpgDec>
<PngDec>
<DeviceInterfaceLinks>
<DeviceLinkBase i:type="DeviceInterfaceLink">
<Bandwidth>40000</Bandwidth>
<ElementType>DeviceInterfaceLink</ElementType>
<EndDevice>OCPSCH0104001MS</EndDevice>
<EndPort>Ethernet24</EndPort>
<StartDevice>SONiC-Inventec-d7054</StartDevice>
<StartPort>Ethernet0</StartPort>
</DeviceLinkBase>
<DeviceLinkBase i:type="DeviceInterfaceLink">
<Bandwidth>40000</Bandwidth>
<ElementType>DeviceInterfaceLink</ElementType>
<EndDevice>OCPSCH0104002MS</EndDevice>
<EndPort>Ethernet24</EndPort>
<StartDevice>SONiC-Inventec-d7054</StartDevice>
<StartPort>Ethernet4</StartPort>
</DeviceLinkBase>
</DeviceInterfaceLinks>
<Devices>
<Device i:type="LeafRouter">
<Hostname>SONiC-Inventec-d7054</Hostname>
<HwSku>INVENTEC-D7054Q28B-S48-Q6</HwSku>
</Device>
</Devices>
</PngDec>
<MetadataDeclaration>
<Devices xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:DeviceMetadata>
<a:Name>SONiC-Inventec-d7054</a:Name>
<a:Properties>
<a:DeviceProperty>
<a:Name>DhcpResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value></a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>NtpResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>0.debian.pool.ntp.org;1.debian.pool.ntp.org;2.debian.pool.ntp.org;3.debian.pool.ntp.org</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>SyslogResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value></a:Value>
</a:DeviceProperty>
</a:Properties>
</a:DeviceMetadata>
</Devices>
<Properties xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</MetadataDeclaration>
<Hostname>SONiC-Inventec-d7054</Hostname>
<HwSku>INVENTEC-D7054Q28B-S48-Q6</HwSku>
</DeviceMiniGraph>

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
#############################################################################
# Inventec d7032q28b
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
# - the eeprom format definition
# - specific encoder/decoder if there is special need
#############################################################################
try:
from sonic_eeprom import eeprom_tlvinfo
except ImportError, e:
raise ImportError (str(e) + "- required module not found")
class board(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self, name, path, cpld_root, ro):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0053/eeprom"
super(board, self).__init__(self.eeprom_path, 0, '', True)

View File

@ -0,0 +1,209 @@
# sfputil.py
#
# Platform-specific SFP transceiver interface for SONiC
#
try:
import time
from sonic_sfp.sfputilbase import SfpUtilBase
except ImportError as e:
raise ImportError("%s - required module not found" % str(e))
class SfpUtil(SfpUtilBase):
"""Platform-specific SfpUtil class"""
PORT_START = 0
PORT_END = 53
PORTS_IN_BLOCK = 54
QSFP_PORT_START = 48
QSFP_PORT_END = 53
_port_to_eeprom_mapping = {}
port_to_i2c_mapping = {
0: 11,
1: 10,
2: 13,
3: 12,
4: 15,
5: 14,
6: 17,
7: 16,
8: 19,
9: 18,
10: 21,
11: 20,
12: 23,
13: 22,
14: 25,
15: 24,
16: 27,
17: 26,
18: 29,
19: 28,
20: 31,
21: 30,
22: 33,
23: 32,
24: 35,
25: 34,
26: 37,
27: 36,
28: 39,
29: 38,
30: 41,
31: 40,
32: 43,
33: 42,
34: 45,
35: 44,
36: 47,
37: 46,
38: 49,
39: 48,
40: 51,
41: 50,
42: 53,
43: 52,
44: 55,
45: 54,
46: 57,
47: 56,
48: 59,
49: 58,
50: 61,
51: 60,
52: 63,
53: 62
}
@property
def port_start(self):
return self.PORT_START
@property
def port_end(self):
return self.PORT_END
@property
def qsfp_port_start(self):
return self.QSFP_PORT_START
@property
def qsfp_port_end(self):
return self.QSFP_PORT_END
@property
def qsfp_ports(self):
return range(0, self.PORTS_IN_BLOCK + 1)
@property
def port_to_eeprom_mapping(self):
return self._port_to_eeprom_mapping
def __init__(self):
eeprom_path = "/sys/bus/i2c/devices/{0}-0050/eeprom"
for x in range(0, self.port_end + 1):
port_eeprom_path = eeprom_path.format(self.port_to_i2c_mapping[x])
self.port_to_eeprom_mapping[x] = port_eeprom_path
SfpUtilBase.__init__(self)
def get_presence(self, port_num):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/present")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = int(reg_file.readline().rstrip())
if reg_value == 0:
return True
return False
def get_low_power_mode(self, port_num):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/lpmod")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
reg_value = int(reg_file.readline().rstrip())
if reg_value == 0:
return False
return True
def set_low_power_mode(self, port_num, lpmode):
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
print "\nError:SFP's don't support this property"
return False
try:
reg_file = open("/sys/class/swps/port"+str(port_num)+"/lpmod", "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = int(reg_file.readline().rstrip())
# LPMode is active high; set or clear the bit accordingly
if lpmode is True:
reg_value = 1
else:
reg_value = 0
reg_file.write(hex(reg_value))
reg_file.close()
return True
def reset(self, port_num):
QSFP_RESET_REGISTER_DEVICE_FILE = "/sys/class/swps/port"+str(port_num)+"/reset"
# Check for invalid port_num
if port_num < self.port_start or port_num > self.port_end:
return False
if port_num < self.qsfp_port_start or port_num > self.qsfp_port_end:
print "\nError:SFP's don't support this property"
return False
try:
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = 0
reg_file.write(hex(reg_value))
reg_file.close()
# Sleep 2 second to allow it to settle
time.sleep(2)
# Flip the value back write back to the register to take port out of reset
try:
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "r+")
except IOError as e:
print "Error: unable to open file: %s" % str(e)
return False
reg_value = 1
reg_file.write(hex(reg_value))
reg_file.close()
return True

View File

@ -13,6 +13,7 @@ $(SONIC_ONE_IMAGE)_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \
$(INGRASYS_S8810_32Q_PLATFORM_MODULE) \
$(ACCTON_AS7712_32X_PLATFORM_MODULE) \
$(INVENTEC_D7032Q28B_PLATFORM_MODULE) \
$(INVENTEC_D7054Q28B_PLATFORM_MODULE) \
$(CEL_DX010_PLATFORM_MODULE)
$(SONIC_ONE_IMAGE)_DOCKERS += $(SONIC_INSTALL_DOCKER_IMAGES)
SONIC_INSTALLERS += $(SONIC_ONE_IMAGE)

View File

@ -1,12 +1,17 @@
# Inventec d7032q28b Platform modules
# Inventec d7032q28b and d7054q28b Platform modules
INVENTEC_D7032Q28B_PLATFORM_MODULE_VERSION = 1.0.0
INVENTEC_D7032Q28B_PLATFORM_MODULE_VERSION = 1.1.0
INVENTEC_D7054Q28B_PLATFORM_MODULE_VERSION = 1.1.0
export INVENTEC_D7032Q28B_PLATFORM_MODULE_VERSION
export INVENTEC_D7054Q28B_PLATFORM_MODULE_VERSION
INVENTEC_D7032Q28B_PLATFORM_MODULE = platform-modules-d7032q28b_$(INVENTEC_D7032Q28B_PLATFORM_MODULE_VERSION)_amd64.deb
$(INVENTEC_D7032Q28B_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-inventec
$(INVENTEC_D7032Q28B_PLATFORM_MODULE)_DEPENDS += $(LINUX_HEADERS) $(LINUX_HEADERS_COMMON)
$(INVENTEC_D7032Q28B_PLATFORM_MODULE)_PLATFORM = x86_64-inventec_d7032q28b-r0
SONIC_DPKG_DEBS += $(INVENTEC_D7032Q28B_PLATFORM_MODULE)
$(eval $(call add_extra_package,$(INVENTEC_D7032Q28B_PLATFORM_MODULE)))
INVENTEC_D7054Q28B_PLATFORM_MODULE = platform-modules-d7054q28b_$(INVENTEC_D7054Q28B_PLATFORM_MODULE_VERSION)_amd64.deb
$(INVENTEC_D7054Q28B_PLATFORM_MODULE)_PLATFORM = x86_64-inventec_d7054q28b-r0
$(eval $(call add_extra_package,$(INVENTEC_D7032Q28B_PLATFORM_MODULE),$(INVENTEC_D7054Q28B_PLATFORM_MODULE)))

View File

@ -1,10 +0,0 @@
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
lpc_ich
i2c-i801
i2c-mux
i2c-mux-pca954x
i2c-dev

View File

@ -1,3 +1,7 @@
obj-m += inv_cpld.o inv_psoc.o
obj-m += inv_platform.o
obj-m += inv_eeprom.o
obj-m += swps.o
swps-objs := inv_swps.o io_expander.o transceiver.o

View File

@ -0,0 +1,181 @@
/*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
#define SLICE_BITS (6)
#define SLICE_SIZE (1 << SLICE_BITS)
#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE)
/* Each client has this additional data */
struct eeprom_data {
struct mutex update_lock;
u8 valid; /* bitfield, bit!=0 if slice is valid */
unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */
u8 data[EEPROM_SIZE]; /* Register values */
};
static void inv_eeprom_update_client(struct i2c_client *client, u8 slice)
{
struct eeprom_data *data = i2c_get_clientdata(client);
int i, j;
int ret;
int addr;
mutex_lock(&data->update_lock);
if (!(data->valid & (1 << slice)) ||
time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
addr = slice << SLICE_BITS;
ret = i2c_smbus_write_byte_data(client, ((u8)addr >> 8) & 0xFF, (u8)addr & 0xFF);
/* select the eeprom address */
if (ret < 0) {
dev_err(&client->dev, "address set failed\n");
goto exit;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
goto exit;
}
for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) {
for (j = i; j < (i+SLICE_SIZE); j++) {
int res;
res = i2c_smbus_read_byte(client);
if (res < 0) {
goto exit;
}
data->data[j] = res & 0xFF;
}
}
data->last_updated[slice] = jiffies;
data->valid |= (1 << slice);
}
exit:
mutex_unlock(&data->update_lock);
}
static ssize_t inv_eeprom_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
u8 slice;
if (off > EEPROM_SIZE) {
return 0;
}
if (off + count > EEPROM_SIZE) {
count = EEPROM_SIZE - off;
}
if (count == 0) {
return 0;
}
/* Only refresh slices which contain requested bytes */
for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) {
inv_eeprom_update_client(client, slice);
}
memcpy(buf, &data->data[off], count);
return count;
}
static struct bin_attribute inv_eeprom_attr = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
},
.size = EEPROM_SIZE,
.read = inv_eeprom_read,
};
static int inv_eeprom_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct eeprom_data *data;
int err;
if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data->data, 0xff, EEPROM_SIZE);
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* create the sysfs eeprom file */
err = sysfs_create_bin_file(&client->dev.kobj, &inv_eeprom_attr);
if (err) {
goto exit_kfree;
}
return 0;
exit_kfree:
kfree(data);
exit:
return err;
}
static int inv_eeprom_remove(struct i2c_client *client)
{
sysfs_remove_bin_file(&client->dev.kobj, &inv_eeprom_attr);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id inv_eeprom_id[] = {
{ "inv_eeprom", 0 },
{ }
};
static struct i2c_driver inv_eeprom_driver = {
.driver = {
.name = "inv_eeprom",
},
.probe = inv_eeprom_probe,
.remove = inv_eeprom_remove,
.id_table = inv_eeprom_id,
};
module_i2c_driver(inv_eeprom_driver);
MODULE_AUTHOR("Inventec");
MODULE_DESCRIPTION("Inventec D7032 Mother Board EEPROM driver");
MODULE_LICENSE("GPL");

View File

@ -1,197 +1,198 @@
#include <linux/i2c.h>
//#include <linux/i2c-algo-bit.h>
#include <linux/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/i2c/pca954x.h>
#include <linux/platform_data/pca953x.h>
#include <linux/platform_data/at24.h>
//#include <asm/gpio.h>
#define IO_EXPAND_BASE 64
#define IO_EXPAND_NGPIO 16
struct inv_i2c_board_info {
int ch;
int size;
struct i2c_board_info *board_info;
};
#define bus_id(id) (id)
static struct pca954x_platform_mode mux_modes_0[] = {
{.adap_id = bus_id(2),}, {.adap_id = bus_id(3),},
{.adap_id = bus_id(4),}, {.adap_id = bus_id(5),},
};
static struct pca954x_platform_mode mux_modes_0_0[] = {
{.adap_id = bus_id(6),}, {.adap_id = bus_id(7),},
{.adap_id = bus_id(8),}, {.adap_id = bus_id(9),},
{.adap_id = bus_id(10),}, {.adap_id = bus_id(11),},
{.adap_id = bus_id(12),}, {.adap_id = bus_id(13),},
};
static struct pca954x_platform_mode mux_modes_0_1[] = {
{.adap_id = bus_id(14),}, {.adap_id = bus_id(15),},
{.adap_id = bus_id(16),}, {.adap_id = bus_id(17),},
{.adap_id = bus_id(18),}, {.adap_id = bus_id(19),},
{.adap_id = bus_id(20),}, {.adap_id = bus_id(21),},
};
static struct pca954x_platform_mode mux_modes_0_2[] = {
{.adap_id = bus_id(22),}, {.adap_id = bus_id(23),},
{.adap_id = bus_id(24),}, {.adap_id = bus_id(25),},
{.adap_id = bus_id(26),}, {.adap_id = bus_id(27),},
{.adap_id = bus_id(28),}, {.adap_id = bus_id(29),},
};
static struct pca954x_platform_mode mux_modes_0_3[] = {
{.adap_id = bus_id(30),}, {.adap_id = bus_id(31),},
{.adap_id = bus_id(32),}, {.adap_id = bus_id(33),},
{.adap_id = bus_id(34),}, {.adap_id = bus_id(35),},
{.adap_id = bus_id(36),}, {.adap_id = bus_id(37),},
};
static struct pca954x_platform_data mux_data_0 = {
.modes = mux_modes_0,
.num_modes = 4,
};
static struct pca954x_platform_data mux_data_0_0 = {
.modes = mux_modes_0_0,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_1 = {
.modes = mux_modes_0_1,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_2 = {
.modes = mux_modes_0_2,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_3 = {
.modes = mux_modes_0_3,
.num_modes = 8,
};
static struct i2c_board_info i2c_device_info0[] __initdata = {
{"inv_psoc", 0, 0x66, 0, 0, 0},//psoc
{"inv_cpld", 0, 0x55, 0, 0, 0},//cpld
{"pca9545", 0, 0x70, &mux_data_0, 0, 0},
};
static struct i2c_board_info i2c_device_info1[] __initdata = {
{"pca9545", 0, 0x70, &mux_data_0, 0, 0},
};
static struct i2c_board_info i2c_device_info2[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_0, 0, 0},
};
static struct i2c_board_info i2c_device_info3[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_1, 0, 0},
};
static struct i2c_board_info i2c_device_info4[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_2, 0, 0},
};
static struct i2c_board_info i2c_device_info5[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_3, 0, 0},
};
static struct inv_i2c_board_info i2cdev_list[] = {
{0, ARRAY_SIZE(i2c_device_info0), i2c_device_info0 }, //smbus 0
{1, ARRAY_SIZE(i2c_device_info1), i2c_device_info1 }, //smbus 1 or gpio11+12
{bus_id(2), ARRAY_SIZE(i2c_device_info2), i2c_device_info2 }, //mux 0
{bus_id(3), ARRAY_SIZE(i2c_device_info3), i2c_device_info3 }, //mux 1
{bus_id(4), ARRAY_SIZE(i2c_device_info4), i2c_device_info4 }, //mux 2
{bus_id(5), ARRAY_SIZE(i2c_device_info5), i2c_device_info5 }, //mux 3
};
/////////////////////////////////////////////////////////////////////////////////////////
static struct i2c_gpio_platform_data i2c_gpio_platdata0 = {
.scl_pin = 8,
.sda_pin = 9,
.udelay = 5, //5:100kHz
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 0
};
static struct i2c_gpio_platform_data i2c_gpio_platdata1 = {
.scl_pin = 12,
.sda_pin = 11,
.udelay = 5, //5:100kHz
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 0
};
static struct platform_device device_i2c_gpio0 = {
.name = "i2c-gpio",
.id = 0, // adapter number
.dev.platform_data = &i2c_gpio_platdata0,
};
static struct platform_device device_i2c_gpio1 = {
.name = "i2c-gpio",
.id = 1, // adapter number
.dev.platform_data = &i2c_gpio_platdata1,
};
static int __init plat_redwood_x86_init(void)
{
struct i2c_adapter *adap = NULL;
struct i2c_client *e = NULL;
int ret = 0;
int i,j;
printk("el6661 plat_redwood_x86_init \n");
#if 0 //disable for ICOS
//use i2c-gpio
//register i2c gpio
//config gpio8,9 to gpio function
outl( inl(0x500) | (1<<8 | 1<<9), 0x500);
ret = platform_device_register(&device_i2c_gpio0);
if (ret) {
printk(KERN_ERR "i2c-gpio: device_i2c_gpio0 register fail %d\n", ret);
}
outl( inl(0x500) | (1<<11 | 1<<12), 0x500);
ret = platform_device_register(&device_i2c_gpio1);
if (ret) {
printk(KERN_ERR "i2c-gpio: device_i2c_gpio1 register fail %d\n", ret);
}
#endif
for(i=0; i<ARRAY_SIZE(i2cdev_list); i++) {
adap = i2c_get_adapter( i2cdev_list[i].ch );
if (adap == NULL) {
printk("redwood_x86 get channel %d adapter fail\n", i);
continue;
return -ENODEV;
}
i2c_put_adapter(adap);
for(j=0; j<i2cdev_list[i].size; j++) {
e = i2c_new_device(adap, &i2cdev_list[i].board_info[j] );
}
}
return ret;
}
module_init(plat_redwood_x86_init);
//arch_initcall(plat_redwood_x86_init);
MODULE_AUTHOR("Inventec");
MODULE_DESCRIPTION("Redwood_x86 Platform devices");
MODULE_LICENSE("GPL");
#include <linux/i2c.h>
//#include <linux/i2c-algo-bit.h>
#include <linux/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/i2c/pca954x.h>
#include <linux/platform_data/pca953x.h>
#include <linux/platform_data/at24.h>
//#include <asm/gpio.h>
#define IO_EXPAND_BASE 64
#define IO_EXPAND_NGPIO 16
struct inv_i2c_board_info {
int ch;
int size;
struct i2c_board_info *board_info;
};
#define bus_id(id) (id)
static struct pca954x_platform_mode mux_modes_0[] = {
{.adap_id = bus_id(2),}, {.adap_id = bus_id(3),},
{.adap_id = bus_id(4),}, {.adap_id = bus_id(5),},
};
static struct pca954x_platform_mode mux_modes_0_0[] = {
{.adap_id = bus_id(6),}, {.adap_id = bus_id(7),},
{.adap_id = bus_id(8),}, {.adap_id = bus_id(9),},
{.adap_id = bus_id(10),}, {.adap_id = bus_id(11),},
{.adap_id = bus_id(12),}, {.adap_id = bus_id(13),},
};
static struct pca954x_platform_mode mux_modes_0_1[] = {
{.adap_id = bus_id(14),}, {.adap_id = bus_id(15),},
{.adap_id = bus_id(16),}, {.adap_id = bus_id(17),},
{.adap_id = bus_id(18),}, {.adap_id = bus_id(19),},
{.adap_id = bus_id(20),}, {.adap_id = bus_id(21),},
};
static struct pca954x_platform_mode mux_modes_0_2[] = {
{.adap_id = bus_id(22),}, {.adap_id = bus_id(23),},
{.adap_id = bus_id(24),}, {.adap_id = bus_id(25),},
{.adap_id = bus_id(26),}, {.adap_id = bus_id(27),},
{.adap_id = bus_id(28),}, {.adap_id = bus_id(29),},
};
static struct pca954x_platform_mode mux_modes_0_3[] = {
{.adap_id = bus_id(30),}, {.adap_id = bus_id(31),},
{.adap_id = bus_id(32),}, {.adap_id = bus_id(33),},
{.adap_id = bus_id(34),}, {.adap_id = bus_id(35),},
{.adap_id = bus_id(36),}, {.adap_id = bus_id(37),},
};
static struct pca954x_platform_data mux_data_0 = {
.modes = mux_modes_0,
.num_modes = 4,
};
static struct pca954x_platform_data mux_data_0_0 = {
.modes = mux_modes_0_0,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_1 = {
.modes = mux_modes_0_1,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_2 = {
.modes = mux_modes_0_2,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_3 = {
.modes = mux_modes_0_3,
.num_modes = 8,
};
static struct i2c_board_info i2c_device_info0[] __initdata = {
{"inv_psoc", 0, 0x66, 0, 0, 0},//psoc
{"inv_cpld", 0, 0x55, 0, 0, 0},//cpld
{"pca9545", 0, 0x70, &mux_data_0, 0, 0},
};
static struct i2c_board_info i2c_device_info1[] __initdata = {
{"pca9545", 0, 0x70, &mux_data_0, 0, 0},
};
static struct i2c_board_info i2c_device_info2[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_0, 0, 0},
};
static struct i2c_board_info i2c_device_info3[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_1, 0, 0},
};
static struct i2c_board_info i2c_device_info4[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_2, 0, 0},
};
static struct i2c_board_info i2c_device_info5[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_3, 0, 0},
};
static struct inv_i2c_board_info i2cdev_list[] = {
{0, ARRAY_SIZE(i2c_device_info0), i2c_device_info0 }, //smbus 0
{1, ARRAY_SIZE(i2c_device_info1), i2c_device_info1 }, //smbus 1 or gpio11+12
{bus_id(2), ARRAY_SIZE(i2c_device_info2), i2c_device_info2 }, //mux 0
{bus_id(3), ARRAY_SIZE(i2c_device_info3), i2c_device_info3 }, //mux 1
{bus_id(4), ARRAY_SIZE(i2c_device_info4), i2c_device_info4 }, //mux 2
{bus_id(5), ARRAY_SIZE(i2c_device_info5), i2c_device_info5 }, //mux 3
};
/////////////////////////////////////////////////////////////////////////////////////////
static struct i2c_gpio_platform_data i2c_gpio_platdata0 = {
.scl_pin = 8,
.sda_pin = 9,
.udelay = 5, //5:100kHz
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 0
};
static struct i2c_gpio_platform_data i2c_gpio_platdata1 = {
.scl_pin = 12,
.sda_pin = 11,
.udelay = 5, //5:100kHz
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 0
};
static struct platform_device device_i2c_gpio0 = {
.name = "i2c-gpio",
.id = 0, // adapter number
.dev.platform_data = &i2c_gpio_platdata0,
};
static struct platform_device device_i2c_gpio1 = {
.name = "i2c-gpio",
.id = 1, // adapter number
.dev.platform_data = &i2c_gpio_platdata1,
};
static int __init plat_redwood_x86_init(void)
{
struct i2c_adapter *adap = NULL;
struct i2c_client *e = NULL;
int ret = 0;
int i,j;
printk("el6661 plat_redwood_x86_init \n");
#if 0 //disable for ICOS
//use i2c-gpio
//register i2c gpio
//config gpio8,9 to gpio function
outl( inl(0x500) | (1<<8 | 1<<9), 0x500);
ret = platform_device_register(&device_i2c_gpio0);
if (ret) {
printk(KERN_ERR "i2c-gpio: device_i2c_gpio0 register fail %d\n", ret);
}
outl( inl(0x500) | (1<<11 | 1<<12), 0x500);
ret = platform_device_register(&device_i2c_gpio1);
if (ret) {
printk(KERN_ERR "i2c-gpio: device_i2c_gpio1 register fail %d\n", ret);
}
#endif
for(i=0; i<ARRAY_SIZE(i2cdev_list); i++) {
adap = i2c_get_adapter( i2cdev_list[i].ch );
if (adap == NULL) {
printk("redwood_x86 get channel %d adapter fail\n", i);
continue;
return -ENODEV;
}
i2c_put_adapter(adap);
for(j=0; j<i2cdev_list[i].size; j++) {
e = i2c_new_device(adap, &i2cdev_list[i].board_info[j] );
}
}
return ret;
}
module_init(plat_redwood_x86_init);
//arch_initcall(plat_redwood_x86_init);
MODULE_AUTHOR("Inventec");
MODULE_DESCRIPTION("Redwood_x86 Platform devices");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,618 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/dmi.h>
#include "inv_swps.h"
static int port_major;
static int ioexp_total;
static int port_total;
static struct class *swp_class_p = NULL;
static struct inv_platform_s *platform_p = NULL;
static struct inv_ioexp_layout_s *ioexp_layout = NULL;
static struct inv_port_layout_s *port_layout = NULL;
static int
__swp_match(struct device *dev,
#ifdef SWPS_KERN_VER_AF_3_10
const void *data){
#else
void *data){
#endif
char *name = (char *)data;
if (strcmp(dev_name(dev), name) == 0)
return 1;
return 0;
}
struct device *
get_swpdev_by_name(char *name){
struct device *dev = class_find_device(swp_class_p,
NULL,
name,
(const void *)__swp_match);
return dev;
}
static int
sscanf_2_int(const char *buf) {
int result = -EBFONT;
char *hex_tag = "0x";
if (strcspn(buf, hex_tag) == 0) {
if (sscanf(buf,"%x",&result)) {
return result;
}
} else {
if (sscanf(buf,"%d",&result)) {
return result;
}
if(sscanf(buf,"-%d",&result)) {
return -result;
}
if (sscanf(buf,"%x",&result)) {
return result;
}
}
return -EBFONT;
}
static int
sscanf_2_binary(const char *buf) {
int result = sscanf_2_int(buf);
if (result < 0){
return -EBFONT;
}
switch (result) {
case 0:
case 1:
return result;
default:
break;
}
return -EBFONT;
}
/* ========== Show functions: For I/O Expander attribute ==========
*/
static ssize_t
_show_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
int (*get_func)(struct ioexp_obj_s *ioexp_p, int voffset),
char *buf_p) {
size_t len;
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
if (!ioexp_p) {
SWPS_ERR(" %s: data corruption! <port>:%s\n", __func__, tobj_p->swp_name);
return -ENODATA;
}
mutex_lock(&ioexp_p->lock);
len = snprintf(buf_p, 8, "%d\n", get_func(ioexp_p, tobj_p->ioexp_virt_offset));
mutex_unlock(&ioexp_p->lock);
return len;
}
static ssize_t
show_attr_present(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_present,
buf_p);
}
static ssize_t
show_attr_reset(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_reset,
buf_p);
}
static ssize_t
show_attr_lpmod(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_lpmod,
buf_p);
}
static ssize_t
show_attr_modsel(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_modsel,
buf_p);
}
/* ========== Store functions: For I/O Expander (R/W) attribute ==========
*/
static ssize_t
_store_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
int (*set_func)(struct ioexp_obj_s *ioexp_p,
int virt_offset, int input_val),
const char *buf_p,
size_t count) {
int input, err;
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
if (!ioexp_p) {
SWPS_ERR("%s: data corruption! <port>:%s\n",
__func__, tobj_p->swp_name);
return -ENODATA;
}
input = sscanf_2_binary(buf_p);
if (input < 0) {
return -EBFONT;
}
mutex_lock(&ioexp_p->lock);
err = set_func(ioexp_p, tobj_p->ioexp_virt_offset, input);
mutex_unlock(&ioexp_p->lock);
if (err < 0){
return err;
}
return count;
}
static ssize_t
store_attr_reset(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_reset,
buf_p,
count);
}
static ssize_t
store_attr_lpmod(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_lpmod,
buf_p,
count);
}
static ssize_t
store_attr_modsel(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_modsel,
buf_p,
count);
}
/* ========== IO Expander attribute: from expander ==========
*/
static DEVICE_ATTR(present, S_IRUGO, show_attr_present, NULL);
static DEVICE_ATTR(reset, S_IRUGO|S_IWUSR, show_attr_reset, store_attr_reset);
static DEVICE_ATTR(lpmod, S_IRUGO|S_IWUSR, show_attr_lpmod, store_attr_lpmod);
static DEVICE_ATTR(modsel, S_IRUGO|S_IWUSR, show_attr_modsel, store_attr_modsel);
/* ========== Functions for module handling ==========
*/
static void
clean_port_obj(void){
dev_t dev_num;
char dev_name[32];
struct device *device_p;
struct transvr_obj_s *transvr_obj_p;
int minor_curr, port_id;
for (minor_curr=0; minor_curr<port_total; minor_curr++){
port_id = port_layout[minor_curr].port_id;
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, sizeof(dev_name), "%s%d", SWP_DEV_PORT, port_id);
device_p = get_swpdev_by_name(dev_name);
if (!device_p){
continue;
}
transvr_obj_p = dev_get_drvdata(device_p);
if (transvr_obj_p){
kfree(transvr_obj_p->i2c_client_p);
kfree(transvr_obj_p);
}
dev_num = MKDEV(port_major, minor_curr);
device_unregister(device_p);
device_destroy(swp_class_p, dev_num);
}
SWPS_DEBUG("%s: done.\n", __func__);
}
static int
get_platform_type(void){
char log_msg[64] = "ERROR";
platform_p = kzalloc(sizeof(struct inv_platform_s), GFP_KERNEL);
if (!platform_p){
snprintf(log_msg, sizeof(log_msg), "kzalloc fail");
goto err_get_platform_type_1;
}
platform_p->id = PLATFORM_SETTINGS;
memset(platform_p->name, 0, sizeof(platform_p->name));
snprintf(platform_p->name, (sizeof(platform_p->name) - 1),
"%s", platform_map.name);
snprintf(log_msg, sizeof(log_msg),
"User setup platform: %d (%s)",
platform_p->id, platform_p->name);
SWPS_DEBUG("%s: %s, <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
return 0;
err_get_platform_type_1:
SWPS_ERR("%s: %s <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
return -1;
}
static int
get_layout_info(void){
ioexp_layout = redwood_ioexp_layout;
port_layout = redwood_port_layout;
ioexp_total = ARRAY_SIZE(redwood_ioexp_layout);
port_total = ARRAY_SIZE(redwood_port_layout);
SWPS_INFO("Start to initial platform: %d (%s)\n",
platform_p->id, platform_p->name);
return 0;
}
/* ========== Functions for register something ==========
*/
static int
register_ioexp_attr_qsfp_1(struct device *device_p){
/* Support machine type:
* - QSFP : Magnolia, Redwood, Hudson32i
* - QSFP+ : Magnolia, Redwood, Hudson32i
* - QSFP28: Redwood
*/
char *err_attr = NULL;
if (device_create_file(device_p, &dev_attr_present) < 0) {
err_attr = "dev_attr_present";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_reset) < 0) {
err_attr = "dev_attr_reset";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_lpmod) < 0) {
err_attr = "dev_attr_lpmod";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_modsel) < 0) {
err_attr = "dev_attr_modsel";
goto err_ioexp_qsfp1_attr;
}
return 0;
err_ioexp_qsfp1_attr:
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
return -1;
}
static int
register_ioexp_attr(struct device *device_p,
struct transvr_obj_s *transvr_obj){
char *err_msg = "ERR";
switch (transvr_obj->ioexp_obj_p->ioexp_type){
case IOEXP_TYPE_REDWOOD_P01P08:
case IOEXP_TYPE_REDWOOD_P09P16:
if (register_ioexp_attr_qsfp_1(device_p) < 0){
err_msg = "register_ioexp_attr_qsfp_1 fail";
goto err_reg_ioexp_attr;
}
break;
default:
err_msg = "Unknow type";
goto err_reg_ioexp_attr;
}
return 0;
err_reg_ioexp_attr:
SWPS_ERR("%s: %s <type>:%d \n",
__func__, err_msg, transvr_obj->ioexp_obj_p->ioexp_type);
return -1;
}
static int
register_port_device(char *dev_name,
dev_t dev_num,
struct transvr_obj_s *transvr_obj){
struct device *device_p = NULL;
device_p = device_create(swp_class_p, /* struct class *cls */
NULL, /* struct device *parent */
dev_num, /* dev_t devt */
transvr_obj, /* void *private_data */
dev_name); /* const char *fmt */
if (IS_ERR(device_p)){
goto err_regswp_create_dev;
}
if (register_ioexp_attr(device_p, transvr_obj) < 0){
goto err_regswp_reg_attr;
}
return 0;
err_regswp_reg_attr:
device_unregister(device_p);
device_destroy(swp_class_p, dev_num);
err_regswp_create_dev:
SWPS_ERR("%s fail! <port>:%s\n", __func__, dev_name);
return -1;
}
static int
register_swp_module(void){
dev_t port_devt = 0;
int dev_total = port_total + 1; /* char_dev for module control */
if (alloc_chrdev_region(&port_devt, 0, dev_total, SWP_CLS_NAME) < 0){
SWPS_WARN("Allocate PORT MAJOR failure! \n");
goto err_register_swp_module_3;
}
port_major = MAJOR(port_devt);
/* Create class object */
swp_class_p = class_create(THIS_MODULE, SWP_CLS_NAME);
if (IS_ERR(swp_class_p)) {
SWPS_ERR("Create class failure! \n");
goto err_register_swp_module_3;
}
return 0;
err_register_swp_module_3:
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
return -1;
}
/* ========== Module initial relate ==========
*/
static int
create_ioexp_objs(void) {
int i, run_mod;
/* Clean IOEXP object */
clean_ioexp_objs();
/* Get running mode */
run_mod = IOEXP_MODE_DIRECT;
/* Create IOEXP object */
for(i=0; i<ioexp_total; i++){
if (create_ioexp_obj(ioexp_layout[i].ioexp_id,
ioexp_layout[i].ioexp_type,
(ioexp_layout[i].addr),
run_mod) < 0) {
goto err_initioexp_create_obj_1;
}
}
return 0;
err_initioexp_create_obj_1:
clean_ioexp_objs();
return -1;
}
static int
create_port_objs(void) {
int port_id, chan_id, ioexp_id, ioexp_virt_offset;
int transvr_type, chipset_type, run_mod, i, j;
int minor_curr = 0;
int ok_count = 0;
int devlen_max = 31; // 32 - 1
char dev_name[32] = "ERROR";
char err_msg[64] = "ERROR";
struct transvr_obj_s* transvr_obj_p = NULL;
struct ioexp_obj_s *ioexp_obj_p = NULL;
struct device *dev_p = NULL;
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
/* Get info from port_layout[] */
port_id = port_layout[minor_curr].port_id;
chan_id = port_layout[minor_curr].chan_id;
ioexp_id = port_layout[minor_curr].ioexp_id;
ioexp_virt_offset = port_layout[minor_curr].ioexp_offset;
transvr_type = port_layout[minor_curr].transvr_type;
chipset_type = port_layout[minor_curr].chipset_type;
/* Get running mode */
run_mod = TRANSVR_MODE_DIRECT;
/* Prepare device name */
if (strlen(SWP_DEV_PORT) > devlen_max) {
snprintf(err_msg, sizeof(err_msg),
"SWP_DEV_PORT too long!");
goto err_initport_create_tranobj;
}
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, devlen_max, "%s%d", SWP_DEV_PORT, port_id);
/* Create transceiver object */
ioexp_obj_p = get_ioexp_obj(ioexp_id);
if (!ioexp_obj_p){
snprintf(err_msg, sizeof(err_msg),
"IOEXP object:%d not exist", ioexp_id);
goto err_initport_create_tranobj;
}
transvr_obj_p = create_transvr_obj(dev_name, chan_id, ioexp_obj_p,
ioexp_virt_offset, transvr_type,
chipset_type, run_mod);
if (!transvr_obj_p){
snprintf(err_msg, sizeof(err_msg),
"Create transceiver object fail <id>:%s", dev_name);
goto err_initport_create_tranobj;
}
/* Setup Lane_ID mapping */
i = ARRAY_SIZE(port_layout[minor_curr].lane_id);
j = ARRAY_SIZE(transvr_obj_p->lane_id);
if (i != j) {
snprintf(err_msg, sizeof(err_msg),
"Lane_id size inconsistent %d/%d", i, j);
goto err_initport_reg_device;
}
memcpy(transvr_obj_p->lane_id, port_layout[minor_curr].lane_id, i*sizeof(int));
/* Create and register device object */
if (register_port_device(dev_name, MKDEV(port_major, minor_curr), transvr_obj_p) < 0){
snprintf(err_msg, sizeof(err_msg),
"register_port_device fail");
goto err_initport_reg_device;
}
/* Setup device_ptr of transvr_obj */
dev_p = get_swpdev_by_name(dev_name);
if (!dev_p){
snprintf(err_msg, sizeof(err_msg),
"get_swpdev_by_name fail");
goto err_initport_reg_device;
}
transvr_obj_p->transvr_dev_p = dev_p;
/* Success */
ok_count++;
}
SWPS_INFO("%s: initialed %d port-dev",__func__, ok_count);
return 0;
err_initport_reg_device:
kfree(transvr_obj_p);
err_initport_create_tranobj:
clean_port_obj();
SWPS_ERR("%s: %s", __func__, err_msg);
SWPS_ERR("Dump: <port_id>:%d <chan_id>:%d <ioexp_id>:%d <voffset>:%d <tvr_type>:%d <run_mod>:%d\n",
port_id, chan_id, ioexp_id, ioexp_virt_offset, transvr_type, run_mod);
return -1;
}
static int __init
swp_module_init(void){
if (get_platform_type() < 0){
goto err_init_out;
}
if (get_layout_info() < 0){
goto err_init_out;
}
if (register_swp_module() < 0){
goto err_init_out;
}
if (create_ioexp_objs() < 0){
goto err_init_ioexp;
}
if (create_port_objs() < 0){
goto err_init_portobj;
}
if (init_ioexp_objs() < 0){
goto err_init_portobj;
}
SWPS_INFO("Inventec switch-port module V.%s initial success.\n", SWP_VERSION);
return 0;
err_init_portobj:
clean_ioexp_objs();
err_init_ioexp:
class_unregister(swp_class_p);
class_destroy(swp_class_p);
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
err_init_out:
SWPS_ERR("Inventec switch-port module V.%s initial failure.\n", SWP_VERSION);
return -1;
}
static void __exit
swp_module_exit(void){
clean_port_obj();
clean_ioexp_objs();
class_unregister(swp_class_p);
class_destroy(swp_class_p);
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
SWPS_INFO("Remove Inventec switch-port module success.\n");
}
/* Module information */
MODULE_AUTHOR(SWP_AUTHOR);
MODULE_DESCRIPTION(SWP_DESC);
MODULE_VERSION(SWP_VERSION);
MODULE_LICENSE(SWP_LICENSE);
module_init(swp_module_init);
module_exit(swp_module_exit);

View File

@ -0,0 +1,131 @@
#ifndef INV_SWPS_H
#define INV_SWPS_H
#include "transceiver.h"
#include "io_expander.h"
/* Module settings */
#define SWP_CLS_NAME "swps"
#define SWP_DEV_PORT "port"
#define SWP_AUTOCONFIG_ENABLE (1)
/* Module information */
#define SWP_AUTHOR "Neil <liao.neil@inventec.com>"
#define SWP_DESC "Inventec port and transceiver driver"
#define SWP_VERSION "4.2.3"
#define SWP_LICENSE "GPL"
/* Module status define */
#define SWP_STATE_NORMAL (0)
#define SWP_STATE_I2C_DIE (-91)
/* [Note]:
* Functions and mechanism for auto-detect platform type is ready,
* But HW and BIOS not ready! We need to wait them.
* So, please do not use PLATFORM_TYPE_AUTO until they are ready.
* (2016.06.13)
*/
#define PLATFORM_TYPE_REDWOOD (121)
/* Current running platfrom */
#define PLATFORM_SETTINGS PLATFORM_TYPE_REDWOOD
/* Define platform flag and kernel version */
#if (PLATFORM_SETTINGS == PLATFORM_TYPE_REDWOOD)
#define SWPS_KERN_VER_BF_3_8 (1)
#endif
struct inv_platform_s {
int id;
char name[64];
};
struct inv_ioexp_layout_s {
int ioexp_id;
int ioexp_type;
struct ioexp_addr_s addr[4];
};
struct inv_port_layout_s {
int port_id;
int chan_id;
int ioexp_id;
int ioexp_offset;
int transvr_type;
int chipset_type;
int lane_id[8];
};
/* ==========================================
* Inventec Platform Settings
* ==========================================
*/
struct inv_platform_s platform_map = {PLATFORM_TYPE_REDWOOD, "D7032Q28B" };
/* ==========================================
* Redwood Layout configuration
* ==========================================
*/
struct inv_ioexp_layout_s redwood_ioexp_layout[] = {
/* IOEXP_ID / IOEXP_TYPE / { Chan_ID, Chip_addr, Read_offset, Write_offset, config_offset, data_default, conf_default } */
{0, IOEXP_TYPE_REDWOOD_P01P08, { {4, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[0] = I/O Expander 1-4 A */
{4, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[1] = I/O Expander 1-4 B */
{0, 0x25, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xff, 0xff}, }, }, /* addr[2] = I/O Expander 5 B */
},
{1, IOEXP_TYPE_REDWOOD_P09P16, { {5, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[0] = I/O Expander 1-4 A */
{5, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[1] = I/O Expander 1-4 B */
{0, 0x25, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xff, 0xff}, }, }, /* addr[2] = I/O Expander 5 B */
},
{2, IOEXP_TYPE_REDWOOD_P01P08, { {2, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[0] = I/O Expander 1-4 A */
{2, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[1] = I/O Expander 1-4 B */
{0, 0x24, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xff, 0xff}, }, }, /* addr[2] = I/O Expander 5 B */
},
{3, IOEXP_TYPE_REDWOOD_P09P16, { {3, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[0] = I/O Expander 1-4 A */
{3, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0x00}, {0x00, 0x0f}, }, /* addr[1] = I/O Expander 1-4 B */
{0, 0x24, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xff, 0xff}, }, }, /* addr[2] = I/O Expander 5 B */
},
};
struct inv_port_layout_s redwood_port_layout[] = {
/* Port_ID / Chan_ID / IOEXP_ID / IOEXP_VIRT_OFFSET / TRANSCEIVER_TYPE / BCM_CHIP_TYPE / LANE_ID */
{ 0, 22, 0, 0, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 1, 2, 3, 4} },
{ 1, 23, 0, 1, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 5, 6, 7, 8} },
{ 2, 24, 0, 2, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 9, 10, 11, 12} },
{ 3, 25, 0, 3, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 13, 14, 15, 16} },
{ 4, 26, 0, 4, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 17, 18, 19, 20} },
{ 5, 27, 0, 5, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 21, 22, 23, 24} },
{ 6, 28, 0, 6, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 25, 26, 27, 28} },
{ 7, 29, 0, 7, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 29, 30, 31, 32} },
{ 8, 30, 1, 0, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 33, 34, 35, 36} },
{ 9, 31, 1, 1, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 37, 38, 39, 40} },
{10, 32, 1, 2, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 41, 42, 43, 44} },
{11, 33, 1, 3, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 45, 46, 47, 48} },
{12, 34, 1, 4, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 49, 50, 51, 52} },
{13, 35, 1, 5, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 53, 54, 55, 56} },
{14, 36, 1, 6, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 57, 58, 59, 60} },
{15, 37, 1, 7, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 61, 62, 63, 64} },
{16, 6, 2, 0, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 65, 66, 67, 68} },
{17, 7, 2, 1, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 69, 70, 71, 72} },
{18, 8, 2, 2, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 73, 74, 75, 76} },
{19, 9, 2, 3, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 77, 78, 79, 80} },
{20, 10, 2, 4, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 81, 82, 83, 84} },
{21, 11, 2, 5, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 85, 86, 87, 88} },
{22, 12, 2, 6, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 89, 90, 91, 92} },
{23, 13, 2, 7, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 93, 94, 95, 96} },
{24, 14, 3, 0, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 97, 98, 99,100} },
{25, 15, 3, 1, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {101,102,103,104} },
{26, 16, 3, 2, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {105,106,107,108} },
{27, 17, 3, 3, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {109,110,111,112} },
{28, 18, 3, 4, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {113,114,115,116} },
{29, 19, 3, 5, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {117,118,119,120} },
{30, 20, 3, 6, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {121,122,123,124} },
{31, 21, 3, 7, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {125,126,127,128} },
};
#endif /* INV_SWPS_H */

View File

@ -0,0 +1,887 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "io_expander.h"
static struct ioexp_obj_s *ioexp_head_p = NULL;
static struct ioexp_obj_s *ioexp_tail_p = NULL;
struct ioexp_map_s ioexp_map_redwood_p01p08_p17p24 = {
.chip_amount = 3,
.data_width = 2,
.map_present = { {2, 0, 0}, /* map_present[0] = MOD_ABS_PORT(X) */
{2, 0, 1}, /* map_present[1] = MOD_ABS_PORT(X+1) */
{2, 0, 2}, /* map_present[2] = MOD_ABS_PORT(X+2) */
{2, 0, 3}, /* map_present[3] = MOD_ABS_PORT(X+3) */
{2, 0, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
{2, 0, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
{2, 0, 6}, /* map_present[6] = MOD_ABS_PORT(X+6) */
{2, 0, 7}, /* map_present[7] = MOD_ABS_PORT(X+7) */
},
.map_reset = { {0, 0, 0}, /* map_reset[0] = QRESET_QSFP28_N_P(X) */
{0, 0, 1}, /* map_reset[1] = QRESET_QSFP28_N_P(X+1) */
{0, 0, 2}, /* map_reset[2] = QRESET_QSFP28_N_P(X+2) */
{0, 0, 3}, /* map_reset[3] = QRESET_QSFP28_N_P(X+3) */
{1, 0, 0}, /* map_reset[4] = QRESET_QSFP28_N_P(X+4) */
{1, 0, 1}, /* map_reset[5] = QRESET_QSFP28_N_P(X+5) */
{1, 0, 2}, /* map_reset[6] = QRESET_QSFP28_N_P(X+6) */
{1, 0, 3}, /* map_reset[7] = QRESET_QSFP28_N_P(X+7) */
},
.map_lpmod = { {0, 0, 4}, /* map_lpmod[0] = LPMODE_QSFP28_P(X) */
{0, 0, 5}, /* map_lpmod[1] = LPMODE_QSFP28_P(X+1) */
{0, 0, 6}, /* map_lpmod[2] = LPMODE_QSFP28_P(X+2) */
{0, 0, 7}, /* map_lpmod[3] = LPMODE_QSFP28_P(X+3) */
{1, 0, 4}, /* map_lpmod[4] = LPMODE_QSFP28_P(X+4) */
{1, 0, 5}, /* map_lpmod[5] = LPMODE_QSFP28_P(X+5) */
{1, 0, 6}, /* map_lpmod[6] = LPMODE_QSFP28_P(X+6) */
{1, 0, 7}, /* map_lpmod[7] = LPMODE_QSFP28_P(X+7) */
},
.map_modsel = { {0, 1, 4}, /* map_modsel[0] = MODSEL_QSFP28_N_P(X) */
{0, 1, 5}, /* map_modsel[1] = MODSEL_QSFP28_N_P(X+1) */
{0, 1, 6}, /* map_modsel[2] = MODSEL_QSFP28_N_P(X+2) */
{0, 1, 7}, /* map_modsel[3] = MODSEL_QSFP28_N_P(X+3) */
{1, 1, 4}, /* map_modsel[4] = MODSEL_QSFP28_N_P(X+4) */
{1, 1, 5}, /* map_modsel[5] = MODSEL_QSFP28_N_P(X+5) */
{1, 1, 6}, /* map_modsel[6] = MODSEL_QSFP28_N_P(X+6) */
{1, 1, 7}, /* map_modsel[7] = MODSEL_QSFP28_N_P(X+7) */
},
};
struct ioexp_map_s ioexp_map_redwood_p09p16_p25p32 = {
.chip_amount = 3,
.data_width = 2,
.map_present = { {2, 1, 0}, /* map_present[0] = MOD_ABS_PORT(X) */
{2, 1, 1}, /* map_present[1] = MOD_ABS_PORT(X+1) */
{2, 1, 2}, /* map_present[2] = MOD_ABS_PORT(X+2) */
{2, 1, 3}, /* map_present[3] = MOD_ABS_PORT(X+3) */
{2, 1, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
{2, 1, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
{2, 1, 6}, /* map_present[6] = MOD_ABS_PORT(X+6) */
{2, 1, 7}, /* map_present[7] = MOD_ABS_PORT(X+7) */
},
.map_reset = { {0, 0, 0}, /* map_reset[0] = QRESET_QSFP28_N_P(X) */
{0, 0, 1}, /* map_reset[1] = QRESET_QSFP28_N_P(X+1) */
{0, 0, 2}, /* map_reset[2] = QRESET_QSFP28_N_P(X+2) */
{0, 0, 3}, /* map_reset[3] = QRESET_QSFP28_N_P(X+3) */
{1, 0, 0}, /* map_reset[4] = QRESET_QSFP28_N_P(X+4) */
{1, 0, 1}, /* map_reset[5] = QRESET_QSFP28_N_P(X+5) */
{1, 0, 2}, /* map_reset[6] = QRESET_QSFP28_N_P(X+6) */
{1, 0, 3}, /* map_reset[7] = QRESET_QSFP28_N_P(X+7) */
},
.map_lpmod = { {0, 0, 4}, /* map_lpmod[0] = LPMODE_QSFP28_P(X) */
{0, 0, 5}, /* map_lpmod[1] = LPMODE_QSFP28_P(X+1) */
{0, 0, 6}, /* map_lpmod[2] = LPMODE_QSFP28_P(X+2) */
{0, 0, 7}, /* map_lpmod[3] = LPMODE_QSFP28_P(X+3) */
{1, 0, 4}, /* map_lpmod[4] = LPMODE_QSFP28_P(X+4) */
{1, 0, 5}, /* map_lpmod[5] = LPMODE_QSFP28_P(X+5) */
{1, 0, 6}, /* map_lpmod[6] = LPMODE_QSFP28_P(X+6) */
{1, 0, 7}, /* map_lpmod[7] = LPMODE_QSFP28_P(X+7) */
},
.map_modsel = { {0, 1, 4}, /* map_modsel[0] = MODSEL_QSFP28_N_P(X) */
{0, 1, 5}, /* map_modsel[1] = MODSEL_QSFP28_N_P(X+1) */
{0, 1, 6}, /* map_modsel[2] = MODSEL_QSFP28_N_P(X+2) */
{0, 1, 7}, /* map_modsel[3] = MODSEL_QSFP28_N_P(X+3) */
{1, 1, 4}, /* map_modsel[4] = MODSEL_QSFP28_N_P(X+4) */
{1, 1, 5}, /* map_modsel[5] = MODSEL_QSFP28_N_P(X+5) */
{1, 1, 6}, /* map_modsel[6] = MODSEL_QSFP28_N_P(X+6) */
{1, 1, 7}, /* map_modsel[7] = MODSEL_QSFP28_N_P(X+7) */
},
};
/* ========== Private functions ==========
*/
int check_channel_tier_1(void);
struct i2c_client *
_get_i2c_client(struct ioexp_obj_s *self,
int chip_id){
struct ioexp_i2c_s *i2c_curr_p = self->i2c_head_p;
if (!(i2c_curr_p)){
SWPS_ERR("%s: i2c_curr_p is NULL\n", __func__);
return NULL;
}
while (i2c_curr_p){
if ((i2c_curr_p->chip_id) == chip_id){
return i2c_curr_p->i2c_client_p;
}
i2c_curr_p = i2c_curr_p->next;
}
SWPS_ERR("%s: not exist! <chip_id>:%d\n", __func__, chip_id);
return NULL;
}
static int
_common_ioexp_update_one(struct ioexp_obj_s *self,
struct ioexp_addr_s *ioexp_addr,
int chip_id,
int data_width,
int show_err,
char *caller_name) {
int buf = 0;
int err = 0;
int data_id = 0;
int r_offset = 0;
for(data_id=0; data_id<data_width; data_id++){
/* Read from IOEXP */
r_offset = ioexp_addr->read_offset[data_id];
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id), r_offset);
/* Check error */
if (buf < 0) {
err = 1;
if (show_err) {
SWPS_INFO("IOEXP-%d read fail! <err>:%d \n", self->ioexp_id, buf);
SWPS_INFO("Dump: <chan>:%d <addr>:0x%02x <offset>:%d, <caller>:%s\n",
ioexp_addr->chan_id, ioexp_addr->chip_addr,
ioexp_addr->read_offset[data_id], caller_name);
}
continue;
}
/* Update IOEXP object */
self->chip_data[chip_id].data[data_id] = (uint8_t)buf;
}
if (err) {
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
static int
common_ioexp_update_all(struct ioexp_obj_s *self,
int show_err,
char *caller_name){
int err = 0;
int chip_id = 0;
int chip_amount = self->ioexp_map_p->chip_amount;
for (chip_id=0; chip_id<chip_amount; chip_id++){
if (_common_ioexp_update_one(self,
&(self->ioexp_map_p->map_addr[chip_id]),
chip_id,
self->ioexp_map_p->data_width,
show_err,
caller_name) < 0) {
err = 1;
}
}
if (err) {
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
static int
_common_get_bit(struct ioexp_obj_s *self,
struct ioexp_bitmap_s *bitmap_obj_p,
char *func_mane){
uint8_t buf;
int err_code;
/* Get address */
err_code = self->fsm_4_direct(self);
if (err_code < 0){
return err_code;
}
if (!bitmap_obj_p){
SWPS_ERR("Layout config incorrect! <ioexp_id>:%d <func>:%s\n",
self->ioexp_id, func_mane);
return ERR_IOEXP_BADCONF;
}
/* Get data form cache */
buf = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
return (int)(buf >> bitmap_obj_p->bit_shift & 0x01);
}
static int
_common_set_bit(struct ioexp_obj_s *self,
struct ioexp_bitmap_s *bitmap_obj_p,
int input_val,
char *func_mane){
int err_code, target_offset;
uint8_t origin_byte;
uint8_t modify_byte;
/* Get address */
err_code = self->fsm_4_direct(self);
if (err_code < 0){
return err_code;
}
if (!bitmap_obj_p){
SWPS_ERR("Layout config incorrect! <ioexp>:%d <func>:%s\n",
self->ioexp_id, func_mane);
return ERR_IOEXP_BADCONF;
}
/* Prepare write date */
origin_byte = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
switch (input_val) {
case 0:
modify_byte = origin_byte;
SWP_BIT_CLEAR(modify_byte, bitmap_obj_p->bit_shift);
break;
case 1:
modify_byte = origin_byte;
SWP_BIT_SET(modify_byte, bitmap_obj_p->bit_shift);
break;
default:
SWPS_ERR("Input value incorrect! <val>:%d <ioexp>:%d <func>:%s\n",
input_val, self->ioexp_id, func_mane);
return ERR_IOEXP_BADINPUT;
}
/* Setup i2c client */
target_offset = self->ioexp_map_p->map_addr[bitmap_obj_p->chip_id].write_offset[bitmap_obj_p->ioexp_voffset];
/* Write byte to chip via I2C */
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, bitmap_obj_p->chip_id),
target_offset,
modify_byte);
/* Update or bollback object */
if (err_code < 0){
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = origin_byte;
SWPS_ERR("I2C write fail! <input>:%d <ioexp>:%d <func>:%s <err>:%d\n",
input_val, self->ioexp_id, func_mane, err_code);
return err_code;
}
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = modify_byte;
return 0;
}
/* ========== Object public functions ==========
*/
int
common_get_present(struct ioexp_obj_s *self,
int virt_offset){
int UNPLUG = 1;
int retval = ERR_IOEXP_UNEXCPT;
retval = _common_get_bit(self,
&(self->ioexp_map_p->map_present[virt_offset]),
"common_get_present");
if (retval < 0) {
/* [Note]
* => Transceiver object does not need to handle IOEXP layer issues.
*/
return UNPLUG;
}
return retval;
}
int
common_get_reset(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_reset[virt_offset]),
"common_get_reset");
}
int
common_get_lpmod(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_lpmod[virt_offset]),
"common_get_lpmod");
}
int
common_get_modsel(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_modsel[virt_offset]),
"common_get_modsel");
}
int
common_set_reset(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_reset[virt_offset]),
input_val,
"common_set_reset");
}
int
common_set_lpmod(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_lpmod[virt_offset]),
input_val,
"common_set_lpmod");
}
int
common_set_modsel(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_modsel[virt_offset]),
input_val,
"common_set_modsel");
}
int
ioexp_get_not_support(struct ioexp_obj_s *self,
int virt_offset){
return ERR_IOEXP_NOTSUPPORT;
}
int
ioexp_set_not_support(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return ERR_IOEXP_NOTSUPPORT;
}
/* ========== Initial functions for IO Expander ==========
*/
int
common_ioexp_init(struct ioexp_obj_s *self) {
int chip_id, offset, err_code;
struct ioexp_addr_s *addr_p;
if (self->mode == IOEXP_MODE_DIRECT) { ///important
goto update_common_ioexp_init;
}
/* Setup default value to each physical IO Expander */
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
/* Get address mapping */
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
if (!addr_p){
SWPS_ERR("%s: IOEXP config incorrect! <chip_id>:%d \n",
__func__, chip_id);
return -1;
}
/* Setup default value */
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
addr_p->write_offset[offset],
addr_p->data_default[offset]);
if (err_code < 0){
SWPS_ERR("%s: set default fail! <error>:%d \n",
__func__, err_code);
return ERR_IOEXP_UNEXCPT;
}
}
}
update_common_ioexp_init:
/* Check and update info to object */
err_code = self->update_all(self, 1, "common_ioexp_init");
if (err_code < 0) {
SWPS_ERR("%s: update_all() fail! <error>:%d \n",
__func__, err_code);
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
/* ========== Object functions for Final State Machine ==========
*/
int
_is_channel_ready(struct ioexp_obj_s *self){
int buf = 0;
int chip_id = 0; /* Use first chip which be registered */
int data_id = 0; /* Use first byte which be registered */
struct ioexp_addr_s *ioexp_addr = NULL;
ioexp_addr = &(self->ioexp_map_p->map_addr[chip_id]);
if (!ioexp_addr){
SWPS_ERR("%s: config incorrect!\n", __func__);
return ERR_IOEXP_UNEXCPT;
}
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id),
ioexp_addr->read_offset[data_id]);
if (buf >= 0){
return 1;
}
return 0;
}
int
_ioexp_init_handler(struct ioexp_obj_s *self){
int return_val;
switch (self->mode) {
case IOEXP_MODE_DIRECT:
return_val = self->init(self);
if (return_val < 0){
self->state = STATE_IOEXP_ABNORMAL;
} else {
self->state = STATE_IOEXP_NORMAL;
}
return return_val;
default:
break;
}
SWPS_ERR("%s: exception occur <mode>:%d\n", __func__, self->mode);
return ERR_IOEXP_UNEXCPT;
}
int
common_ioexp_fsm_4_direct(struct ioexp_obj_s *self){
int result_val;
int show_err = 1;
char *func_mane = "common_ioexp_fsm_4_direct";
switch (self->state){
case STATE_IOEXP_INIT:
result_val = _ioexp_init_handler(self);
/* Exception case: terminate initial procedure */
if(result_val < 0){
/* Initial fail */
return result_val;
}
if(self->state == STATE_IOEXP_INIT){
/* Keep in INIT state, and return error */
return ERR_IOEXP_UNINIT;
}
/* Case: Initial done */
return 0;
case STATE_IOEXP_NORMAL:
result_val = self->update_all(self, show_err, func_mane);
if (result_val < 0){
SWPS_INFO("%s: NORMAL -> ABNORMAL <err>:%d\n",
__func__, result_val);
self->state = STATE_IOEXP_ABNORMAL;
return result_val;
}
self->state = STATE_IOEXP_NORMAL;
return 0;
case STATE_IOEXP_ABNORMAL:
result_val = self->update_all(self, show_err, func_mane);
if (result_val < 0){
self->state = STATE_IOEXP_ABNORMAL;
return result_val;
}
SWPS_DEBUG("%s: ABNORMAL -> NORMAL <err>:%d\n",
__func__, result_val);
self->state = STATE_IOEXP_NORMAL;
return 0;
default:
break;
}
SWPS_ERR("%s: Exception occurs <state>:%d\n",
__func__, self->state);
return ERR_IOEXP_UNEXCPT;
}
/* ========== Functions for Factory pattern ==========
*/
static struct ioexp_map_s *
get_ioexp_map(int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_REDWOOD_P01P08:
return &ioexp_map_redwood_p01p08_p17p24;
case IOEXP_TYPE_REDWOOD_P09P16:
return &ioexp_map_redwood_p09p16_p25p32;
default:
return NULL;
}
}
int
setup_ioexp_ssize_attr(struct ioexp_obj_s *self,
struct ioexp_map_s *ioexp_map_p,
int ioexp_id,
int ioexp_type,
int run_mode){
switch (run_mode){
case IOEXP_MODE_DIRECT: /* Direct access device mode */
self->mode = run_mode;
break;
default:
SWPS_ERR("%s: non-defined run_mode:%d\n",
__func__, run_mode);
self->mode = ERR_IOEXP_UNEXCPT;
return ERR_IOEXP_UNEXCPT;
}
self->ioexp_id = ioexp_id;
self->ioexp_type = ioexp_type;
self->ioexp_map_p = ioexp_map_p;
self->state = STATE_IOEXP_INIT;
mutex_init(&self->lock);
return 0;
}
static int
setup_addr_mapping(struct ioexp_obj_s *self,
struct ioexp_addr_s *addr_map_p){
if (!addr_map_p){
SWPS_ERR("%s: map is null\n", __func__);
return -1;
}
self->ioexp_map_p->map_addr = addr_map_p;
return 0;
}
static int
setup_ioexp_public_cb(struct ioexp_obj_s *self,
int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_REDWOOD_P01P08:
case IOEXP_TYPE_REDWOOD_P09P16:
self->get_present = common_get_present;
self->get_reset = common_get_reset;
self->get_lpmod = common_get_lpmod;
self->get_modsel = common_get_modsel;
self->set_reset = common_set_reset;
self->set_lpmod = common_set_lpmod;
self->set_modsel = common_set_modsel;
return 0;
default:
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
break;
}
return ERR_IOEXP_UNEXCPT;
}
static int
setup_ioexp_private_cb(struct ioexp_obj_s *self,
int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_REDWOOD_P01P08:
case IOEXP_TYPE_REDWOOD_P09P16:
self->init = common_ioexp_init;
self->update_all = common_ioexp_update_all;
self->fsm_4_direct = common_ioexp_fsm_4_direct;
return 0;
default:
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
break;
}
return ERR_IOEXP_UNEXCPT;
}
static int
setup_i2c_client_one(struct ioexp_obj_s *self,
int chip_id){
char *err_msg = "ERROR";
struct i2c_adapter *adap = NULL;
struct i2c_client *client = NULL;
struct ioexp_i2c_s *i2c_obj_p = NULL;
struct ioexp_i2c_s *i2c_curr_p = NULL;
int chan_id = self->ioexp_map_p->map_addr[chip_id].chan_id;
adap = i2c_get_adapter(chan_id);
if(!adap){
err_msg = "Can not get adap!";
goto err_ioexp_setup_i2c_1;
}
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client){
err_msg = "Can not kzalloc client!";
goto err_ioexp_setup_i2c_1;
}
i2c_obj_p = kzalloc(sizeof(*i2c_obj_p), GFP_KERNEL);
if (!i2c_obj_p){
err_msg = "Can not kzalloc i2c_obj_p!";
goto err_ioexp_setup_i2c_2;
}
client->adapter = adap;
client->addr = self->ioexp_map_p->map_addr[chip_id].chip_addr;
i2c_obj_p->i2c_client_p = client;
i2c_obj_p->chip_id = chip_id;
i2c_obj_p->next = NULL;
if (!self->i2c_head_p){
self->i2c_head_p = i2c_obj_p;
} else {
i2c_curr_p = self->i2c_head_p;
while (i2c_curr_p->next){
i2c_curr_p = i2c_curr_p->next;
}
i2c_curr_p->next = i2c_obj_p;
}
return 0;
err_ioexp_setup_i2c_2:
kfree(client);
err_ioexp_setup_i2c_1:
SWPS_ERR("%s: %s <chanID>:%d\n", __func__, err_msg, chan_id);
return -1;
}
static int
setup_i2c_client(struct ioexp_obj_s* self){
int result;
int chip_id = 0;
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
result = setup_i2c_client_one(self, chip_id);
if (result < 0){
SWPS_ERR("%s fail! <chip_id>:%d\n", __func__, chip_id);
return -1;
}
}
return 0;
}
static int
setup_ioexp_config(struct ioexp_obj_s *self) {
int chip_id, offset, err_code;
struct ioexp_addr_s *addr_p;
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
if (!addr_p){
SWPS_ERR("IOEXP config incorrect! <chip_id>:%d \n",chip_id);
return -1;
}
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
addr_p->conf_offset[offset],
addr_p->conf_default[offset]);
if (err_code < 0){
SWPS_INFO("%s: set conf fail! <err>:%d \n", __func__, err_code);
return -2;
}
}
}
return 0;
}
struct ioexp_obj_s *
_create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode){
struct ioexp_map_s* ioexp_map_p;
struct ioexp_obj_s* result_p;
struct ioexp_i2c_s *i2c_curr_p;
struct ioexp_i2c_s *i2c_next_p;
/* Get layout */
ioexp_map_p = get_ioexp_map(ioexp_type);
if (!ioexp_map_p){
SWPS_ERR("%s: Invalid ioexp_type\n", __func__);
goto err_create_ioexp_fail;
}
/* Prepare IOEXP object */
result_p = kzalloc(sizeof(*result_p), GFP_KERNEL);
if (!result_p){
SWPS_ERR("%s: kzalloc failure!\n", __func__);
goto err_create_ioexp_fail;
}
/* Prepare static size attributes */
if (setup_ioexp_ssize_attr(result_p,
ioexp_map_p,
ioexp_id,
ioexp_type,
run_mode) < 0){
goto err_create_ioexp_setup_attr_fail;
}
/* Prepare address mapping */
if (setup_addr_mapping(result_p, addr_map_p) < 0){
goto err_create_ioexp_setup_attr_fail;
}
if (setup_i2c_client(result_p) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
/* Prepare call back functions of object */
if (setup_ioexp_public_cb(result_p, ioexp_type) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
if (setup_ioexp_private_cb(result_p, ioexp_type) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
return result_p;
err_create_ioexp_setup_i2c_fail:
i2c_curr_p = result_p->i2c_head_p;
i2c_next_p = result_p->i2c_head_p;
while (i2c_curr_p){
i2c_next_p = i2c_curr_p->next;
kfree(i2c_curr_p->i2c_client_p);
kfree(i2c_curr_p);
i2c_curr_p = i2c_next_p;
}
err_create_ioexp_setup_attr_fail:
kfree(result_p);
err_create_ioexp_fail:
SWPS_ERR("%s: fail! <id>:%d <type>:%d \n",
__func__, ioexp_id, ioexp_type);
return NULL;
}
int
create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode){
struct ioexp_obj_s *ioexp_p = NULL;
ioexp_p = _create_ioexp_obj(ioexp_id, ioexp_type,
addr_map_p, run_mode);
if (!ioexp_p){
return -1;
}
if (ioexp_head_p == NULL){
ioexp_head_p = ioexp_p;
ioexp_tail_p = ioexp_p;
return 0;
}
ioexp_tail_p->next = ioexp_p;
ioexp_tail_p = ioexp_p;
return 0;
}
static int
_init_ioexp_obj(struct ioexp_obj_s* self) {
char *err_msg = "ERR";
char *func_name = "_init_ioexp_obj";
/* Setup IOEXP configure byte */
if (setup_ioexp_config(self) < 0){
err_msg = "setup_ioexp_config fail";
goto err_init_ioexp_obj;
}
/* Setup default data */
if (_ioexp_init_handler(self) < 0){
err_msg = "_ioexp_init_handler fail";
goto err_init_ioexp_obj;
}
/* Update all */
if (self->state == STATE_IOEXP_NORMAL){
if (self->update_all(self, 1, func_name) < 0){
err_msg = "update_all() fail";
goto err_init_ioexp_obj;
}
}
return 0;
err_init_ioexp_obj:
SWPS_DEBUG("%s: %s\n", __func__, err_msg);
return -1;
}
int
init_ioexp_objs(void){
/* Return value:
* 0: Success
* -1: Detect topology error
* -2: SWPS internal error
*/
struct ioexp_obj_s *curr_p = ioexp_head_p;
if (!curr_p) {
SWPS_ERR("%s: ioexp_head_p is NULL\n", __func__);
return -2;
}
while (curr_p) {
if (_init_ioexp_obj(curr_p) < 0) {
SWPS_DEBUG("%s: _init_ioexp_obj() fail\n", __func__);
return -1;
}
curr_p = curr_p->next;
}
SWPS_DEBUG("%s: done.\n", __func__);
return 0;
}
void
clean_ioexp_objs(void){
struct ioexp_i2c_s *i2c_curr_p = NULL;
struct ioexp_i2c_s *i2c_next_p = NULL;
struct ioexp_obj_s *ioexp_next_p = NULL;
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
if (ioexp_head_p == NULL){
ioexp_tail_p = NULL;
return;
}
while(ioexp_curr_p){
ioexp_next_p = ioexp_curr_p->next;
i2c_curr_p = ioexp_curr_p->i2c_head_p;
while (i2c_curr_p) {
i2c_next_p = i2c_curr_p->next;
kfree(i2c_curr_p->i2c_client_p);
kfree(i2c_curr_p);
i2c_curr_p = i2c_next_p;
}
kfree(ioexp_curr_p);
ioexp_curr_p = ioexp_next_p;
}
ioexp_tail_p = NULL;
SWPS_DEBUG("%s: done.\n", __func__);
}
struct ioexp_obj_s *
get_ioexp_obj(int ioexp_id){
struct ioexp_obj_s *result_p = NULL;
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
while(ioexp_curr_p){
if (ioexp_curr_p->ioexp_id == ioexp_id){
result_p = ioexp_curr_p;
break;
}
ioexp_curr_p = ioexp_curr_p->next;
}
return result_p;
}
int
check_channel_tier_1(void) {
if ( (!_is_channel_ready(ioexp_head_p)) &&
(!_is_channel_ready(ioexp_tail_p)) ){
return -1;
}
return 0;
}

View File

@ -0,0 +1,136 @@
#ifndef IO_EXPANDER_H
#define IO_EXPANDER_H
#include <linux/types.h>
/* IOEXP type define (QSFP series) */
#define IOEXP_TYPE_REDWOOD_P01P08 (10202)
#define IOEXP_TYPE_REDWOOD_P09P16 (10203)
/* IOEXP mode define */
#define IOEXP_MODE_DIRECT (19001)
/* IOEXP state define */
#define STATE_IOEXP_NORMAL (0)
#define STATE_IOEXP_INIT (-1)
#define STATE_IOEXP_ABNORMAL (-2)
/* IOEXP error code define */
#define ERR_IOEXP_NOTSUPPORT (-100)
#define ERR_IOEXP_UNINIT (-101)
#define ERR_IOEXP_BADCONF (-102)
#define ERR_IOEXP_BADINPUT (-105)
#define ERR_IOEXP_UNEXCPT (-199)
#define SWPS_INFO(fmt, args...) printk( KERN_INFO "[SWPS] " fmt, ##args)
#define SWPS_WARN(fmt, args...) printk( KERN_WARNING "[SWPS] " fmt, ##args)
#define SWPS_ERR(fmt, args...) printk( KERN_ERR "[SWPS] " fmt, ##args)
#ifdef DEBUG_SWPS
# define SWPS_DEBUG(fmt, args...) printk( KERN_DEBUG "[SWPS] " fmt, ##args)
#else
# define SWPS_DEBUG(fmt, args...)
#endif
struct ioexp_addr_s {
int chan_id;
int chip_addr;
int read_offset[8];
int write_offset[8];
int conf_offset[8];
uint8_t data_default[8];
uint8_t conf_default[8];
};
struct ioexp_i2c_s {
int chip_id;
struct i2c_client *i2c_client_p;
struct ioexp_i2c_s *next;
};
struct ioexp_bitmap_s {
int chip_id; /* IOEXP chip id */
int ioexp_voffset; /* IOEXP virtual offset */
int bit_shift;
};
struct ioexp_map_s {
int chip_amount; /* Number of chips that IOEXP object content */
int data_width; /* Number of (Read/Write/Config) bytes */
struct ioexp_addr_s *map_addr; /* Chip address info */
struct ioexp_bitmap_s map_present[8]; /* IOEXP for SFP / QSFP */
struct ioexp_bitmap_s map_reset[8]; /* IOEXP for QSFP */
struct ioexp_bitmap_s map_lpmod[8]; /* IOEXP for QSFP */
struct ioexp_bitmap_s map_modsel[8]; /* IOEXP for QSFP */
};
struct ioexp_data_s {
uint8_t data[8];
};
struct ioexp_obj_s {
/* ============================
* Object public property
* ============================
*/
int ioexp_id;
int ioexp_type;
/* ============================
* Object private property
* ============================
*/
struct ioexp_data_s chip_data[16]; /* Max: 8-ioexp in one virt-ioexp(ioexp_obj) */
struct ioexp_map_s *ioexp_map_p;
struct ioexp_obj_s *next;
struct ioexp_i2c_s *i2c_head_p;
struct mutex lock;
int mode;
int state;
/* ===========================================
* Object public functions
* ===========================================
*/
int (*get_present)(struct ioexp_obj_s *self, int virt_offset);
int (*get_reset)(struct ioexp_obj_s *self, int virt_offset);
int (*get_lpmod)(struct ioexp_obj_s *self, int virt_offset);
int (*get_modsel)(struct ioexp_obj_s *self, int virt_offset);
int (*set_reset)(struct ioexp_obj_s *self, int virt_offset, int input_val);
int (*set_lpmod)(struct ioexp_obj_s *self, int virt_offset, int input_val);
int (*set_modsel)(struct ioexp_obj_s *self, int virt_offset, int input_val);
/* ===========================================
* Object private functions
* ===========================================
*/
int (*init)(struct ioexp_obj_s *self);
int (*update_all)(struct ioexp_obj_s *self, int show_err, char *caller_name);
int (*fsm_4_direct)(struct ioexp_obj_s* self);
};
struct ioexp_obj_s* get_ioexp_obj(int ioexp_id);
int create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode);
int init_ioexp_objs(void);
void clean_ioexp_objs(void);
int check_channel_tier_1(void);
/* Macro for bit control */
#define SWP_BIT_SET(byte_val,bit_shift) ((byte_val) |= (1<<(bit_shift)))
#define SWP_BIT_CLEAR(byte_val,bit_shift) ((byte_val) &= ~(1<<(bit_shift)))
#endif /* IO_EXPANDER_H */

View File

@ -0,0 +1,898 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/kobject.h>
#include <linux/delay.h>
#include "io_expander.h"
#include "transceiver.h"
/* ========== Register EEPROM address mapping ==========
*/
struct eeprom_map_s eeprom_map_qsfp = {
.addr_rx_los =0x50, .page_rx_los =-1, .offset_rx_los =3, .length_rx_los =1,
.addr_tx_disable =0x50, .page_tx_disable =-1, .offset_tx_disable =86, .length_tx_disable =1,
.addr_tx_fault =0x50, .page_tx_fault =-1, .offset_tx_fault =4, .length_tx_fault =1,
};
struct eeprom_map_s eeprom_map_qsfp28 = {
.addr_rx_los =0x50, .page_rx_los =-1, .offset_rx_los =3, .length_rx_los =1,
.addr_tx_disable =0x50, .page_tx_disable =-1, .offset_tx_disable =86, .length_tx_disable =1,
.addr_tx_fault =0x50, .page_tx_fault =-1, .offset_tx_fault =4, .length_tx_fault =1,
};
/* ========== Utility Functions ==========
*/
void
alarm_msg_2_user(struct transvr_obj_s *self,
char *emsg) {
SWPS_ERR("%s on %s.\n", emsg, self->swp_name);
}
/* ========== Private functions ==========
*/
static int
_reload_transvr_obj(struct transvr_obj_s *self,int new_type);
static int
reload_transvr_obj(struct transvr_obj_s *self,int new_type);
static int
_transvr_init_handler(struct transvr_obj_s *self);
static void
_transvr_clean_retry(struct transvr_obj_s *self) {
self->retry = 0;
}
static int
_transvr_handle_retry(struct transvr_obj_s *self, int retry) {
/* Return: 0: keep retry
* -1: stop retry
*/
if (self->retry == 0) {
self->retry = retry;
}
self->retry -= 1;
if (self->retry <= 0) {
_transvr_clean_retry(self);
return -1;
}
return 0;
}
static int
_common_setup_page(struct transvr_obj_s *self,
int addr,
int page,
int offset,
int len,
int show_e) {
/* return:
* 0 : OK
* -1 : EEPROM settings incorrect
* -2 : I2C R/W failure
* -3 : Undefined case
*/
int retval = DEBUG_TRANSVR_INT_VAL;
char *emsg = DEBUG_TRANSVR_STR_VAL;
/* Check */
if ((addr < 0) || (offset < 0) || (len < 0)) {
emsg = "EEPROM settings incorrect";
retval = -1;
goto err_common_setup_page;
}
/* Case1: continue access */
if ((self->i2c_client_p->addr == addr) &&
(self->curr_page == page)) {
return 0;
}
self->i2c_client_p->addr = addr;
/* Case2: select lower page */
if (page == -1) {
self->curr_page = page;
return 0;
}
/* Case3: select upper page */
if (page >= 0) {
goto upper_common_setup_page;
}
/* Unexpected case */
show_e = 1;
emsg = "Unexpected case";
retval = -3;
goto err_common_setup_page;
upper_common_setup_page:
if (i2c_smbus_write_byte_data(self->i2c_client_p,
VAL_TRANSVR_PAGE_SELECT_OFFSET,
page) < 0) {
emsg = "I2C R/W failure";
retval = -2;
goto err_common_setup_page;
}
self->curr_page = page;
mdelay(VAL_TRANSVR_PAGE_SELECT_DELAY);
return 0;
err_common_setup_page:
if (show_e) {
SWPS_INFO("%s: %s", __func__, emsg);
SWPS_INFO("%s: <addr>:0x%02x <page>:%d <offs>:%d <len>:%d\n",
__func__, addr, page, offset, len);
}
return retval;
}
/* ========== Object functions for Final State Machine ==========
*/
int
is_plugged(struct transvr_obj_s *self){
int limit = 63;
int present = DEBUG_TRANSVR_INT_VAL;
char emsg[64] = DEBUG_TRANSVR_STR_VAL;
struct ioexp_obj_s *ioexp_p = self->ioexp_obj_p;
if (!ioexp_p) {
snprintf(emsg, limit, "ioexp_p is null!");
goto err_is_plugged_1;
}
present = ioexp_p->get_present(ioexp_p, self->ioexp_virt_offset);
switch (present){
case 0:
return 1;
case 1:
return 0;
case ERR_IOEXP_UNINIT:
snprintf(emsg, limit, "ioexp_p not ready!");
goto err_is_plugged_1;
default:
if (ioexp_p->state == STATE_IOEXP_INIT){
snprintf(emsg, limit, "ioexp_p not ready!");
goto err_is_plugged_1;
}
break;
}
SWPS_INFO("%s: Exception case! <pres>:%d <istate>:%d\n",
__func__, present, ioexp_p->state);
return 0;
err_is_plugged_1:
SWPS_DEBUG("%s: %s\n", __func__, emsg);
return 0;
}
static int
detect_transvr_type(struct transvr_obj_s* self){
int type = TRANSVR_TYPE_ERROR;
self->i2c_client_p->addr = VAL_TRANSVR_COMID_ARREESS;
type = i2c_smbus_read_byte_data(self->i2c_client_p,
VAL_TRANSVR_COMID_OFFSET);
/* Case: 1. Wait transceiver I2C module.
* 2. Transceiver I2C module failure.
* Note: 1. SFF allow maximum transceiver initial time is 2 second. So, there
* are exist some case that we need to wait transceiver.
* For these case, we keeps status on "TRANSVR_TYPE_UNPLUGGED", than
* state machine will keep trace with it.
* 2. There exist some I2C failure case we need to handle. Such as user
* insert the failure transceiver, or any reason cause it abnormal.
*/
if (type < 0){
switch (type) {
case -EIO:
SWPS_DEBUG("%s: %s smbus return:-5 (I/O error)\n",
__func__, self->swp_name);
return TRANSVR_TYPE_UNPLUGGED;
case -ENXIO:
SWPS_DEBUG("%s: %s smbus return:-6 (No such device or address)\n",
__func__, self->swp_name);
return TRANSVR_TYPE_UNPLUGGED;
default:
break;
}
SWPS_INFO("%s: %s unexpected smbus return:%d \n",
__func__, self->swp_name, type);
return TRANSVR_TYPE_ERROR;
}
/* Identify valid transceiver type */
switch (type){
case TRANSVR_TYPE_SFP:
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
break;
case TRANSVR_TYPE_UNKNOW_1:
case TRANSVR_TYPE_UNKNOW_2:
type = TRANSVR_TYPE_UNKNOW_2;
break;
default:
SWPS_DEBUG("%s: unknow type:0x%02x \n", __func__, type);
type = TRANSVR_TYPE_ERROR;
break;
}
return type;
}
static int
detect_transvr_state(struct transvr_obj_s *self,
int result[2]){
/* [return] [result-0] [result-1]
* 0 STATE_TRANSVR_CONNECTED TRANSVR_TYPE_FAKE
* 0 STATE_TRANSVR_DISCONNECTED TRANSVR_TYPE_UNPLUGGED
* 0 STATE_TRANSVR_ISOLATED TRANSVR_TYPE_ERROR
* 0 STATE_TRANSVR_INIT <NEW_TYPE>/<OLD_TYPE>
* 0 STATE_TRANSVR_SWAPPED <NEW_TYPE>
* 0 STATE_TRANSVR_CONNECTED <OLD_TYPE>
* ERR_TRNASVR_BE_ISOLATED STATE_TRANSVR_ISOLATED TRANSVR_TYPE_ERROR <Isolated>
* ERR_TRANSVR_I2C_CRASH STATE_TRANSVR_UNEXCEPTED TRANSVR_TYPE_ERROR <New event>
* ERR_TRANSVR_UNEXCPT STATE_TRANSVR_UNEXCEPTED TRANSVR_TYPE_UNKNOW_1/2
*/
result[0] = STATE_TRANSVR_UNEXCEPTED; /* For return state */
result[1] = TRANSVR_TYPE_ERROR; /* For return type */
/* Case1: Fake type */
if (self->type == TRANSVR_TYPE_FAKE){
result[0] = STATE_TRANSVR_CONNECTED;
result[1] = TRANSVR_TYPE_FAKE;
return 0;
}
/* Case2: Transceiver unplugged */
if (!is_plugged(self)){
result[0] = STATE_TRANSVR_DISCONNECTED;
result[1] = TRANSVR_TYPE_UNPLUGGED;
return 0;
}
/* Case3: Transceiver be isolated */
if (self->state == STATE_TRANSVR_ISOLATED){
result[0] = STATE_TRANSVR_ISOLATED;
result[1] = TRANSVR_TYPE_ERROR;
return ERR_TRNASVR_BE_ISOLATED;
}
/* Case4: Transceiver plugged */
result[1] = detect_transvr_type(self);
/* Case4.1: I2C topology crash
* Note : There are some I2C issues cause by transceiver/cables.
* We need to check topology status when user insert it.
* But in this step, we can't not ensure this is the issues
* port. So, it return the ERR_TRANSVR_I2C_CRASH, then upper
* layer will diagnostic I2C topology.
*/
if (check_channel_tier_1() < 0) {
SWPS_INFO("%s: %s detect I2C crash <obj-state>:%d\n",
__func__, self->swp_name, self->state);
result[0] = STATE_TRANSVR_UNEXCEPTED;
result[1] = TRANSVR_TYPE_ERROR;
return ERR_TRANSVR_I2C_CRASH;
}
/* Case4.2: System initial not ready,
* Note : Sometime i2c channel or transceiver EEPROM will delay that will
* cause system in inconsistent state between EEPROM and IOEXP.
* In this case, SWP transceiver object keep state at LINK_DOWN
* to wait system ready.
* By the way, State Machine will handle these case.
*/
if (result[1] == TRANSVR_TYPE_UNPLUGGED){
result[0] = STATE_TRANSVR_DISCONNECTED;
return 0;
}
/* Case4.3: Error transceiver type */
if (result[1] == TRANSVR_TYPE_ERROR){
result[0] = STATE_TRANSVR_ISOLATED;
SWPS_INFO("%s: %s detect error type\n", __func__, self->swp_name);
alarm_msg_2_user(self, "detected transceiver/cables not meet SFF standard!");
return ERR_TRNASVR_BE_ISOLATED;
}
/* Case3.3: Unknow transceiver type */
if ((result[1] == TRANSVR_TYPE_UNKNOW_1) ||
(result[1] == TRANSVR_TYPE_UNKNOW_2) ){
result[0] = STATE_TRANSVR_UNEXCEPTED;
return ERR_TRANSVR_UNEXCPT;
}
/* Case3.4: During initial process */
if (self->state == STATE_TRANSVR_INIT){
result[0] = STATE_TRANSVR_INIT;
return 0;
}
/* Case3.5: Transceiver be swapped */
if (self->type != result[1]){
result[0] = STATE_TRANSVR_SWAPPED;
return 0;
}
/* Case3.6: Link up state */
result[0] = STATE_TRANSVR_CONNECTED;
return 0;
}
int
common_fsm_4_direct_mode(struct transvr_obj_s* self,
char *caller_name){
int err;
int detect_result[2];
int current_state = STATE_TRANSVR_UNEXCEPTED;
int current_type = TRANSVR_TYPE_ERROR;
if (self->state == STATE_TRANSVR_NEW) {
if (_transvr_init_handler(self) < 0){
return ERR_TRANSVR_INIT_FAIL;
}
}
err = detect_transvr_state(self, detect_result);
if (err < 0) {
return err;
}
/* In Direct mode, driver only detect transceiver when user call driver interface
* which on sysfs. So it only need consider the state of Transceiver.
*/
current_state = detect_result[0];
current_type = detect_result[1];
switch (current_state){
case STATE_TRANSVR_DISCONNECTED: /* Transceiver is not plugged */
self->state = current_state;
self->type = current_type;
return ERR_TRANSVR_UNPLUGGED;
case STATE_TRANSVR_INIT: /* Transceiver is plugged, system not ready */
return ERR_TRANSVR_UNINIT;
case STATE_TRANSVR_ISOLATED: /* Transceiver is plugged, but has some issues */
return ERR_TRNASVR_BE_ISOLATED;
case STATE_TRANSVR_CONNECTED: /* Transceiver is plugged, system is ready */
self->state = current_state;
self->type = current_type;
return 0;
case STATE_TRANSVR_SWAPPED: /* Transceiver is plugged, system detect user changed */
self->type = current_type;
if (reload_transvr_obj(self, current_type) < 0){
self->state = STATE_TRANSVR_UNEXCEPTED;
return ERR_TRANSVR_UNEXCPT;
}
self->state = current_state;
return 0;
case STATE_TRANSVR_UNEXCEPTED: /* Transceiver type or state is unexpected case */
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = TRANSVR_TYPE_ERROR;
return ERR_TRANSVR_UNEXCPT;
default:
SWPS_INFO("%s: state:%d not in define.\n", __func__, current_state);
break;
}
return ERR_TRANSVR_UNEXCPT;
}
int
fake_fsm_4_direct_mode(struct transvr_obj_s* self,
char *caller_name){
self->state = STATE_TRANSVR_CONNECTED;
self->type = TRANSVR_TYPE_FAKE;
return 0;
}
/* ========== Object Initial handler ==========
*/
static int
_is_transvr_valid(struct transvr_obj_s *self,
int type,
int state) {
/* [Return]
* 0 : OK, inserted
* EVENT_TRANSVR_INIT_DOWN : OK, removed
* EVENT_TRANSVR_INIT_FAIL : Outside error, type doesn't supported
* EVENT_TRANSVR_EXCEP_INIT : Internal error, state undefined
*/
switch (type) {
case TRANSVR_TYPE_SFP:
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
case TRANSVR_TYPE_UNPLUGGED:
case TRANSVR_TYPE_FAKE:
break;
default:
SWPS_INFO("detect undefined type:0x%02x on %s\n",
type, self->swp_name);
return EVENT_TRANSVR_INIT_FAIL;
}
switch (state) {
case STATE_TRANSVR_DISCONNECTED:
return EVENT_TRANSVR_INIT_DOWN;
case STATE_TRANSVR_INIT:
case STATE_TRANSVR_CONNECTED:
case STATE_TRANSVR_SWAPPED:
break;
default:
SWPS_INFO("detect undefined state:%d on %s\n",
state, self->swp_name);
return EVENT_TRANSVR_EXCEP_INIT;
}
return 0;
}
static int
_is_transvr_hw_ready(struct transvr_obj_s *self,
int type){
/* [Return]
* EVENT_TRANSVR_TASK_DONE : Ready
* EVENT_TRANSVR_TASK_WAIT : Not ready
* EVENT_TRANSVR_INIT_FAIL : Error
*/
int addr = DEBUG_TRANSVR_INT_VAL;
int page = DEBUG_TRANSVR_INT_VAL;
int offs = DEBUG_TRANSVR_INT_VAL;
int bit = DEBUG_TRANSVR_INT_VAL;
int ready = DEBUG_TRANSVR_INT_VAL;
int err = DEBUG_TRANSVR_INT_VAL;
char *emsg = DEBUG_TRANSVR_STR_VAL;
uint8_t ab_val = DEBUG_TRANSVR_HEX_VAL;
switch (type) {
case TRANSVR_TYPE_SFP:
addr = VAL_TRANSVR_8472_READY_ADDR;
page = VAL_TRANSVR_8472_READY_PAGE;
offs = VAL_TRANSVR_8472_READY_OFFSET;
bit = VAL_TRANSVR_8472_READY_BIT;
ready = VAL_TRANSVR_8472_READY_VALUE;
ab_val = VAL_TRANSVR_8472_READY_ABNORMAL;
break;
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
addr = VAL_TRANSVR_8436_READY_ADDR;
page = VAL_TRANSVR_8436_READY_PAGE;
offs = VAL_TRANSVR_8436_READY_OFFSET;
bit = VAL_TRANSVR_8436_READY_BIT;
ready = VAL_TRANSVR_8436_READY_VALUE;
ab_val = VAL_TRANSVR_8436_READY_ABNORMAL;
break;
case TRANSVR_TYPE_UNPLUGGED:
case TRANSVR_TYPE_FAKE:
return EVENT_TRANSVR_TASK_DONE;
default:
emsg = "unexpected case";
goto err_is_transvr_hw_ready;
}
/* Select target page */
err = _common_setup_page(self, addr, page, offs, 1, 0);
if (err < 0) {
emsg = "setup page fail";
goto err_is_transvr_hw_ready;
}
/* Check feature supported
* [Note]
* Some of transceiver/cables doesn't support "Status Indicators"
* (ex:DAC, RJ45 copper SFP ...etc). In these case, we bypass the
* step of checking Status Indicators, then state machine will take
* the following handle procedure.
*/
err = i2c_smbus_read_byte_data(self->i2c_client_p,
VAL_TRANSVR_COMID_OFFSET);
if (err < 0) {
emsg = "doesn't support Status Indicators";
goto bypass_is_transvr_hw_ready;
}
/* Filter abnormal case */
if (err == ab_val) {
emsg = "detect using unusual definition.";
goto bypass_is_transvr_hw_ready;
}
/* Get Status Indicators */
err = i2c_smbus_read_byte_data(self->i2c_client_p, offs);
if (err < 0) {
emsg = "detect current value fail";
goto err_is_transvr_hw_ready;
}
if ((err & (1<<bit)) == ready) {
return EVENT_TRANSVR_TASK_DONE;
}
return EVENT_TRANSVR_TASK_WAIT;
bypass_is_transvr_hw_ready:
SWPS_DEBUG("%s: %s <type>:%d\n", __func__, emsg, type);
return EVENT_TRANSVR_TASK_DONE;
err_is_transvr_hw_ready:
SWPS_DEBUG("%s: %s <type>:%d\n", __func__, emsg, type);
return EVENT_TRANSVR_INIT_FAIL;
}
static int
_transvr_init_handler(struct transvr_obj_s *self){
int detect[2];
int d_state = STATE_TRANSVR_UNEXCEPTED;
int d_type = TRANSVR_TYPE_ERROR;
int result = ERR_TRANSVR_UNINIT;
int retry = 6; /* (6+1) x 0.3 = 2.1s > spec:2.0s */
int elimit = 63;
char emsg[64] = DEBUG_TRANSVR_STR_VAL;
/* Clean and check callback */
self->state = STATE_TRANSVR_INIT;
if (self->init == NULL) {
snprintf(emsg, elimit, "init() is null");
goto initer_err_case_unexcept_0;
}
/* Detect transceiver information */
result = detect_transvr_state(self, detect);
if (result < 0) {
snprintf(emsg, elimit, "detect_transvr_state() fail");
switch (result) {
case ERR_TRANSVR_I2C_CRASH:
goto initer_err_case_i2c_ceash;
case ERR_TRNASVR_BE_ISOLATED:
goto initer_err_case_be_isolated;
case ERR_TRANSVR_UNEXCPT:
default:
break;
}
goto initer_err_case_retry_1;
}
d_state = detect[0];
d_type = detect[1];
/* Verify transceiver type and state */
switch (_is_transvr_valid(self, d_type, d_state)) {
case 0:
break;
case EVENT_TRANSVR_INIT_DOWN:
goto initer_ok_case_down;;
case EVENT_TRANSVR_INIT_FAIL:
snprintf(emsg, elimit, "transceiver type doesn't support");
goto initer_err_case_alarm_to_user;
case EVENT_TRANSVR_EXCEP_INIT:
default:
goto initer_err_case_unexcept_0;
}
/* Handle reload case */
if (self->type != d_type){
/* This is the protect mechanism. Normally, This case will not happen.
* When State machine detect swap event during initial, It will trigger
* reload function to ensure type correct. */
if (_reload_transvr_obj(self, d_type) < 0){
snprintf(emsg, elimit, "reload object fail");
goto initer_err_case_unexcept_0;
}
}
/* Check transceiver HW initial ready */
switch (_is_transvr_hw_ready(self, d_type)) {
case EVENT_TRANSVR_TASK_DONE:
break;
case EVENT_TRANSVR_TASK_WAIT:
goto initer_err_case_retry_1;
case EVENT_TRANSVR_INIT_FAIL:
default:
goto initer_err_case_unexcept_0;
}
/* Try to update all and check */
if (self->update_all(self, 1) < 0){
/* For some transceiver, EEPROME has lag issues during initial stage.
* In this case, we set status back to STATE_TRANSVR_NEW, than it will
* be checked in next polling cycle. */
goto initer_err_case_retry_1;
}
/* Execute init() call back */
result = self->init(self);
switch (result) {
case EVENT_TRANSVR_TASK_DONE:
break;
case EVENT_TRANSVR_TASK_WAIT:
goto initer_ok_case_wait;
default:
snprintf(emsg, elimit, "undefined init() return:%d\n", result);
goto initer_err_case_unexcept_0;
}
goto initer_ok_case_up;
initer_ok_case_wait:
return EVENT_TRANSVR_TASK_WAIT;
initer_ok_case_up:
self->state = STATE_TRANSVR_CONNECTED;
self->temp = 0;
return EVENT_TRANSVR_INIT_UP;
initer_ok_case_down:
self->temp = 0;
self->state = STATE_TRANSVR_DISCONNECTED;
return EVENT_TRANSVR_INIT_DOWN;
initer_err_case_i2c_ceash:
SWPS_DEBUG("%s: %s <port>:%s <case>:I2C crash\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_UNEXCEPTED;
return EVENT_TRANSVR_I2C_CRASH;
initer_err_case_be_isolated:
SWPS_DEBUG("%s: %s <port>:%s <case>:isolated\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_ISOLATED;
return EVENT_TRANSVR_EXCEP_ISOLATED;
initer_err_case_retry_1:
SWPS_DEBUG("%s: %s <port>:%s <case>:retry\n",
__func__, emsg, self->swp_name);
if (_transvr_handle_retry(self, retry) == 0) {
self->state = STATE_TRANSVR_NEW;
return EVENT_TRANSVR_INIT_REINIT;
}
goto initer_err_case_alarm_to_user;
initer_err_case_unexcept_0:
self->state = STATE_TRANSVR_UNEXCEPTED;
return EVENT_TRANSVR_INIT_FAIL;
initer_err_case_alarm_to_user:
SWPS_DEBUG("%s: %s <port>:%s <case>:alarm_to_user\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_UNEXCEPTED;
alarm_msg_2_user(self, "detected transceiver/cables not meet SFF standard");
return EVENT_TRANSVR_INIT_FAIL;
}
static int
setup_transvr_private_cb(struct transvr_obj_s *self,
int transvr_type){
switch (transvr_type){
case TRANSVR_TYPE_SFP:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_QSFP_28:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_FAKE:
self->fsm_4_direct = fake_fsm_4_direct_mode;
return 0;
default:
break;
}
SWPS_WARN("%s: Detect non-defined type:%d\n", __func__, transvr_type);
return ERR_TRANSVR_UNEXCPT;
}
static struct eeprom_map_s *
get_eeprom_map(int transvr_type){
switch (transvr_type){
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
return &eeprom_map_qsfp;
case TRANSVR_TYPE_QSFP_28:
return &eeprom_map_qsfp28;
default:
break;
}
SWPS_WARN("%s: Detect non-defined type:%d\n", __func__, transvr_type);
return NULL;
}
static int
setup_transvr_ssize_attr(char *swp_name,
struct transvr_obj_s *self,
struct eeprom_map_s *map_p,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int chan_id,
int run_mode){
switch (run_mode){
case TRANSVR_MODE_DIRECT: /* Direct access device mode */
self->mode = run_mode;
break;
default:
SWPS_ERR("%s: non-defined run_mode:%d\n",
__func__, run_mode);
self->mode = DEBUG_TRANSVR_INT_VAL;
return -1;
}
self->eeprom_map_p = map_p;
self->ioexp_obj_p = ioexp_obj_p;
self->ioexp_virt_offset = ioexp_virt_offset;
self->chan_id = chan_id;
self->layout = transvr_type;
self->type = transvr_type;
self->chipset_type = chipset_type;
self->state = STATE_TRANSVR_NEW;
self->info = STATE_TRANSVR_NEW;
self->auto_tx_disable = VAL_TRANSVR_FUNCTION_DISABLE;
strncpy(self->swp_name, swp_name, 32);
mutex_init(&self->lock);
return 0;
}
static int
setup_i2c_client(struct transvr_obj_s *self){
struct i2c_adapter *adap = NULL;
struct i2c_client *client = NULL;
char err_msg[64] = DEBUG_TRANSVR_STR_VAL;
adap = i2c_get_adapter(self->chan_id);
if(!adap){
snprintf(err_msg, sizeof(err_msg),
"can not get adap:%d", self->chan_id);
goto err_setup_i2c_client;
}
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client){
snprintf(err_msg, sizeof(err_msg),
"can not kzalloc client:%d", self->chan_id);
goto err_setup_i2c_client;
}
client->adapter = adap;
self->i2c_client_p = client;
self->i2c_client_p->addr = VAL_TRANSVR_COMID_ARREESS;
return 0;
err_setup_i2c_client:
SWPS_ERR("%s: %s\n", __func__, err_msg);
return ERR_TRANSVR_UNEXCPT;
}
struct transvr_obj_s *
create_transvr_obj(char *swp_name,
int chan_id,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int run_mode){
struct transvr_obj_s *result_p;
struct eeprom_map_s *map_p;
char err_msg[64] = DEBUG_TRANSVR_STR_VAL;
/* Allocate transceiver object */
map_p = get_eeprom_map(transvr_type);
if (!map_p){
snprintf(err_msg, sizeof(err_msg),
"Invalid transvr_type:%d", transvr_type);
goto err_create_transvr_fail;
}
result_p = kzalloc(sizeof(*result_p), GFP_KERNEL);
if (!result_p){
snprintf(err_msg, sizeof(err_msg), "kzalloc fail");
goto err_create_transvr_fail;
}
/* Prepare static size attributes */
if (setup_transvr_ssize_attr(swp_name,
result_p,
map_p,
ioexp_obj_p,
ioexp_virt_offset,
transvr_type,
chipset_type,
chan_id,
run_mode) < 0){
goto err_create_transvr_sattr_fail;
}
/* Prepare call back functions of object */
if (setup_transvr_private_cb(result_p, transvr_type) < 0){
goto err_create_transvr_sattr_fail;
}
/* Prepare i2c client object */
if (setup_i2c_client(result_p) < 0){
goto err_create_transvr_sattr_fail;
}
return result_p;
err_create_transvr_sattr_fail:
kfree(result_p);
err_create_transvr_fail:
SWPS_ERR("%s: %s <chan>:%d <voff>:%d <type>:%d\n",
__func__, err_msg, chan_id, ioexp_virt_offset, transvr_type);
return NULL;
}
static int
_reload_transvr_obj(struct transvr_obj_s *self,
int new_type){
struct eeprom_map_s *new_map_p;
struct eeprom_map_s *old_map_p = self->eeprom_map_p;
struct i2c_client *old_i2c_p = self->i2c_client_p;
int old_type = self->type;
/* Change state to STATE_TRANSVR_INIT */
self->state = STATE_TRANSVR_INIT;
self->type = new_type;
/* Replace EEPROME map */
new_map_p = get_eeprom_map(new_type);
if (!new_map_p){
goto err_private_reload_func_1;
}
self->eeprom_map_p = new_map_p;
/* Reload i2c client */
if (setup_i2c_client(self) < 0){
goto err_private_reload_func_2;
}
if (setup_transvr_private_cb(self, new_type) < 0){
goto err_private_reload_func_3;
}
kfree(old_i2c_p);
return 0;
err_private_reload_func_3:
SWPS_INFO("%s: init() fail!\n", __func__);
kfree(old_i2c_p);
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = TRANSVR_TYPE_ERROR;
return -2;
err_private_reload_func_2:
self->eeprom_map_p = old_map_p;
self->i2c_client_p = old_i2c_p;
err_private_reload_func_1:
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = old_type;
SWPS_INFO("%s fail! <type>:0x%02x\n", __func__, new_type);
return -1;
}
static int
reload_transvr_obj(struct transvr_obj_s *self,
int new_type){
int result_val = ERR_TRANSVR_UNEXCPT;
/* Reload phase */
result_val = _reload_transvr_obj(self, new_type);
if (result_val < 0){
SWPS_INFO("%s: reload phase fail! <err>:%d\n",
__func__, result_val);
return EVENT_TRANSVR_RELOAD_FAIL;
}
/* Initial phase */
result_val = _transvr_init_handler(self);
if (result_val < 0){
SWPS_INFO("%s: initial phase fail! <err>:%d\n",
__func__, result_val);
}
return result_val;
}

View File

@ -0,0 +1,167 @@
#ifndef TRANSCEIVER_H
#define TRANSCEIVER_H
#include <linux/types.h>
/* Transceiver type define */
#define TRANSVR_TYPE_UNKNOW_1 (0x00)
#define TRANSVR_TYPE_UNKNOW_2 (0xff)
#define TRANSVR_TYPE_SFP (0x03) /* Define for SFP, SFP+, SFP28 */
#define TRANSVR_TYPE_QSFP (0x0c)
#define TRANSVR_TYPE_QSFP_PLUS (0x0d)
#define TRANSVR_TYPE_QSFP_28 (0x11)
#define TRANSVR_TYPE_UNPLUGGED (0xfa) /* Define for ERROR handle */
#define TRANSVR_TYPE_FAKE (0xfc) /* Define for ERROR handle */
#define TRANSVR_TYPE_INCONSISTENT (0xfd) /* Define for ERROR handle */
#define TRANSVR_TYPE_ERROR (0xfe) /* Define for ERROR handle */
/* Transceiver mode define */
#define TRANSVR_MODE_DIRECT (21000)
/* Transceiver state define
* [Note]
* 1. State is used to represent the state of "Transceiver" and "Object".
* 2. State for different target has different means. The description as following:
*/
#define STATE_TRANSVR_CONNECTED (0) /* [Transvr]:Be plugged in. [Obj]:Link up, and work normally. */
#define STATE_TRANSVR_NEW (-100) /* [Transvr]:(Not used) [Obj]:Create */
#define STATE_TRANSVR_INIT (-101) /* [Transvr]:Be plugged in. [Obj]:Link up, and in initial process. */
#define STATE_TRANSVR_ISOLATED (-102) /* [Transvr]:Be plugged in. [Obj]:Isolate, and not provide service. */
#define STATE_TRANSVR_SWAPPED (-200) /* [Transvr]:Be plugged in. [Obj]:(Not used) */
#define STATE_TRANSVR_DISCONNECTED (-300) /* [Transvr]:Un-plugged. [Obj]:Link down, and not provide service. */
#define STATE_TRANSVR_UNEXCEPTED (-901) /* [Transvr]:Any [Obj]:Any, and not in expect case. */
/* Event for task handling */
#define EVENT_TRANSVR_TASK_WAIT (2101)
#define EVENT_TRANSVR_TASK_DONE (0)
#define EVENT_TRANSVR_TASK_FAIL (-2101)
/* Event for initial handling */
#define EVENT_TRANSVR_INIT_UP (2201)
#define EVENT_TRANSVR_INIT_DOWN (1)
#define EVENT_TRANSVR_INIT_REINIT (-2201)
#define EVENT_TRANSVR_INIT_FAIL (-2202)
/* Event for others */
#define EVENT_TRANSVR_RELOAD_FAIL (-2301)
#define EVENT_TRANSVR_EXCEP_INIT (-2401)
#define EVENT_TRANSVR_EXCEP_UP (-2402)
#define EVENT_TRANSVR_EXCEP_DOWN (-2403)
#define EVENT_TRANSVR_EXCEP_SWAP (-2404)
#define EVENT_TRANSVR_EXCEP_EXCEP (-2405)
#define EVENT_TRANSVR_EXCEP_ISOLATED (-2406)
#define EVENT_TRANSVR_I2C_CRASH (-2501)
/* Transceiver error code define */
#define ERR_TRANSVR_UNINIT (-201)
#define ERR_TRANSVR_UNPLUGGED (-202)
#define ERR_TRANSVR_ABNORMAL (-203)
#define ERR_TRANSVR_NOSTATE (-204)
#define ERR_TRANSVR_NOTSUPPORT (-205)
#define ERR_TRANSVR_BADINPUT (-206)
#define ERR_TRANSVR_UPDATE_FAIL (-207)
#define ERR_TRANSVR_RELOAD_FAIL (-208)
#define ERR_TRANSVR_INIT_FAIL (-209)
#define ERR_TRANSVR_UNDEFINED (-210)
#define ERR_TRANSVR_TASK_FAIL (-211)
#define ERR_TRANSVR_TASK_BUSY (-212)
#define ERR_TRANSVR_FUNC_DISABLE (-214)
#define ERR_TRANSVR_I2C_CRASH (-297)
#define ERR_TRNASVR_BE_ISOLATED (-298)
#define ERR_TRANSVR_UNEXCPT (-299)
/* For debug */
#define DEBUG_TRANSVR_INT_VAL (-99)
#define DEBUG_TRANSVR_HEX_VAL (0xfe)
#define DEBUG_TRANSVR_STR_VAL "ERROR"
/* For system internal */
#define VAL_TRANSVR_COMID_ARREESS (0x50)
#define VAL_TRANSVR_COMID_OFFSET (0x00)
#define VAL_TRANSVR_8472_READY_ADDR (0x51)
#define VAL_TRANSVR_8472_READY_PAGE (-1)
#define VAL_TRANSVR_8472_READY_OFFSET (110)
#define VAL_TRANSVR_8472_READY_BIT (0)
#define VAL_TRANSVR_8472_READY_VALUE (0)
#define VAL_TRANSVR_8472_READY_ABNORMAL (0xff)
#define VAL_TRANSVR_8436_READY_ADDR (0x50)
#define VAL_TRANSVR_8436_READY_PAGE (-1)
#define VAL_TRANSVR_8436_READY_OFFSET (2)
#define VAL_TRANSVR_8436_READY_BIT (0)
#define VAL_TRANSVR_8436_READY_VALUE (0)
#define VAL_TRANSVR_8436_READY_ABNORMAL (0xff)
#define VAL_TRANSVR_8436_PWD_ADDR (0x50)
#define VAL_TRANSVR_8436_PWD_PAGE (-1)
#define VAL_TRANSVR_8436_PWD_OFFSET (123)
#define VAL_TRANSVR_PAGE_FREE (-99)
#define VAL_TRANSVR_PAGE_SELECT_OFFSET (127)
#define VAL_TRANSVR_PAGE_SELECT_DELAY (5)
#define VAL_TRANSVR_TASK_RETRY_FOREVER (-999)
#define VAL_TRANSVR_FUNCTION_DISABLE (-1)
#define STR_TRANSVR_QSFP "QSFP"
#define STR_TRANSVR_QSFP_PLUS "QSFP+"
#define STR_TRANSVR_QSFP28 "QSFP28"
/* BCM chip type define */
#define BCM_CHIP_TYPE_TOMAHAWK (31002) /* Redwood, Cypress */
/* Info from transceiver EEPROM */
struct eeprom_map_s {
int addr_rx_los; int page_rx_los; int offset_rx_los; int length_rx_los;
int addr_tx_disable; int page_tx_disable; int offset_tx_disable; int length_tx_disable;
int addr_tx_fault; int page_tx_fault; int offset_tx_fault; int length_tx_fault;
};
/* Class of transceiver object */
struct transvr_obj_s {
/* ========== Object private property ==========
*/
struct device *transvr_dev_p;
struct eeprom_map_s *eeprom_map_p;
struct i2c_client *i2c_client_p;
struct ioexp_obj_s *ioexp_obj_p;
struct mutex lock;
char swp_name[32];
int auto_tx_disable;
int chan_id;
int chipset_type;
int curr_page;
int info;
int ioexp_virt_offset;
int lane_id[8];
int layout;
int mode;
int retry;
int state;
int temp;
int type;
/* ========== Object private functions ==========
*/
int (*init)(struct transvr_obj_s *self);
int (*update_all)(struct transvr_obj_s *self, int show_err);
int (*fsm_4_direct)(struct transvr_obj_s* self, char *caller_name);
};
/* For AVL Mapping */
struct transvr_avl_s {
char vendor_name[32];
char vendor_pn[32];
int (*init)(struct transvr_obj_s *self);
};
struct transvr_obj_s *
create_transvr_obj(char *swp_name,
int chan_id,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int run_mode);
void alarm_msg_2_user(struct transvr_obj_s *self, char *emsg);
#endif /* TRANSCEIVER_H */

View File

@ -0,0 +1,217 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 Inventec, 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 3 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, see <http://www.gnu.org/licenses/>.
"""
Usage: %(scriptName)s [options] command object
options:
-h | --help : this help message
-d | --debug : run with debug mode
-f | --force : ignore error during installation or clean
command:
install : install drivers and generate related sysfs nodes
clean : uninstall drivers and remove related sysfs nodes
"""
import os
import commands
import sys, getopt
import logging
import re
import time
from collections import namedtuple
DEBUG = False
args = []
FORCE = 0
i2c_prefix = '/sys/bus/i2c/devices/'
if DEBUG == True:
print sys.argv[0]
print 'ARGV :', sys.argv[1:]
def main():
global DEBUG
global args
global FORCE
if len(sys.argv)<2:
show_help()
options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help',
'debug',
'force',
])
if DEBUG == True:
print options
print args
print len(sys.argv)
for opt, arg in options:
if opt in ('-h', '--help'):
show_help()
elif opt in ('-d', '--debug'):
DEBUG = True
logging.basicConfig(level=logging.INFO)
elif opt in ('-f', '--force'):
FORCE = 1
else:
logging.info('no option')
for arg in args:
if arg == 'install':
install()
elif arg == 'clean':
uninstall()
else:
show_help()
return 0
def show_help():
print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]}
sys.exit(0)
def show_log(txt):
if DEBUG == True:
print "[D7032]"+txt
return
def exec_cmd(cmd, show):
logging.info('Run :'+cmd)
status, output = commands.getstatusoutput(cmd)
show_log (cmd +"with result:" + str(status))
show_log (" output:"+output)
if status:
logging.info('Failed :'+cmd)
if show:
print('Failed :'+cmd)
return status, output
instantiate =[
#'echo pca9545 0x70> /sys/bus/i2c/devices/i2c-0/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-1/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-2/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-3/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-4/new_device',
#'echo inv_psoc 0x66> /sys/bus/i2c/devices/i2c-5/new_device',
#'echo inv_cpld 0x55> /sys/bus/i2c/devices/i2c-5/new_device',
'echo inv_eeprom 0x53> /sys/bus/i2c/devices/i2c-0/new_device']
drivers =[
'lpc_ich',
'i2c-i801',
'i2c-mux',
'i2c-mux-pca954x',
'i2c-dev',
'inv_eeprom',
'inv_platform',
'inv_psoc',
'inv_cpld',
'swps']
def system_install():
global FORCE
#remove default drivers to avoid modprobe order conflicts
status, output = exec_cmd("rmmod i2c_ismt ", 1)
status, output = exec_cmd("rmmod i2c-i801 ", 1)
#install drivers
for i in range(0,len(drivers)):
status, output = exec_cmd("modprobe "+drivers[i], 1)
if status:
print output
if FORCE == 0:
return status
#instantiate devices
for i in range(0,len(instantiate)):
time.sleep(1)
status, output = exec_cmd(instantiate[i], 1)
if status:
print output
if FORCE == 0:
return status
for i in range(22,30):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-4/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(30,38):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-5/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(6,14):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-2/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(14,22):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-3/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
return
def system_ready():
if not device_found():
return False
return True
def install():
if not device_found():
print "No device, installing...."
status = system_install()
if status:
if FORCE == 0:
return status
else:
print " D7032 devices detected...."
return
def uninstall():
global FORCE
#uninstall drivers
for i in range(len(drivers)-1,-1,-1):
status, output = exec_cmd("rmmod "+drivers[i], 1)
if status:
print output
if FORCE == 0:
return status
return
def device_found():
ret1, log = exec_cmd("ls "+i2c_prefix+"*0072", 0)
ret2, log = exec_cmd("ls "+i2c_prefix+"i2c-2", 0)
return not(ret1 or ret2)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,6 @@
obj-m += inv_cpld.o inv_psoc.o
obj-m += inv_platform.o
obj-m += inv_eeprom.o
obj-m += swps.o
swps-objs := inv_swps.o io_expander.o transceiver.o

View File

@ -0,0 +1,415 @@
/*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.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 "I2CHostCommunication.h"
#define USE_SMBUS 1
/* definition */
#define CPLD_INFO_OFFSET 0x00
#define CPLD_PSU_OFFSET 0x08
#define CPLD_LED_OFFSET 0x0E
#define CPLD_LED_STATU_OFFSET 0x0D
#define CPLD_CTL_OFFSET 0x0C
/* Each client has this additional data */
struct cpld_data {
struct device *hwmon_dev;
struct mutex update_lock;
};
/*-----------------------------------------------------------------------*/
static ssize_t cpld_i2c_read(struct i2c_client *client, u8 *buf, u8 offset, size_t count)
{
#if USE_SMBUS
int i;
for(i=0; i<count; i++) {
buf[i] = i2c_smbus_read_byte_data(client, offset+i);
}
return count;
#else
struct i2c_msg msg[2];
char msgbuf[2];
int status;
memset(msg, 0, sizeof(msg));
msgbuf[0] = offset;
msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
status = i2c_transfer(client->adapter, msg, 2);
if(status == 2)
status = count;
return status;
#endif
}
static ssize_t cpld_i2c_write(struct i2c_client *client, char *buf, unsigned offset, size_t count)
{
#if USE_SMBUS
int i;
for(i=0; i<count; i++) {
i2c_smbus_write_byte_data(client, offset+i, buf[i]);
}
return count;
#else
struct i2c_msg msg;
int status;
u8 writebuf[64];
int i = 0;
msg.addr = client->addr;
msg.flags = 0;
/* msg.buf is u8 and casts will mask the values */
msg.buf = writebuf;
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
status = i2c_transfer(client->adapter, &msg, 1);
if (status == 1)
status = count;
return status;
#endif
}
/*-----------------------------------------------------------------------*/
/* sysfs attributes for hwmon */
static ssize_t show_info(struct device *dev, struct device_attribute *da,
char *buf)
{
u32 status;
//struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
u8 b[4];
memset(b, 0, 4);
mutex_lock(&data->update_lock);
status = cpld_i2c_read(client, b, CPLD_INFO_OFFSET, 4);
mutex_unlock(&data->update_lock);
if(status != 4) return sprintf(buf, "read cpld info fail\n");
status = sprintf (buf, "The CPLD release date is %02d/%02d/%d.\n", b[2] & 0xf, (b[3] & 0x1f), 2014+(b[2] >> 4)); /* mm/dd/yyyy*/
status = sprintf (buf, "%sThe PCB version is %X%X\n", buf, b[0]>>4, b[0]&0xf);
status = sprintf (buf, "%sThe CPLD version is %d.%d\n", buf, b[1]>>4, b[1]&0xf);
return strlen(buf);
}
static ssize_t show_ctl(struct device *dev, struct device_attribute *da,
char *buf)
{
u32 status;
//struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
u8 b[1];
mutex_lock(&data->update_lock);
status = cpld_i2c_read(client, b, CPLD_CTL_OFFSET, 1);
mutex_unlock(&data->update_lock);
if(status != 1) return sprintf(buf, "read cpld ctl fail\n");
status = sprintf (buf, "0x%X\n", b[0]);
return strlen(buf);
}
static ssize_t set_ctl(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 cpld_data *data = i2c_get_clientdata(client);
u8 byte;
u8 temp = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
cpld_i2c_read(client, &byte, CPLD_CTL_OFFSET, 1);
if(temp) byte |= (1<<0);
else byte &= ~(1<<0);
cpld_i2c_write(client, &byte, CPLD_CTL_OFFSET, 1);
mutex_unlock(&data->update_lock);
return count;
}
static char* led_str[] = {
"OFF", //000
"0.5 Hz", //001
"1 Hz", //010
"2 Hz", //011
"NA", //100
"NA", //101
"NA", //110
"ON", //111
};
static ssize_t show_led(struct device *dev, struct device_attribute *da,
char *buf)
{
u32 status;
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
u8 byte;
int shift = (attr->index == 0)?3:0;
mutex_lock(&data->update_lock);
status = cpld_i2c_read(client, &byte, CPLD_LED_OFFSET, 1);
mutex_unlock(&data->update_lock);
if(status != 1) return sprintf(buf, "read cpld offset 0x%x\n", CPLD_LED_OFFSET);
byte = (byte >> shift) & 0x7;
/*
0: off
1: 0.5hz
2: 1 hz
3: 2 hz
4~6: not define
7: on
*/
status = sprintf (buf, "%d: %s\n", byte, led_str[byte]);
return strlen(buf);
}
static ssize_t set_led(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 cpld_data *data = i2c_get_clientdata(client);
u8 temp = simple_strtol(buf, NULL, 16);
u8 byte;
int shift = (attr->index == 0)?3:0;
temp &= 0x7;
//validate temp value: 0,1,2,3,7, TBD
mutex_lock(&data->update_lock);
cpld_i2c_read(client, &byte, CPLD_LED_OFFSET, 1);
byte &= ~(0x7<<shift);
byte |= (temp<<shift);
cpld_i2c_write(client, &byte, CPLD_LED_OFFSET, 1);
mutex_unlock(&data->update_lock);
return count;
}
/*
CPLD report the PSU0 status
000 = PSU normal operation
100 = PSU fault
010 = PSU unpowered
111 = PSU not installed
7 6 | 5 4 3 | 2 1 0
----------------------
| psu0 | psu1
*/
static char* psu_str[] = {
"normal", //000
"NA", //001
"unpowered", //010
"NA", //011
"fault", //100
"NA", //101
"NA", //110
"not installed", //111
};
static ssize_t show_psu(struct device *dev, struct device_attribute *da,
char *buf)
{
u32 status;
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
struct cpld_data *data = i2c_get_clientdata(client);
u8 byte;
int shift = (attr->index == 1)?0:3;
mutex_lock(&data->update_lock);
status = cpld_i2c_read(client, &byte, CPLD_PSU_OFFSET, 1);
mutex_unlock(&data->update_lock);
byte = (byte >> shift) & 0x7;
status = sprintf (buf, "%d : %s\n", byte, psu_str[byte]);
return strlen(buf);
}
static SENSOR_DEVICE_ATTR(info, S_IRUGO, show_info, 0, 0);
static SENSOR_DEVICE_ATTR(ctl, S_IWUSR|S_IRUGO, show_ctl, set_ctl, 0);
static SENSOR_DEVICE_ATTR(grn_led, S_IWUSR|S_IRUGO, show_led, set_led, 0);
static SENSOR_DEVICE_ATTR(red_led, S_IWUSR|S_IRUGO, show_led, set_led, 1);
static SENSOR_DEVICE_ATTR(psu0, S_IRUGO, show_psu, 0, 0);
static SENSOR_DEVICE_ATTR(psu1, S_IRUGO, show_psu, 0, 1);
static struct attribute *cpld_attributes[] = {
//info
&sensor_dev_attr_info.dev_attr.attr,
&sensor_dev_attr_ctl.dev_attr.attr,
&sensor_dev_attr_grn_led.dev_attr.attr,
&sensor_dev_attr_red_led.dev_attr.attr,
&sensor_dev_attr_psu0.dev_attr.attr,
&sensor_dev_attr_psu1.dev_attr.attr,
NULL
};
static const struct attribute_group cpld_group = {
.attrs = cpld_attributes,
};
/*-----------------------------------------------------------------------*/
/* device probe and removal */
static int
cpld_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct cpld_data *data;
int status;
printk("+%s\n", __func__);
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
data = kzalloc(sizeof(struct cpld_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &cpld_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: sensor '%s'\n",
dev_name(data->hwmon_dev), client->name);
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &cpld_group);
exit_free:
i2c_set_clientdata(client, NULL);
kfree(data);
return status;
}
static int cpld_remove(struct i2c_client *client)
{
struct cpld_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &cpld_group);
i2c_set_clientdata(client, NULL);
kfree(data);
return 0;
}
static const struct i2c_device_id cpld_ids[] = {
{ "inv_cpld", 0, },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, cpld_ids);
static struct i2c_driver cpld_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "inv_cpld",
},
.probe = cpld_probe,
.remove = cpld_remove,
.id_table = cpld_ids,
};
/*-----------------------------------------------------------------------*/
/* module glue */
static int __init inv_cpld_init(void)
{
return i2c_add_driver(&cpld_driver);
}
static void __exit inv_cpld_exit(void)
{
i2c_del_driver(&cpld_driver);
}
MODULE_AUTHOR("eddie.lan <eddie.lan@inventec>");
MODULE_DESCRIPTION("inv cpld driver");
MODULE_LICENSE("GPL");
module_init(inv_cpld_init);
module_exit(inv_cpld_exit);

View File

@ -0,0 +1,181 @@
/*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
#define SLICE_BITS (6)
#define SLICE_SIZE (1 << SLICE_BITS)
#define SLICE_NUM (EEPROM_SIZE/SLICE_SIZE)
/* Each client has this additional data */
struct eeprom_data {
struct mutex update_lock;
u8 valid; /* bitfield, bit!=0 if slice is valid */
unsigned long last_updated[SLICE_NUM]; /* In jiffies, 8 slices */
u8 data[EEPROM_SIZE]; /* Register values */
};
static void inv_eeprom_update_client(struct i2c_client *client, u8 slice)
{
struct eeprom_data *data = i2c_get_clientdata(client);
int i, j;
int ret;
int addr;
mutex_lock(&data->update_lock);
if (!(data->valid & (1 << slice)) ||
time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
addr = slice << SLICE_BITS;
ret = i2c_smbus_write_byte_data(client, ((u8)addr >> 8) & 0xFF, (u8)addr & 0xFF);
/* select the eeprom address */
if (ret < 0) {
dev_err(&client->dev, "address set failed\n");
goto exit;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
goto exit;
}
for (i = slice << SLICE_BITS; i < (slice + 1) << SLICE_BITS; i+= SLICE_SIZE) {
for (j = i; j < (i+SLICE_SIZE); j++) {
int res;
res = i2c_smbus_read_byte(client);
if (res < 0) {
goto exit;
}
data->data[j] = res & 0xFF;
}
}
data->last_updated[slice] = jiffies;
data->valid |= (1 << slice);
}
exit:
mutex_unlock(&data->update_lock);
}
static ssize_t inv_eeprom_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
u8 slice;
if (off > EEPROM_SIZE) {
return 0;
}
if (off + count > EEPROM_SIZE) {
count = EEPROM_SIZE - off;
}
if (count == 0) {
return 0;
}
/* Only refresh slices which contain requested bytes */
for (slice = off >> SLICE_BITS; slice <= (off + count - 1) >> SLICE_BITS; slice++) {
inv_eeprom_update_client(client, slice);
}
memcpy(buf, &data->data[off], count);
return count;
}
static struct bin_attribute inv_eeprom_attr = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
},
.size = EEPROM_SIZE,
.read = inv_eeprom_read,
};
static int inv_eeprom_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct eeprom_data *data;
int err;
if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data->data, 0xff, EEPROM_SIZE);
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* create the sysfs eeprom file */
err = sysfs_create_bin_file(&client->dev.kobj, &inv_eeprom_attr);
if (err) {
goto exit_kfree;
}
return 0;
exit_kfree:
kfree(data);
exit:
return err;
}
static int inv_eeprom_remove(struct i2c_client *client)
{
sysfs_remove_bin_file(&client->dev.kobj, &inv_eeprom_attr);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id inv_eeprom_id[] = {
{ "inv_eeprom", 0 },
{ }
};
static struct i2c_driver inv_eeprom_driver = {
.driver = {
.name = "inv_eeprom",
},
.probe = inv_eeprom_probe,
.remove = inv_eeprom_remove,
.id_table = inv_eeprom_id,
};
module_i2c_driver(inv_eeprom_driver);
MODULE_AUTHOR("Inventec");
MODULE_DESCRIPTION("Inventec D7054 Mother Board EEPROM driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,219 @@
#include <linux/i2c.h>
//#include <linux/i2c-algo-bit.h>
#include <linux/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/i2c/pca954x.h>
//#include <linux/i2c/pca953x.h>
//#include <linux/i2c/at24.h>
//#include <asm/gpio.h>
//#define IO_EXPAND_BASE 64
//#define IO_EXPAND_NGPIO 16
struct inv_i2c_board_info {
int ch;
int size;
struct i2c_board_info *board_info;
};
#define bus_id(id) (id)
static struct pca954x_platform_mode mux_modes_0[] = {
{.adap_id = bus_id(2),}, {.adap_id = bus_id(3),},
{.adap_id = bus_id(4),}, {.adap_id = bus_id(5),},
{.adap_id = bus_id(6),}, {.adap_id = bus_id(7),},
{.adap_id = bus_id(8),}, {.adap_id = bus_id(9),},
};
static struct pca954x_platform_mode mux_modes_0_0[] = {
{.adap_id = bus_id(10),}, {.adap_id = bus_id(11),},
{.adap_id = bus_id(12),}, {.adap_id = bus_id(13),},
{.adap_id = bus_id(14),}, {.adap_id = bus_id(15),},
{.adap_id = bus_id(16),}, {.adap_id = bus_id(17),},
};
static struct pca954x_platform_mode mux_modes_0_1[] = {
{.adap_id = bus_id(18),}, {.adap_id = bus_id(19),},
{.adap_id = bus_id(20),}, {.adap_id = bus_id(21),},
{.adap_id = bus_id(22),}, {.adap_id = bus_id(23),},
{.adap_id = bus_id(24),}, {.adap_id = bus_id(25),},
};
static struct pca954x_platform_mode mux_modes_0_2[] = {
{.adap_id = bus_id(26),}, {.adap_id = bus_id(27),},
{.adap_id = bus_id(28),}, {.adap_id = bus_id(29),},
{.adap_id = bus_id(30),}, {.adap_id = bus_id(31),},
{.adap_id = bus_id(32),}, {.adap_id = bus_id(33),},
};
static struct pca954x_platform_mode mux_modes_0_3[] = {
{.adap_id = bus_id(34),}, {.adap_id = bus_id(35),},
{.adap_id = bus_id(36),}, {.adap_id = bus_id(37),},
{.adap_id = bus_id(38),}, {.adap_id = bus_id(39),},
{.adap_id = bus_id(40),}, {.adap_id = bus_id(41),},
};
static struct pca954x_platform_mode mux_modes_0_4[] = {
{.adap_id = bus_id(42),}, {.adap_id = bus_id(43),},
{.adap_id = bus_id(44),}, {.adap_id = bus_id(45),},
{.adap_id = bus_id(46),}, {.adap_id = bus_id(47),},
{.adap_id = bus_id(48),}, {.adap_id = bus_id(49),},
};
static struct pca954x_platform_mode mux_modes_0_5[] = {
{.adap_id = bus_id(50),}, {.adap_id = bus_id(51),},
{.adap_id = bus_id(52),}, {.adap_id = bus_id(53),},
{.adap_id = bus_id(54),}, {.adap_id = bus_id(55),},
{.adap_id = bus_id(56),}, {.adap_id = bus_id(57),},
};
static struct pca954x_platform_mode mux_modes_0_6[] = {
{.adap_id = bus_id(58),}, {.adap_id = bus_id(59),},
{.adap_id = bus_id(60),}, {.adap_id = bus_id(61),},
{.adap_id = bus_id(62),}, {.adap_id = bus_id(63),},
{.adap_id = bus_id(64),}, {.adap_id = bus_id(65),},
};
//no i2c device driver attach to mux 7
static struct pca954x_platform_data mux_data_0 = {
.modes = mux_modes_0,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_0 = {
.modes = mux_modes_0_0,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_1 = {
.modes = mux_modes_0_1,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_2 = {
.modes = mux_modes_0_2,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_3 = {
.modes = mux_modes_0_3,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_4 = {
.modes = mux_modes_0_4,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_5 = {
.modes = mux_modes_0_5,
.num_modes = 8,
};
static struct pca954x_platform_data mux_data_0_6 = {
.modes = mux_modes_0_6,
.num_modes = 8,
};
static struct i2c_board_info i2c_device_info0[] __initdata = {
{"inv_psoc", 0, 0x66, 0, 0, 0},//psoc
{"inv_cpld", 0, 0x55, 0, 0, 0},//cpld
{"pca9548", 0, 0x71, &mux_data_0, 0, 0},
};
static struct i2c_board_info i2c_device_info2[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_0, 0, 0},
};
static struct i2c_board_info i2c_device_info3[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_1, 0, 0},
};
static struct i2c_board_info i2c_device_info4[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_2, 0, 0},
};
static struct i2c_board_info i2c_device_info5[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_3, 0, 0},
};
static struct i2c_board_info i2c_device_info6[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_4, 0, 0},
};
static struct i2c_board_info i2c_device_info7[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_5, 0, 0},
};
static struct i2c_board_info i2c_device_info8[] __initdata = {
{"pca9548", 0, 0x72, &mux_data_0_6, 0, 0},
};
static struct inv_i2c_board_info i2cdev_list[] = {
{0, ARRAY_SIZE(i2c_device_info0), i2c_device_info0 }, //smbus 0
{bus_id(2), ARRAY_SIZE(i2c_device_info2), i2c_device_info2 }, //mux 0
{bus_id(3), ARRAY_SIZE(i2c_device_info3), i2c_device_info3 }, //mux 1
{bus_id(4), ARRAY_SIZE(i2c_device_info4), i2c_device_info4 }, //mux 2
{bus_id(5), ARRAY_SIZE(i2c_device_info5), i2c_device_info5 }, //mux 3
{bus_id(6), ARRAY_SIZE(i2c_device_info6), i2c_device_info6 }, //mux 4
{bus_id(7), ARRAY_SIZE(i2c_device_info7), i2c_device_info7 }, //mux 5
{bus_id(8), ARRAY_SIZE(i2c_device_info8), i2c_device_info8 }, //mux 6
};
/////////////////////////////////////////////////////////////////////////////////////////
#if 0
static struct i2c_gpio_platform_data i2c_gpio_platdata0 = {
.scl_pin = 58,
.sda_pin = 75,
.udelay = 5, //5:100kHz
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 0
};
static struct platform_device device_i2c_gpio0 = {
.name = "i2c-gpio",
.id = 0, // adapter number
.dev.platform_data = &i2c_gpio_platdata0,
};
#endif
static int __init inv_platform_init(void)
{
struct i2c_adapter *adap = NULL;
struct i2c_client *e = NULL;
int ret = 0;
int i,j;
printk("%s \n", __func__);
#if 0
//use i2c-gpio
//register i2c gpio
//config gpio58,75 to gpio function 58=32+3*8+2 75=32*2+8*1+3
outl( inl(0x533) | (1<<2), 0x533);
outl( inl(0x541) | (1<<3), 0x541);
ret = platform_device_register(&device_i2c_gpio0);
if (ret) {
printk(KERN_ERR "i2c-gpio: device_i2c_gpio0 register fail %d\n", ret);
}
#endif
for(i=0; i<ARRAY_SIZE(i2cdev_list); i++) {
adap = i2c_get_adapter( i2cdev_list[i].ch );
if (adap == NULL) {
printk("cypress get channel %d adapter fail\n", i);
continue;
return -ENODEV;
}
i2c_put_adapter(adap);
for(j=0; j<i2cdev_list[i].size; j++) {
e = i2c_new_device(adap, &i2cdev_list[i].board_info[j] );
}
}
return ret;
}
module_init(inv_platform_init);
//arch_initcall(inv_platform_init);
MODULE_AUTHOR("Inventec");
MODULE_DESCRIPTION("Cypess Platform devices");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,730 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/dmi.h>
#include "inv_swps.h"
static int port_major;
static int ioexp_total;
static int port_total;
static struct class *swp_class_p = NULL;
static struct inv_platform_s *platform_p = NULL;
static struct inv_ioexp_layout_s *ioexp_layout = NULL;
static struct inv_port_layout_s *port_layout = NULL;
static int
__swp_match(struct device *dev,
#ifdef SWPS_KERN_VER_AF_3_10
const void *data){
#else
void *data){
#endif
char *name = (char *)data;
if (strcmp(dev_name(dev), name) == 0)
return 1;
return 0;
}
struct device *
get_swpdev_by_name(char *name){
struct device *dev = class_find_device(swp_class_p,
NULL,
name,
(const void *)__swp_match);
return dev;
}
static int
sscanf_2_int(const char *buf) {
int result = -EBFONT;
char *hex_tag = "0x";
if (strcspn(buf, hex_tag) == 0) {
if (sscanf(buf,"%x",&result)) {
return result;
}
} else {
if (sscanf(buf,"%d",&result)) {
return result;
}
if(sscanf(buf,"-%d",&result)) {
return -result;
}
if (sscanf(buf,"%x",&result)) {
return result;
}
}
return -EBFONT;
}
static int
sscanf_2_binary(const char *buf) {
int result = sscanf_2_int(buf);
if (result < 0){
return -EBFONT;
}
switch (result) {
case 0:
case 1:
return result;
default:
break;
}
return -EBFONT;
}
/* ========== Show functions: For I/O Expander attribute ==========
*/
static ssize_t
_show_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
int (*get_func)(struct ioexp_obj_s *ioexp_p, int voffset),
char *buf_p) {
size_t len;
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
if (!ioexp_p) {
SWPS_ERR(" %s: data corruption! <port>:%s\n", __func__, tobj_p->swp_name);
return -ENODATA;
}
mutex_lock(&ioexp_p->lock);
len = snprintf(buf_p, 8, "%d\n", get_func(ioexp_p, tobj_p->ioexp_virt_offset));
mutex_unlock(&ioexp_p->lock);
return len;
}
static ssize_t
show_attr_present(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_present,
buf_p);
}
static ssize_t
show_attr_tx_fault(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_tx_fault,
buf_p);
}
static ssize_t
show_attr_rxlos(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_rxlos,
buf_p);
}
static ssize_t
show_attr_tx_disable(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_tx_disable,
buf_p);
}
static ssize_t
show_attr_reset(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_reset,
buf_p);
}
static ssize_t
show_attr_lpmod(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_lpmod,
buf_p);
}
static ssize_t
show_attr_modsel(struct device *dev_p,
struct device_attribute *attr_p,
char *buf_p){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p){
return -ENODEV;
}
return _show_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->get_modsel,
buf_p);
}
/* ========== Store functions: For I/O Expander (R/W) attribute ==========
*/
static ssize_t
_store_ioexp_binary_attr(struct transvr_obj_s *tobj_p,
int (*set_func)(struct ioexp_obj_s *ioexp_p,
int virt_offset, int input_val),
const char *buf_p,
size_t count) {
int input, err;
struct ioexp_obj_s *ioexp_p = tobj_p->ioexp_obj_p;
if (!ioexp_p) {
SWPS_ERR("%s: data corruption! <port>:%s\n",
__func__, tobj_p->swp_name);
return -ENODATA;
}
input = sscanf_2_binary(buf_p);
if (input < 0) {
return -EBFONT;
}
mutex_lock(&ioexp_p->lock);
err = set_func(ioexp_p, tobj_p->ioexp_virt_offset, input);
mutex_unlock(&ioexp_p->lock);
if (err < 0){
return err;
}
return count;
}
static ssize_t
store_attr_tx_disable(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_tx_disable,
buf_p,
count);
}
static ssize_t
store_attr_reset(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_reset,
buf_p,
count);
}
static ssize_t
store_attr_lpmod(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_lpmod,
buf_p,
count);
}
static ssize_t
store_attr_modsel(struct device *dev_p,
struct device_attribute *attr_p,
const char *buf_p,
size_t count){
struct transvr_obj_s *tobj_p = dev_get_drvdata(dev_p);
if (!tobj_p) {
return -ENODEV;
}
return _store_ioexp_binary_attr(tobj_p,
tobj_p->ioexp_obj_p->set_modsel,
buf_p,
count);
}
/* ========== IO Expander attribute: from expander ==========
*/
static DEVICE_ATTR(present, S_IRUGO, show_attr_present, NULL);
static DEVICE_ATTR(tx_fault, S_IRUGO, show_attr_tx_fault, NULL);
static DEVICE_ATTR(rxlos, S_IRUGO, show_attr_rxlos, NULL);
static DEVICE_ATTR(tx_disable, S_IRUGO|S_IWUSR, show_attr_tx_disable, store_attr_tx_disable);
static DEVICE_ATTR(reset, S_IRUGO|S_IWUSR, show_attr_reset, store_attr_reset);
static DEVICE_ATTR(lpmod, S_IRUGO|S_IWUSR, show_attr_lpmod, store_attr_lpmod);
static DEVICE_ATTR(modsel, S_IRUGO|S_IWUSR, show_attr_modsel, store_attr_modsel);
/* ========== Functions for module handling ==========
*/
static void
clean_port_obj(void){
dev_t dev_num;
char dev_name[32];
struct device *device_p;
struct transvr_obj_s *transvr_obj_p;
int minor_curr, port_id;
for (minor_curr=0; minor_curr<port_total; minor_curr++){
port_id = port_layout[minor_curr].port_id;
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, sizeof(dev_name), "%s%d", SWP_DEV_PORT, port_id);
device_p = get_swpdev_by_name(dev_name);
if (!device_p){
continue;
}
transvr_obj_p = dev_get_drvdata(device_p);
if (transvr_obj_p){
kfree(transvr_obj_p->i2c_client_p);
kfree(transvr_obj_p);
}
dev_num = MKDEV(port_major, minor_curr);
device_unregister(device_p);
device_destroy(swp_class_p, dev_num);
}
SWPS_DEBUG("%s: done.\n", __func__);
}
static int
get_platform_type(void){
char log_msg[64] = "ERROR";
platform_p = kzalloc(sizeof(struct inv_platform_s), GFP_KERNEL);
if (!platform_p){
snprintf(log_msg, sizeof(log_msg), "kzalloc fail");
goto err_get_platform_type_1;
}
platform_p->id = PLATFORM_SETTINGS;
memset(platform_p->name, 0, sizeof(platform_p->name));
snprintf(platform_p->name, (sizeof(platform_p->name) - 1),
"%s", platform_map.name);
snprintf(log_msg, sizeof(log_msg),
"User setup platform: %d (%s)",
platform_p->id, platform_p->name);
SWPS_DEBUG("%s: %s, <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
return 0;
err_get_platform_type_1:
SWPS_ERR("%s: %s <conf>:%d\n", __func__, log_msg, PLATFORM_SETTINGS);
return -1;
}
static int
get_layout_info(void){
ioexp_layout = cypress_ga2_ioexp_layout;
port_layout = cypress_ga2_port_layout;
ioexp_total = ARRAY_SIZE(cypress_ga2_ioexp_layout);
port_total = ARRAY_SIZE(cypress_ga2_port_layout);
SWPS_INFO("Start to initial platform: %d (%s)\n",
platform_p->id, platform_p->name);
return 0;
}
/* ========== Functions for register something ==========
*/
static int
register_ioexp_attr_sfp_1(struct device *device_p){
/* Support machine type:
* - SFP : Magnolia
*/
char *err_attr = NULL;
if (device_create_file(device_p, &dev_attr_present) < 0) {
err_attr = "dev_attr_present";
goto err_ioexp_sfp1_attr;
}
if (device_create_file(device_p, &dev_attr_tx_fault) < 0) {
err_attr = "dev_attr_tx_fault";
goto err_ioexp_sfp1_attr;
}
if (device_create_file(device_p, &dev_attr_rxlos) < 0) {
err_attr = "dev_attr_rxlos";
goto err_ioexp_sfp1_attr;
}
if (device_create_file(device_p, &dev_attr_tx_disable) < 0) {
err_attr = "dev_attr_tx_disable";
goto err_ioexp_sfp1_attr;
}
return 0;
err_ioexp_sfp1_attr:
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
return -1;
}
static int
register_ioexp_attr_sfp_2(struct device *device_p){
/* Support machine type:
* - SFP28 : Cypress
*/
char *err_attr = NULL;
if (register_ioexp_attr_sfp_1(device_p) < 0){
goto err_ioexp_sfp2_attr;
}
return 0;
err_ioexp_sfp2_attr:
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
return -1;
}
static int
register_ioexp_attr_qsfp_1(struct device *device_p){
/* Support machine type:
* - QSFP : Magnolia, Redwood, Hudson32i
* - QSFP+ : Magnolia, Redwood, Hudson32i
* - QSFP28: Redwood
*/
char *err_attr = NULL;
if (device_create_file(device_p, &dev_attr_present) < 0) {
err_attr = "dev_attr_present";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_reset) < 0) {
err_attr = "dev_attr_reset";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_lpmod) < 0) {
err_attr = "dev_attr_lpmod";
goto err_ioexp_qsfp1_attr;
}
if (device_create_file(device_p, &dev_attr_modsel) < 0) {
err_attr = "dev_attr_modsel";
goto err_ioexp_qsfp1_attr;
}
return 0;
err_ioexp_qsfp1_attr:
SWPS_ERR("Add device attribute:%s failure! \n",err_attr);
return -1;
}
static int
register_ioexp_attr(struct device *device_p,
struct transvr_obj_s *transvr_obj){
char *err_msg = "ERR";
switch (transvr_obj->ioexp_obj_p->ioexp_type){
case IOEXP_TYPE_CYPRESS_NABC:
if (register_ioexp_attr_sfp_2(device_p) < 0){
err_msg = "register_ioexp_attr_sfp_2 fail";
goto err_reg_ioexp_attr;
}
break;
case IOEXP_TYPE_CYPRESS_7ABC:
if (register_ioexp_attr_qsfp_1(device_p) < 0){
err_msg = "register_ioexp_attr_qsfp_1 fail";
goto err_reg_ioexp_attr;
}
break;
default:
err_msg = "Unknow type";
goto err_reg_ioexp_attr;
}
return 0;
err_reg_ioexp_attr:
SWPS_ERR("%s: %s <type>:%d \n",
__func__, err_msg, transvr_obj->ioexp_obj_p->ioexp_type);
return -1;
}
static int
register_port_device(char *dev_name,
dev_t dev_num,
struct transvr_obj_s *transvr_obj){
struct device *device_p = NULL;
device_p = device_create(swp_class_p, /* struct class *cls */
NULL, /* struct device *parent */
dev_num, /* dev_t devt */
transvr_obj, /* void *private_data */
dev_name); /* const char *fmt */
if (IS_ERR(device_p)){
goto err_regswp_create_dev;
}
if (register_ioexp_attr(device_p, transvr_obj) < 0){
goto err_regswp_reg_attr;
}
return 0;
err_regswp_reg_attr:
device_unregister(device_p);
device_destroy(swp_class_p, dev_num);
err_regswp_create_dev:
SWPS_ERR("%s fail! <port>:%s\n", __func__, dev_name);
return -1;
}
static int
register_swp_module(void){
dev_t port_devt = 0;
int dev_total = port_total + 1; /* char_dev for module control */
if (alloc_chrdev_region(&port_devt, 0, dev_total, SWP_CLS_NAME) < 0){
SWPS_WARN("Allocate PORT MAJOR failure! \n");
goto err_register_swp_module_3;
}
port_major = MAJOR(port_devt);
/* Create class object */
swp_class_p = class_create(THIS_MODULE, SWP_CLS_NAME);
if (IS_ERR(swp_class_p)) {
SWPS_ERR("Create class failure! \n");
goto err_register_swp_module_3;
}
return 0;
err_register_swp_module_3:
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
return -1;
}
/* ========== Module initial relate ==========
*/
static int
create_ioexp_objs(void) {
int i, run_mod;
/* Clean IOEXP object */
clean_ioexp_objs();
/* Get running mode */
run_mod = IOEXP_MODE_DIRECT;
/* Create IOEXP object */
for(i=0; i<ioexp_total; i++){
if (create_ioexp_obj(ioexp_layout[i].ioexp_id,
ioexp_layout[i].ioexp_type,
(ioexp_layout[i].addr),
run_mod) < 0) {
goto err_initioexp_create_obj_1;
}
}
return 0;
err_initioexp_create_obj_1:
clean_ioexp_objs();
return -1;
}
static int
create_port_objs(void) {
int port_id, chan_id, ioexp_id, ioexp_virt_offset;
int transvr_type, chipset_type, run_mod, i, j;
int minor_curr = 0;
int ok_count = 0;
int devlen_max = 31; // 32 - 1
char dev_name[32] = "ERROR";
char err_msg[64] = "ERROR";
struct transvr_obj_s* transvr_obj_p = NULL;
struct ioexp_obj_s *ioexp_obj_p = NULL;
struct device *dev_p = NULL;
for (minor_curr=0; minor_curr<port_total; minor_curr++) {
/* Get info from port_layout[] */
port_id = port_layout[minor_curr].port_id;
chan_id = port_layout[minor_curr].chan_id;
ioexp_id = port_layout[minor_curr].ioexp_id;
ioexp_virt_offset = port_layout[minor_curr].ioexp_offset;
transvr_type = port_layout[minor_curr].transvr_type;
chipset_type = port_layout[minor_curr].chipset_type;
/* Get running mode */
run_mod = TRANSVR_MODE_DIRECT;
/* Prepare device name */
if (strlen(SWP_DEV_PORT) > devlen_max) {
snprintf(err_msg, sizeof(err_msg),
"SWP_DEV_PORT too long!");
goto err_initport_create_tranobj;
}
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, devlen_max, "%s%d", SWP_DEV_PORT, port_id);
/* Create transceiver object */
ioexp_obj_p = get_ioexp_obj(ioexp_id);
if (!ioexp_obj_p){
snprintf(err_msg, sizeof(err_msg),
"IOEXP object:%d not exist", ioexp_id);
goto err_initport_create_tranobj;
}
transvr_obj_p = create_transvr_obj(dev_name, chan_id, ioexp_obj_p,
ioexp_virt_offset, transvr_type,
chipset_type, run_mod);
if (!transvr_obj_p){
snprintf(err_msg, sizeof(err_msg),
"Create transceiver object fail <id>:%s", dev_name);
goto err_initport_create_tranobj;
}
/* Setup Lane_ID mapping */
i = ARRAY_SIZE(port_layout[minor_curr].lane_id);
j = ARRAY_SIZE(transvr_obj_p->lane_id);
if (i != j) {
snprintf(err_msg, sizeof(err_msg),
"Lane_id size inconsistent %d/%d", i, j);
goto err_initport_reg_device;
}
memcpy(transvr_obj_p->lane_id, port_layout[minor_curr].lane_id, i*sizeof(int));
/* Create and register device object */
if (register_port_device(dev_name, MKDEV(port_major, minor_curr), transvr_obj_p) < 0){
snprintf(err_msg, sizeof(err_msg),
"register_port_device fail");
goto err_initport_reg_device;
}
/* Setup device_ptr of transvr_obj */
dev_p = get_swpdev_by_name(dev_name);
if (!dev_p){
snprintf(err_msg, sizeof(err_msg),
"get_swpdev_by_name fail");
goto err_initport_reg_device;
}
transvr_obj_p->transvr_dev_p = dev_p;
/* Success */
ok_count++;
}
SWPS_INFO("%s: initialed %d port-dev",__func__, ok_count);
return 0;
err_initport_reg_device:
kfree(transvr_obj_p);
err_initport_create_tranobj:
clean_port_obj();
SWPS_ERR("%s: %s", __func__, err_msg);
SWPS_ERR("Dump: <port_id>:%d <chan_id>:%d <ioexp_id>:%d <voffset>:%d <tvr_type>:%d <run_mod>:%d\n",
port_id, chan_id, ioexp_id, ioexp_virt_offset, transvr_type, run_mod);
return -1;
}
static int __init
swp_module_init(void){
if (get_platform_type() < 0){
goto err_init_out;
}
if (get_layout_info() < 0){
goto err_init_out;
}
if (register_swp_module() < 0){
goto err_init_out;
}
if (create_ioexp_objs() < 0){
goto err_init_ioexp;
}
if (create_port_objs() < 0){
goto err_init_portobj;
}
if (init_ioexp_objs() < 0){
goto err_init_portobj;
}
SWPS_INFO("Inventec switch-port module V.%s initial success.\n", SWP_VERSION);
return 0;
err_init_portobj:
clean_ioexp_objs();
err_init_ioexp:
class_unregister(swp_class_p);
class_destroy(swp_class_p);
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
err_init_out:
SWPS_ERR("Inventec switch-port module V.%s initial failure.\n", SWP_VERSION);
return -1;
}
static void __exit
swp_module_exit(void){
clean_port_obj();
clean_ioexp_objs();
class_unregister(swp_class_p);
class_destroy(swp_class_p);
unregister_chrdev_region(MKDEV(port_major, 0), port_total);
SWPS_INFO("Remove Inventec switch-port module success.\n");
}
/* Module information */
MODULE_AUTHOR(SWP_AUTHOR);
MODULE_DESCRIPTION(SWP_DESC);
MODULE_VERSION(SWP_VERSION);
MODULE_LICENSE(SWP_LICENSE);
module_init(swp_module_init);
module_exit(swp_module_exit);

View File

@ -0,0 +1,170 @@
#ifndef INV_SWPS_H
#define INV_SWPS_H
#include "transceiver.h"
#include "io_expander.h"
/* Module settings */
#define SWP_CLS_NAME "swps"
#define SWP_DEV_PORT "port"
#define SWP_AUTOCONFIG_ENABLE (1)
/* Module information */
#define SWP_AUTHOR "Neil <liao.neil@inventec.com>"
#define SWP_DESC "Inventec port and transceiver driver"
#define SWP_VERSION "4.2.3"
#define SWP_LICENSE "GPL"
/* Module status define */
#define SWP_STATE_NORMAL (0)
#define SWP_STATE_I2C_DIE (-91)
/* [Note]:
* Functions and mechanism for auto-detect platform type is ready,
* But HW and BIOS not ready! We need to wait them.
* So, please do not use PLATFORM_TYPE_AUTO until they are ready.
* (2016.06.13)
*/
#define PLATFORM_TYPE_CYPRESS_GA2 (152) /* Down -> Up */
#define PLATFORM_TYPE_CYPRESS_BAI (153) /* Down -> Up */
/* Current running platfrom */
#define PLATFORM_SETTINGS PLATFORM_TYPE_CYPRESS_GA2
/* Define platform flag and kernel version */
#if (PLATFORM_SETTINGS == PLATFORM_TYPE_CYPRESS_GA2)
#define SWPS_KERN_VER_BF_3_8 (1)
#elif (PLATFORM_SETTINGS == PLATFORM_TYPE_CYPRESS_BAI)
#define SWPS_KERN_VER_AF_3_10 (1)
#endif
struct inv_platform_s {
int id;
char name[64];
};
struct inv_ioexp_layout_s {
int ioexp_id;
int ioexp_type;
struct ioexp_addr_s addr[4];
};
struct inv_port_layout_s {
int port_id;
int chan_id;
int ioexp_id;
int ioexp_offset;
int transvr_type;
int chipset_type;
int lane_id[8];
};
/* ==========================================
* Inventec Platform Settings
* ==========================================
*/
struct inv_platform_s platform_map = {PLATFORM_TYPE_CYPRESS_GA2, "D7054Q28B" };
/* ==========================================
* Cypress Layout configuration (Inventec version [Down->Up])
* ==========================================
*/
struct inv_ioexp_layout_s cypress_ga2_ioexp_layout[] = {
/* IOEXP_ID / IOEXP_TYPE / { Chan_ID, Chip_addr, Read_offset, Write_offset, config_offset, data_default, conf_default } */
{0, IOEXP_TYPE_CYPRESS_NABC, { {2, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{2, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{2, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{1, IOEXP_TYPE_CYPRESS_NABC, { {3, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{3, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{3, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{2, IOEXP_TYPE_CYPRESS_NABC, { {4, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{4, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{4, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{3, IOEXP_TYPE_CYPRESS_NABC, { {5, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{5, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{5, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{4, IOEXP_TYPE_CYPRESS_NABC, { {6, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{6, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{6, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{5, IOEXP_TYPE_CYPRESS_NABC, { {7, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[0] = I/O Expander N A */
{7, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xf0}, {0xff, 0xf0}, }, /* addr[1] = I/O Expander N B */
{7, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0x00, 0x00}, }, }, /* addr[2] = I/O Expander N C */
},
{6, IOEXP_TYPE_CYPRESS_7ABC, { {8, 0x20, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xc0, 0xc0}, }, /* addr[0] = I/O Expander 7 A */
{8, 0x21, {0, 1}, {2, 3}, {6, 7}, {0xc0, 0xc0}, {0xff, 0xc0}, }, /* addr[1] = I/O Expander 7 B */
{8, 0x22, {0, 1}, {2, 3}, {6, 7}, {0xff, 0xff}, {0xff, 0xff}, }, }, /* addr[2] = I/O Expander 7 C */
},
};
struct inv_port_layout_s cypress_ga2_port_layout[] = {
/* Port_ID / Chan_ID / IOEXP_ID / IOEXP_VIRT_OFFSET / TRANSCEIVER_TYPE / BCM_CHIP_TYPE / LANE_ID */
{ 0, 11, 0, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 2} },
{ 1, 10, 0, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 1} },
{ 2, 13, 0, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 4} },
{ 3, 12, 0, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 3} },
{ 4, 15, 0, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 6} },
{ 5, 14, 0, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 5} },
{ 6, 17, 0, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 8} },
{ 7, 16, 0, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 7} },
{ 8, 19, 1, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 10} },
{ 9, 18, 1, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 9} },
{10, 21, 1, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 12} },
{11, 20, 1, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 11} },
{12, 23, 1, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 22} },
{13, 22, 1, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 21} },
{14, 25, 1, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 24} },
{15, 24, 1, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 23} },
{16, 27, 2, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 34} },
{17, 26, 2, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 33} },
{18, 29, 2, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 36} },
{19, 28, 2, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 35} },
{20, 31, 2, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 38} },
{21, 30, 2, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 37} },
{22, 33, 2, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 40} },
{23, 32, 2, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 39} },
{24, 35, 3, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 42} },
{25, 34, 3, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 41} },
{26, 37, 3, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 44} },
{27, 36, 3, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 43} },
{28, 39, 3, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 50} },
{29, 38, 3, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 49} },
{30, 41, 3, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 52} },
{31, 40, 3, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 51} },
{32, 43, 4, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 54} },
{33, 42, 4, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 53} },
{34, 45, 4, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 56} },
{35, 44, 4, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 55} },
{36, 47, 4, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 66} },
{37, 46, 4, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 65} },
{38, 49, 4, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 68} },
{39, 48, 4, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 67} },
{40, 51, 5, 1, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 70} },
{41, 50, 5, 0, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 69} },
{42, 53, 5, 3, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 72} },
{43, 52, 5, 2, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 71} },
{44, 55, 5, 5, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 82} },
{45, 54, 5, 4, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 81} },
{46, 57, 5, 7, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 84} },
{47, 56, 5, 6, TRANSVR_TYPE_SFP, BCM_CHIP_TYPE_TOMAHAWK, { 83} },
{48, 59, 6, 1, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 85, 86, 87, 88} },
{49, 58, 6, 0, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, { 97, 98, 99,100} },
{50, 61, 6, 3, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {105,106,107,108} },
{51, 60, 6, 2, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {101,102,103,104} },
{52, 63, 6, 5, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {117,118,119,120} },
{53, 62, 6, 4, TRANSVR_TYPE_QSFP_28, BCM_CHIP_TYPE_TOMAHAWK, {109,110,111,112} },
};
#endif /* SFP_DRIVER_H */

View File

@ -0,0 +1,944 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include "io_expander.h"
static struct ioexp_obj_s *ioexp_head_p = NULL;
static struct ioexp_obj_s *ioexp_tail_p = NULL;
struct ioexp_map_s ioexp_map_cypress_nabc = {
.chip_amount = 3,
.data_width = 2,
.map_present = { {0, 0, 4}, /* map_present[0] = MOD_ABS_PORT(X) */
{0, 0, 5}, /* map_present[1] = MOD_ABS_PORT(X+1) */
{0, 0, 6}, /* map_present[2] = MOD_ABS_PORT(X+2) */
{0, 0, 7}, /* map_present[3] = MOD_ABS_PORT(X+3) */
{1, 0, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
{1, 0, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
{1, 0, 6}, /* map_present[6] = MOD_ABS_PORT(X+6) */
{1, 0, 7}, /* map_present[7] = MOD_ABS_PORT(X+7) */
},
.map_tx_disable = { {0, 1, 0}, /* map_tx_disable[0] = TXDISABLE_SFP+_P(X) */
{0, 1, 1}, /* map_tx_disable[1] = TXDISABLE_SFP+_P(X+1) */
{0, 1, 2}, /* map_tx_disable[2] = TXDISABLE_SFP+_P(X+2) */
{0, 1, 3}, /* map_tx_disable[3] = TXDISABLE_SFP+_P(X+3) */
{1, 1, 0}, /* map_tx_disable[4] = TXDISABLE_SFP+_P(X+4) */
{1, 1, 1}, /* map_tx_disable[5] = TXDISABLE_SFP+_P(X+5) */
{1, 1, 2}, /* map_tx_disable[6] = TXDISABLE_SFP+_P(X+6) */
{1, 1, 3}, /* map_tx_disable[7] = TXDISABLE_SFP+_P(X+7) */
},
.map_tx_fault = { {0, 0, 0}, /* map_tx_fault[0] = TXFAULT_SFP+_P(X) */
{0, 0, 1}, /* map_tx_fault[1] = TXFAULT_SFP+_P(X+1) */
{0, 0, 2}, /* map_tx_fault[2] = TXFAULT_SFP+_P(X+2) */
{0, 0, 3}, /* map_tx_fault[3] = TXFAULT_SFP+_P(X+3) */
{1, 0, 0}, /* map_tx_fault[4] = TXFAULT_SFP+_P(X+4) */
{1, 0, 1}, /* map_tx_fault[5] = TXFAULT_SFP+_P(X+5) */
{1, 0, 2}, /* map_tx_fault[6] = TXFAULT_SFP+_P(X+6) */
{1, 0, 3}, /* map_tx_fault[7] = TXFAULT_SFP+_P(X+7) */
},
.map_rxlos = { {0, 1, 4}, /* map_rxlos[0] = OPRXLOS_PORT(X) */
{0, 1, 5}, /* map_rxlos[1] = OPRXLOS_PORT(X+1) */
{0, 1, 6}, /* map_rxlos[2] = OPRXLOS_PORT(X+2) */
{0, 1, 7}, /* map_rxlos[3] = OPRXLOS_PORT(X+3) */
{1, 1, 4}, /* map_rxlos[4] = OPRXLOS_PORT(X+4) */
{1, 1, 5}, /* map_rxlos[5] = OPRXLOS_PORT(X+5) */
{1, 1, 6}, /* map_rxlos[6] = OPRXLOS_PORT(X+6) */
{1, 1, 7}, /* map_rxlos[7] = OPRXLOS_PORT(X+7) */
},
};
struct ioexp_map_s ioexp_map_cypress_7abc = {
.chip_amount = 3,
.data_width = 2,
.map_present = { {2, 0, 0}, /* map_present[0] = MOD_ABS_PORT(X) */
{2, 0, 1}, /* map_present[1] = MOD_ABS_PORT(X+1) */
{2, 0, 2}, /* map_present[2] = MOD_ABS_PORT(X+2) */
{2, 0, 3}, /* map_present[3] = MOD_ABS_PORT(X+3) */
{2, 0, 4}, /* map_present[4] = MOD_ABS_PORT(X+4) */
{2, 0, 5}, /* map_present[5] = MOD_ABS_PORT(X+5) */
},
.map_reset = { {0, 0, 0}, /* map_reset[0] = QRESET_QSFP_N_P(X) */
{0, 0, 1}, /* map_reset[1] = QRESET_QSFP_N_P(X+1) */
{0, 0, 2}, /* map_reset[2] = QRESET_QSFP_N_P(X+2) */
{0, 0, 3}, /* map_reset[3] = QRESET_QSFP_N_P(X+3) */
{0, 0, 4}, /* map_reset[4] = QRESET_QSFP_N_P(X+4) */
{0, 0, 5}, /* map_reset[5] = QRESET_QSFP_N_P(X+5) */
},
.map_lpmod = { {0, 1, 0}, /* map_lpmod[0] = LPMODE_QSFP_P(X) */
{0, 1, 1}, /* map_lpmod[1] = LPMODE_QSFP_P(X+1) */
{0, 1, 2}, /* map_lpmod[2] = LPMODE_QSFP_P(X+2) */
{0, 1, 3}, /* map_lpmod[3] = LPMODE_QSFP_P(X+3) */
{0, 1, 4}, /* map_lpmod[4] = LPMODE_QSFP_P(X+4) */
{0, 1, 5}, /* map_lpmod[5] = LPMODE_QSFP_P(X+5) */
},
.map_modsel = { {1, 1, 0}, /* map_modsel[0] = MODSEL_QSFP_N_P(X) */
{1, 1, 1}, /* map_modsel[1] = MODSEL_QSFP_N_P(X+1) */
{1, 1, 2}, /* map_modsel[2] = MODSEL_QSFP_N_P(X+2) */
{1, 1, 3}, /* map_modsel[3] = MODSEL_QSFP_N_P(X+3) */
{1, 1, 4}, /* map_modsel[4] = MODSEL_QSFP_N_P(X+4) */
{1, 1, 5}, /* map_modsel[5] = MODSEL_QSFP_N_P(X+5) */
},
};
/* ========== Private functions ==========
*/
int check_channel_tier_1(void);
struct i2c_client *
_get_i2c_client(struct ioexp_obj_s *self,
int chip_id){
struct ioexp_i2c_s *i2c_curr_p = self->i2c_head_p;
if (!(i2c_curr_p)){
SWPS_ERR("%s: i2c_curr_p is NULL\n", __func__);
return NULL;
}
while (i2c_curr_p){
if ((i2c_curr_p->chip_id) == chip_id){
return i2c_curr_p->i2c_client_p;
}
i2c_curr_p = i2c_curr_p->next;
}
SWPS_ERR("%s: not exist! <chip_id>:%d\n", __func__, chip_id);
return NULL;
}
static int
_common_ioexp_update_one(struct ioexp_obj_s *self,
struct ioexp_addr_s *ioexp_addr,
int chip_id,
int data_width,
int show_err,
char *caller_name) {
int buf = 0;
int err = 0;
int data_id = 0;
int r_offset = 0;
for(data_id=0; data_id<data_width; data_id++){
/* Read from IOEXP */
r_offset = ioexp_addr->read_offset[data_id];
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id), r_offset);
/* Check error */
if (buf < 0) {
err = 1;
if (show_err) {
SWPS_INFO("IOEXP-%d read fail! <err>:%d \n", self->ioexp_id, buf);
SWPS_INFO("Dump: <chan>:%d <addr>:0x%02x <offset>:%d, <caller>:%s\n",
ioexp_addr->chan_id, ioexp_addr->chip_addr,
ioexp_addr->read_offset[data_id], caller_name);
}
continue;
}
/* Update IOEXP object */
self->chip_data[chip_id].data[data_id] = (uint8_t)buf;
}
if (err) {
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
static int
common_ioexp_update_all(struct ioexp_obj_s *self,
int show_err,
char *caller_name){
int err = 0;
int chip_id = 0;
int chip_amount = self->ioexp_map_p->chip_amount;
for (chip_id=0; chip_id<chip_amount; chip_id++){
if (_common_ioexp_update_one(self,
&(self->ioexp_map_p->map_addr[chip_id]),
chip_id,
self->ioexp_map_p->data_width,
show_err,
caller_name) < 0) {
err = 1;
}
}
if (err) {
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
static int
_common_get_bit(struct ioexp_obj_s *self,
struct ioexp_bitmap_s *bitmap_obj_p,
char *func_mane){
uint8_t buf;
int err_code;
/* Get address */
err_code = self->fsm_4_direct(self);
if (err_code < 0){
return err_code;
}
if (!bitmap_obj_p){
SWPS_ERR("Layout config incorrect! <ioexp_id>:%d <func>:%s\n",
self->ioexp_id, func_mane);
return ERR_IOEXP_BADCONF;
}
/* Get data form cache */
buf = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
return (int)(buf >> bitmap_obj_p->bit_shift & 0x01);
}
static int
_common_set_bit(struct ioexp_obj_s *self,
struct ioexp_bitmap_s *bitmap_obj_p,
int input_val,
char *func_mane){
int err_code, target_offset;
uint8_t origin_byte;
uint8_t modify_byte;
/* Get address */
err_code = self->fsm_4_direct(self);
if (err_code < 0){
return err_code;
}
if (!bitmap_obj_p){
SWPS_ERR("Layout config incorrect! <ioexp>:%d <func>:%s\n",
self->ioexp_id, func_mane);
return ERR_IOEXP_BADCONF;
}
/* Prepare write date */
origin_byte = self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset];
switch (input_val) {
case 0:
modify_byte = origin_byte;
SWP_BIT_CLEAR(modify_byte, bitmap_obj_p->bit_shift);
break;
case 1:
modify_byte = origin_byte;
SWP_BIT_SET(modify_byte, bitmap_obj_p->bit_shift);
break;
default:
SWPS_ERR("Input value incorrect! <val>:%d <ioexp>:%d <func>:%s\n",
input_val, self->ioexp_id, func_mane);
return ERR_IOEXP_BADINPUT;
}
/* Setup i2c client */
target_offset = self->ioexp_map_p->map_addr[bitmap_obj_p->chip_id].write_offset[bitmap_obj_p->ioexp_voffset];
/* Write byte to chip via I2C */
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, bitmap_obj_p->chip_id),
target_offset,
modify_byte);
/* Update or bollback object */
if (err_code < 0){
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = origin_byte;
SWPS_ERR("I2C write fail! <input>:%d <ioexp>:%d <func>:%s <err>:%d\n",
input_val, self->ioexp_id, func_mane, err_code);
return err_code;
}
self->chip_data[bitmap_obj_p->chip_id].data[bitmap_obj_p->ioexp_voffset] = modify_byte;
return 0;
}
/* ========== Object public functions ==========
*/
int
common_get_present(struct ioexp_obj_s *self,
int virt_offset){
int UNPLUG = 1;
int retval = ERR_IOEXP_UNEXCPT;
retval = _common_get_bit(self,
&(self->ioexp_map_p->map_present[virt_offset]),
"common_get_present");
if (retval < 0) {
/* [Note]
* => Transceiver object does not need to handle IOEXP layer issues.
*/
return UNPLUG;
}
return retval;
}
int
common_get_tx_fault(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_tx_fault[virt_offset]),
"common_get_tx_fault");
}
int
common_get_rxlos(struct ioexp_obj_s *self,
int virt_offset){
/* [Receiver Loss of Signal (Rx_LOS)]
* The post-amplification IC also includes transition detection circuitry
* which monitors the ac level of incoming optical signals and provides a
* TTL/CMOS compatible status signal to the host (pin 8). An adequate optical
* input results in a low Rx_LOS output while a high Rx_LOS output indicates
* an unusable optical input. The Rx_LOS thresholds are factory set so that
* a high output indicates a definite optical fault has occurred. Rx_LOS can
* also be monitored via the two-wire serial interface
* (address A2h, byte 110, bit 1).
*
* 0: Normal
* 1: Abnormal
*/
return _common_get_bit(self,
&(self->ioexp_map_p->map_rxlos[virt_offset]),
"common_get_rxlos");
}
int
common_get_tx_disable(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_tx_disable[virt_offset]),
"common_get_tx_disable");
}
int
common_get_reset(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_reset[virt_offset]),
"common_get_reset");
}
int
common_get_lpmod(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_lpmod[virt_offset]),
"common_get_lpmod");
}
int
common_get_modsel(struct ioexp_obj_s *self,
int virt_offset){
return _common_get_bit(self,
&(self->ioexp_map_p->map_modsel[virt_offset]),
"common_get_modsel");
}
int
common_set_tx_disable(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_tx_disable[virt_offset]),
input_val,
"common_set_tx_disable");
}
int
common_set_reset(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_reset[virt_offset]),
input_val,
"common_set_reset");
}
int
common_set_lpmod(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_lpmod[virt_offset]),
input_val,
"common_set_lpmod");
}
int
common_set_modsel(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return _common_set_bit(self,
&(self->ioexp_map_p->map_modsel[virt_offset]),
input_val,
"common_set_modsel");
}
int
ioexp_get_not_support(struct ioexp_obj_s *self,
int virt_offset){
return ERR_IOEXP_NOTSUPPORT;
}
int
ioexp_set_not_support(struct ioexp_obj_s *self,
int virt_offset,
int input_val){
return ERR_IOEXP_NOTSUPPORT;
}
/* ========== Initial functions for IO Expander ==========
*/
int
common_ioexp_init(struct ioexp_obj_s *self) {
int chip_id, offset, err_code;
struct ioexp_addr_s *addr_p;
if (self->mode == IOEXP_MODE_DIRECT) { ///important
goto update_common_ioexp_init;
}
/* Setup default value to each physical IO Expander */
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
/* Get address mapping */
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
if (!addr_p){
SWPS_ERR("%s: IOEXP config incorrect! <chip_id>:%d \n",
__func__, chip_id);
return -1;
}
/* Setup default value */
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
addr_p->write_offset[offset],
addr_p->data_default[offset]);
if (err_code < 0){
SWPS_ERR("%s: set default fail! <error>:%d \n",
__func__, err_code);
return ERR_IOEXP_UNEXCPT;
}
}
}
update_common_ioexp_init:
/* Check and update info to object */
err_code = self->update_all(self, 1, "common_ioexp_init");
if (err_code < 0) {
SWPS_ERR("%s: update_all() fail! <error>:%d \n",
__func__, err_code);
return ERR_IOEXP_UNEXCPT;
}
return 0;
}
/* ========== Object functions for Final State Machine ==========
*/
int
_is_channel_ready(struct ioexp_obj_s *self){
int buf = 0;
int chip_id = 0; /* Use first chip which be registered */
int data_id = 0; /* Use first byte which be registered */
struct ioexp_addr_s *ioexp_addr = NULL;
ioexp_addr = &(self->ioexp_map_p->map_addr[chip_id]);
if (!ioexp_addr){
SWPS_ERR("%s: config incorrect!\n", __func__);
return ERR_IOEXP_UNEXCPT;
}
buf = i2c_smbus_read_byte_data(_get_i2c_client(self, chip_id),
ioexp_addr->read_offset[data_id]);
if (buf >= 0){
return 1;
}
return 0;
}
int
_ioexp_init_handler(struct ioexp_obj_s *self){
int return_val;
switch (self->mode) {
case IOEXP_MODE_DIRECT:
return_val = self->init(self);
if (return_val < 0){
self->state = STATE_IOEXP_ABNORMAL;
} else {
self->state = STATE_IOEXP_NORMAL;
}
return return_val;
default:
break;
}
SWPS_ERR("%s: exception occur <mode>:%d\n", __func__, self->mode);
return ERR_IOEXP_UNEXCPT;
}
int
common_ioexp_fsm_4_direct(struct ioexp_obj_s *self){
int result_val;
int show_err = 1;
char *func_mane = "common_ioexp_fsm_4_direct";
switch (self->state){
case STATE_IOEXP_INIT:
result_val = _ioexp_init_handler(self);
/* Exception case: terminate initial procedure */
if(result_val < 0){
/* Initial fail */
return result_val;
}
if(self->state == STATE_IOEXP_INIT){
/* Keep in INIT state, and return error */
return ERR_IOEXP_UNINIT;
}
/* Case: Initial done */
return 0;
case STATE_IOEXP_NORMAL:
result_val = self->update_all(self, show_err, func_mane);
if (result_val < 0){
SWPS_INFO("%s: NORMAL -> ABNORMAL <err>:%d\n",
__func__, result_val);
self->state = STATE_IOEXP_ABNORMAL;
return result_val;
}
self->state = STATE_IOEXP_NORMAL;
return 0;
case STATE_IOEXP_ABNORMAL:
result_val = self->update_all(self, show_err, func_mane);
if (result_val < 0){
self->state = STATE_IOEXP_ABNORMAL;
return result_val;
}
SWPS_DEBUG("%s: ABNORMAL -> NORMAL <err>:%d\n",
__func__, result_val);
self->state = STATE_IOEXP_NORMAL;
return 0;
default:
break;
}
SWPS_ERR("%s: Exception occurs <state>:%d\n",
__func__, self->state);
return ERR_IOEXP_UNEXCPT;
}
/* ========== Functions for Factory pattern ==========
*/
static struct ioexp_map_s *
get_ioexp_map(int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_CYPRESS_NABC:
return &ioexp_map_cypress_nabc;
case IOEXP_TYPE_CYPRESS_7ABC:
return &ioexp_map_cypress_7abc;
default:
return NULL;
}
}
int
setup_ioexp_ssize_attr(struct ioexp_obj_s *self,
struct ioexp_map_s *ioexp_map_p,
int ioexp_id,
int ioexp_type,
int run_mode){
switch (run_mode){
case IOEXP_MODE_DIRECT: /* Direct access device mode */
self->mode = run_mode;
break;
default:
SWPS_ERR("%s: non-defined run_mode:%d\n",
__func__, run_mode);
self->mode = ERR_IOEXP_UNEXCPT;
return ERR_IOEXP_UNEXCPT;
}
self->ioexp_id = ioexp_id;
self->ioexp_type = ioexp_type;
self->ioexp_map_p = ioexp_map_p;
self->state = STATE_IOEXP_INIT;
mutex_init(&self->lock);
return 0;
}
static int
setup_addr_mapping(struct ioexp_obj_s *self,
struct ioexp_addr_s *addr_map_p){
if (!addr_map_p){
SWPS_ERR("%s: map is null\n", __func__);
return -1;
}
self->ioexp_map_p->map_addr = addr_map_p;
return 0;
}
static int
setup_ioexp_public_cb(struct ioexp_obj_s *self,
int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_CYPRESS_NABC:
self->get_present = common_get_present;
self->get_tx_fault = common_get_tx_fault;
self->get_rxlos = common_get_rxlos;
self->get_tx_disable = common_get_tx_disable;
self->get_reset = ioexp_get_not_support;
self->get_lpmod = ioexp_get_not_support;
self->get_modsel = ioexp_get_not_support;
self->set_tx_disable = common_set_tx_disable;
self->set_reset = ioexp_set_not_support;
self->set_lpmod = ioexp_set_not_support;
self->set_modsel = ioexp_set_not_support;
return 0;
case IOEXP_TYPE_CYPRESS_7ABC:
self->get_present = common_get_present;
self->get_tx_fault = ioexp_get_not_support;
self->get_rxlos = ioexp_get_not_support;
self->get_tx_disable = ioexp_get_not_support;
self->get_reset = common_get_reset;
self->get_lpmod = common_get_lpmod;
self->get_modsel = common_get_modsel;
self->set_tx_disable = ioexp_set_not_support;
self->set_reset = common_set_reset;
self->set_lpmod = common_set_lpmod;
self->set_modsel = common_set_modsel;
return 0;
default:
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
break;
}
return ERR_IOEXP_UNEXCPT;
}
static int
setup_ioexp_private_cb(struct ioexp_obj_s *self,
int ioexp_type){
switch (ioexp_type){
case IOEXP_TYPE_CYPRESS_NABC:
case IOEXP_TYPE_CYPRESS_7ABC:
self->init = common_ioexp_init;
self->update_all = common_ioexp_update_all;
self->fsm_4_direct = common_ioexp_fsm_4_direct;
return 0;
default:
SWPS_ERR("%s: type:%d incorrect!\n", __func__, ioexp_type);
break;
}
return ERR_IOEXP_UNEXCPT;
}
static int
setup_i2c_client_one(struct ioexp_obj_s *self,
int chip_id){
char *err_msg = "ERROR";
struct i2c_adapter *adap = NULL;
struct i2c_client *client = NULL;
struct ioexp_i2c_s *i2c_obj_p = NULL;
struct ioexp_i2c_s *i2c_curr_p = NULL;
int chan_id = self->ioexp_map_p->map_addr[chip_id].chan_id;
adap = i2c_get_adapter(chan_id);
if(!adap){
err_msg = "Can not get adap!";
goto err_ioexp_setup_i2c_1;
}
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client){
err_msg = "Can not kzalloc client!";
goto err_ioexp_setup_i2c_1;
}
i2c_obj_p = kzalloc(sizeof(*i2c_obj_p), GFP_KERNEL);
if (!i2c_obj_p){
err_msg = "Can not kzalloc i2c_obj_p!";
goto err_ioexp_setup_i2c_2;
}
client->adapter = adap;
client->addr = self->ioexp_map_p->map_addr[chip_id].chip_addr;
i2c_obj_p->i2c_client_p = client;
i2c_obj_p->chip_id = chip_id;
i2c_obj_p->next = NULL;
if (!self->i2c_head_p){
self->i2c_head_p = i2c_obj_p;
} else {
i2c_curr_p = self->i2c_head_p;
while (i2c_curr_p->next){
i2c_curr_p = i2c_curr_p->next;
}
i2c_curr_p->next = i2c_obj_p;
}
return 0;
err_ioexp_setup_i2c_2:
kfree(client);
err_ioexp_setup_i2c_1:
SWPS_ERR("%s: %s <chanID>:%d\n", __func__, err_msg, chan_id);
return -1;
}
static int
setup_i2c_client(struct ioexp_obj_s* self){
int result;
int chip_id = 0;
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
result = setup_i2c_client_one(self, chip_id);
if (result < 0){
SWPS_ERR("%s fail! <chip_id>:%d\n", __func__, chip_id);
return -1;
}
}
return 0;
}
static int
setup_ioexp_config(struct ioexp_obj_s *self) {
int chip_id, offset, err_code;
struct ioexp_addr_s *addr_p;
for (chip_id=0; chip_id<(self->ioexp_map_p->chip_amount); chip_id++){
addr_p = &(self->ioexp_map_p->map_addr[chip_id]);
if (!addr_p){
SWPS_ERR("IOEXP config incorrect! <chip_id>:%d \n",chip_id);
return -1;
}
for (offset=0; offset<(self->ioexp_map_p->data_width); offset++){
err_code = i2c_smbus_write_byte_data(_get_i2c_client(self, chip_id),
addr_p->conf_offset[offset],
addr_p->conf_default[offset]);
if (err_code < 0){
SWPS_INFO("%s: set conf fail! <err>:%d \n", __func__, err_code);
return -2;
}
}
}
return 0;
}
struct ioexp_obj_s *
_create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode){
struct ioexp_map_s* ioexp_map_p;
struct ioexp_obj_s* result_p;
struct ioexp_i2c_s *i2c_curr_p;
struct ioexp_i2c_s *i2c_next_p;
/* Get layout */
ioexp_map_p = get_ioexp_map(ioexp_type);
if (!ioexp_map_p){
SWPS_ERR("%s: Invalid ioexp_type\n", __func__);
goto err_create_ioexp_fail;
}
/* Prepare IOEXP object */
result_p = kzalloc(sizeof(*result_p), GFP_KERNEL);
if (!result_p){
SWPS_ERR("%s: kzalloc failure!\n", __func__);
goto err_create_ioexp_fail;
}
/* Prepare static size attributes */
if (setup_ioexp_ssize_attr(result_p,
ioexp_map_p,
ioexp_id,
ioexp_type,
run_mode) < 0){
goto err_create_ioexp_setup_attr_fail;
}
/* Prepare address mapping */
if (setup_addr_mapping(result_p, addr_map_p) < 0){
goto err_create_ioexp_setup_attr_fail;
}
if (setup_i2c_client(result_p) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
/* Prepare call back functions of object */
if (setup_ioexp_public_cb(result_p, ioexp_type) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
if (setup_ioexp_private_cb(result_p, ioexp_type) < 0){
goto err_create_ioexp_setup_i2c_fail;
}
return result_p;
err_create_ioexp_setup_i2c_fail:
i2c_curr_p = result_p->i2c_head_p;
i2c_next_p = result_p->i2c_head_p;
while (i2c_curr_p){
i2c_next_p = i2c_curr_p->next;
kfree(i2c_curr_p->i2c_client_p);
kfree(i2c_curr_p);
i2c_curr_p = i2c_next_p;
}
err_create_ioexp_setup_attr_fail:
kfree(result_p);
err_create_ioexp_fail:
SWPS_ERR("%s: fail! <id>:%d <type>:%d \n",
__func__, ioexp_id, ioexp_type);
return NULL;
}
int
create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode){
struct ioexp_obj_s *ioexp_p = NULL;
ioexp_p = _create_ioexp_obj(ioexp_id, ioexp_type,
addr_map_p, run_mode);
if (!ioexp_p){
return -1;
}
if (ioexp_head_p == NULL){
ioexp_head_p = ioexp_p;
ioexp_tail_p = ioexp_p;
return 0;
}
ioexp_tail_p->next = ioexp_p;
ioexp_tail_p = ioexp_p;
return 0;
}
static int
_init_ioexp_obj(struct ioexp_obj_s* self) {
char *err_msg = "ERR";
char *func_name = "_init_ioexp_obj";
/* Setup IOEXP configure byte */
if (setup_ioexp_config(self) < 0){
err_msg = "setup_ioexp_config fail";
goto err_init_ioexp_obj;
}
/* Setup default data */
if (_ioexp_init_handler(self) < 0){
err_msg = "_ioexp_init_handler fail";
goto err_init_ioexp_obj;
}
/* Update all */
if (self->state == STATE_IOEXP_NORMAL){
if (self->update_all(self, 1, func_name) < 0){
err_msg = "update_all() fail";
goto err_init_ioexp_obj;
}
}
return 0;
err_init_ioexp_obj:
SWPS_DEBUG("%s: %s\n", __func__, err_msg);
return -1;
}
int
init_ioexp_objs(void){
/* Return value:
* 0: Success
* -1: Detect topology error
* -2: SWPS internal error
*/
struct ioexp_obj_s *curr_p = ioexp_head_p;
if (!curr_p) {
SWPS_ERR("%s: ioexp_head_p is NULL\n", __func__);
return -2;
}
while (curr_p) {
if (_init_ioexp_obj(curr_p) < 0) {
SWPS_DEBUG("%s: _init_ioexp_obj() fail\n", __func__);
return -1;
}
curr_p = curr_p->next;
}
SWPS_DEBUG("%s: done.\n", __func__);
return 0;
}
void
clean_ioexp_objs(void){
struct ioexp_i2c_s *i2c_curr_p = NULL;
struct ioexp_i2c_s *i2c_next_p = NULL;
struct ioexp_obj_s *ioexp_next_p = NULL;
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
if (ioexp_head_p == NULL){
ioexp_tail_p = NULL;
return;
}
while(ioexp_curr_p){
ioexp_next_p = ioexp_curr_p->next;
i2c_curr_p = ioexp_curr_p->i2c_head_p;
while (i2c_curr_p) {
i2c_next_p = i2c_curr_p->next;
kfree(i2c_curr_p->i2c_client_p);
kfree(i2c_curr_p);
i2c_curr_p = i2c_next_p;
}
kfree(ioexp_curr_p);
ioexp_curr_p = ioexp_next_p;
}
ioexp_tail_p = NULL;
SWPS_DEBUG("%s: done.\n", __func__);
}
struct ioexp_obj_s *
get_ioexp_obj(int ioexp_id){
struct ioexp_obj_s *result_p = NULL;
struct ioexp_obj_s *ioexp_curr_p = ioexp_head_p;
while(ioexp_curr_p){
if (ioexp_curr_p->ioexp_id == ioexp_id){
result_p = ioexp_curr_p;
break;
}
ioexp_curr_p = ioexp_curr_p->next;
}
return result_p;
}
int
check_channel_tier_1(void) {
if ( (!_is_channel_ready(ioexp_head_p)) &&
(!_is_channel_ready(ioexp_tail_p)) ){
return -1;
}
return 0;
}

View File

@ -0,0 +1,143 @@
#ifndef IO_EXPANDER_H
#define IO_EXPANDER_H
#include <linux/types.h>
/* IOEXP type define (QSFP series) */
#define IOEXP_TYPE_CYPRESS_NABC (10102)
#define IOEXP_TYPE_CYPRESS_7ABC (10207)
/* IOEXP mode define */
#define IOEXP_MODE_DIRECT (19001)
/* IOEXP state define */
#define STATE_IOEXP_NORMAL (0)
#define STATE_IOEXP_INIT (-1)
#define STATE_IOEXP_ABNORMAL (-2)
/* IOEXP error code define */
#define ERR_IOEXP_NOTSUPPORT (-100)
#define ERR_IOEXP_UNINIT (-101)
#define ERR_IOEXP_BADCONF (-102)
#define ERR_IOEXP_BADINPUT (-105)
#define ERR_IOEXP_UNEXCPT (-199)
#define SWPS_INFO(fmt, args...) printk( KERN_INFO "[SWPS] " fmt, ##args)
#define SWPS_WARN(fmt, args...) printk( KERN_WARNING "[SWPS] " fmt, ##args)
#define SWPS_ERR(fmt, args...) printk( KERN_ERR "[SWPS] " fmt, ##args)
#ifdef DEBUG_SWPS
# define SWPS_DEBUG(fmt, args...) printk( KERN_DEBUG "[SWPS] " fmt, ##args)
#else
# define SWPS_DEBUG(fmt, args...)
#endif
struct ioexp_addr_s {
int chan_id;
int chip_addr;
int read_offset[8];
int write_offset[8];
int conf_offset[8];
uint8_t data_default[8];
uint8_t conf_default[8];
};
struct ioexp_i2c_s {
int chip_id;
struct i2c_client *i2c_client_p;
struct ioexp_i2c_s *next;
};
struct ioexp_bitmap_s {
int chip_id; /* IOEXP chip id */
int ioexp_voffset; /* IOEXP virtual offset */
int bit_shift;
};
struct ioexp_map_s {
int chip_amount; /* Number of chips that IOEXP object content */
int data_width; /* Number of (Read/Write/Config) bytes */
struct ioexp_addr_s *map_addr; /* Chip address info */
struct ioexp_bitmap_s map_present[8]; /* IOEXP for SFP / QSFP */
struct ioexp_bitmap_s map_tx_disable[8]; /* IOEXP for SFP */
struct ioexp_bitmap_s map_tx_fault[8]; /* IOEXP for SFP */
struct ioexp_bitmap_s map_rxlos[8]; /* IOEXP for SFP */
struct ioexp_bitmap_s map_reset[8]; /* IOEXP for QSFP */
struct ioexp_bitmap_s map_lpmod[8]; /* IOEXP for QSFP */
struct ioexp_bitmap_s map_modsel[8]; /* IOEXP for QSFP */
};
struct ioexp_data_s {
uint8_t data[8];
};
struct ioexp_obj_s {
/* ============================
* Object public property
* ============================
*/
int ioexp_id;
int ioexp_type;
/* ============================
* Object private property
* ============================
*/
struct ioexp_data_s chip_data[16]; /* Max: 8-ioexp in one virt-ioexp(ioexp_obj) */
struct ioexp_map_s *ioexp_map_p;
struct ioexp_obj_s *next;
struct ioexp_i2c_s *i2c_head_p;
struct mutex lock;
int mode;
int state;
/* ===========================================
* Object public functions
* ===========================================
*/
int (*get_present)(struct ioexp_obj_s *self, int virt_offset);
int (*get_tx_fault)(struct ioexp_obj_s *self, int virt_offset);
int (*get_rxlos)(struct ioexp_obj_s *self, int virt_offset);
int (*get_tx_disable)(struct ioexp_obj_s *self, int virt_offset);
int (*get_reset)(struct ioexp_obj_s *self, int virt_offset);
int (*get_lpmod)(struct ioexp_obj_s *self, int virt_offset);
int (*get_modsel)(struct ioexp_obj_s *self, int virt_offset);
int (*set_tx_disable)(struct ioexp_obj_s *self, int virt_offset, int input_val);
int (*set_reset)(struct ioexp_obj_s *self, int virt_offset, int input_val);
int (*set_lpmod)(struct ioexp_obj_s *self, int virt_offset, int input_val);
int (*set_modsel)(struct ioexp_obj_s *self, int virt_offset, int input_val);
/* ===========================================
* Object private functions
* ===========================================
*/
int (*init)(struct ioexp_obj_s *self);
int (*update_all)(struct ioexp_obj_s *self, int show_err, char *caller_name);
int (*fsm_4_direct)(struct ioexp_obj_s* self);
};
struct ioexp_obj_s* get_ioexp_obj(int ioexp_id);
int create_ioexp_obj(int ioexp_id,
int ioexp_type,
struct ioexp_addr_s *addr_map_p,
int run_mode);
int init_ioexp_objs(void);
void clean_ioexp_objs(void);
int check_channel_tier_1(void);
/* Macro for bit control */
#define SWP_BIT_SET(byte_val,bit_shift) ((byte_val) |= (1<<(bit_shift)))
#define SWP_BIT_CLEAR(byte_val,bit_shift) ((byte_val) &= ~(1<<(bit_shift)))
#endif /* IO_EXPANDER_H */

View File

@ -0,0 +1,906 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/kobject.h>
#include <linux/delay.h>
#include "io_expander.h"
#include "transceiver.h"
/* ========== Register EEPROM address mapping ==========
*/
struct eeprom_map_s eeprom_map_sfp = {
.addr_rx_los =-1, .page_rx_los =-1, .offset_rx_los =-1, .length_rx_los =-1,
.addr_tx_disable =-1, .page_tx_disable =-1, .offset_tx_disable =-1, .length_tx_disable =-1,
.addr_tx_fault =-1, .page_tx_fault =-1, .offset_tx_fault =-1, .length_tx_fault =-1,
};
struct eeprom_map_s eeprom_map_qsfp = {
.addr_rx_los =0x50, .page_rx_los =-1, .offset_rx_los =3, .length_rx_los =1,
.addr_tx_disable =0x50, .page_tx_disable =-1, .offset_tx_disable =86, .length_tx_disable =1,
.addr_tx_fault =0x50, .page_tx_fault =-1, .offset_tx_fault =4, .length_tx_fault =1,
};
struct eeprom_map_s eeprom_map_qsfp28 = {
.addr_rx_los =0x50, .page_rx_los =-1, .offset_rx_los =3, .length_rx_los =1,
.addr_tx_disable =0x50, .page_tx_disable =-1, .offset_tx_disable =86, .length_tx_disable =1,
.addr_tx_fault =0x50, .page_tx_fault =-1, .offset_tx_fault =4, .length_tx_fault =1,
};
/* ========== Utility Functions ==========
*/
void
alarm_msg_2_user(struct transvr_obj_s *self,
char *emsg) {
SWPS_ERR("%s on %s.\n", emsg, self->swp_name);
}
/* ========== Private functions ==========
*/
static int
_reload_transvr_obj(struct transvr_obj_s *self,int new_type);
static int
reload_transvr_obj(struct transvr_obj_s *self,int new_type);
static int
_transvr_init_handler(struct transvr_obj_s *self);
static void
_transvr_clean_retry(struct transvr_obj_s *self) {
self->retry = 0;
}
static int
_transvr_handle_retry(struct transvr_obj_s *self, int retry) {
/* Return: 0: keep retry
* -1: stop retry
*/
if (self->retry == 0) {
self->retry = retry;
}
self->retry -= 1;
if (self->retry <= 0) {
_transvr_clean_retry(self);
return -1;
}
return 0;
}
static int
_common_setup_page(struct transvr_obj_s *self,
int addr,
int page,
int offset,
int len,
int show_e) {
/* return:
* 0 : OK
* -1 : EEPROM settings incorrect
* -2 : I2C R/W failure
* -3 : Undefined case
*/
int retval = DEBUG_TRANSVR_INT_VAL;
char *emsg = DEBUG_TRANSVR_STR_VAL;
/* Check */
if ((addr < 0) || (offset < 0) || (len < 0)) {
emsg = "EEPROM settings incorrect";
retval = -1;
goto err_common_setup_page;
}
/* Case1: continue access */
if ((self->i2c_client_p->addr == addr) &&
(self->curr_page == page)) {
return 0;
}
self->i2c_client_p->addr = addr;
/* Case2: select lower page */
if (page == -1) {
self->curr_page = page;
return 0;
}
/* Case3: select upper page */
if (page >= 0) {
goto upper_common_setup_page;
}
/* Unexpected case */
show_e = 1;
emsg = "Unexpected case";
retval = -3;
goto err_common_setup_page;
upper_common_setup_page:
if (i2c_smbus_write_byte_data(self->i2c_client_p,
VAL_TRANSVR_PAGE_SELECT_OFFSET,
page) < 0) {
emsg = "I2C R/W failure";
retval = -2;
goto err_common_setup_page;
}
self->curr_page = page;
mdelay(VAL_TRANSVR_PAGE_SELECT_DELAY);
return 0;
err_common_setup_page:
if (show_e) {
SWPS_INFO("%s: %s", __func__, emsg);
SWPS_INFO("%s: <addr>:0x%02x <page>:%d <offs>:%d <len>:%d\n",
__func__, addr, page, offset, len);
}
return retval;
}
/* ========== Object functions for Final State Machine ==========
*/
int
is_plugged(struct transvr_obj_s *self){
int limit = 63;
int present = DEBUG_TRANSVR_INT_VAL;
char emsg[64] = DEBUG_TRANSVR_STR_VAL;
struct ioexp_obj_s *ioexp_p = self->ioexp_obj_p;
if (!ioexp_p) {
snprintf(emsg, limit, "ioexp_p is null!");
goto err_is_plugged_1;
}
present = ioexp_p->get_present(ioexp_p, self->ioexp_virt_offset);
switch (present){
case 0:
return 1;
case 1:
return 0;
case ERR_IOEXP_UNINIT:
snprintf(emsg, limit, "ioexp_p not ready!");
goto err_is_plugged_1;
default:
if (ioexp_p->state == STATE_IOEXP_INIT){
snprintf(emsg, limit, "ioexp_p not ready!");
goto err_is_plugged_1;
}
break;
}
SWPS_INFO("%s: Exception case! <pres>:%d <istate>:%d\n",
__func__, present, ioexp_p->state);
return 0;
err_is_plugged_1:
SWPS_DEBUG("%s: %s\n", __func__, emsg);
return 0;
}
static int
detect_transvr_type(struct transvr_obj_s* self){
int type = TRANSVR_TYPE_ERROR;
self->i2c_client_p->addr = VAL_TRANSVR_COMID_ARREESS;
type = i2c_smbus_read_byte_data(self->i2c_client_p,
VAL_TRANSVR_COMID_OFFSET);
/* Case: 1. Wait transceiver I2C module.
* 2. Transceiver I2C module failure.
* Note: 1. SFF allow maximum transceiver initial time is 2 second. So, there
* are exist some case that we need to wait transceiver.
* For these case, we keeps status on "TRANSVR_TYPE_UNPLUGGED", than
* state machine will keep trace with it.
* 2. There exist some I2C failure case we need to handle. Such as user
* insert the failure transceiver, or any reason cause it abnormal.
*/
if (type < 0){
switch (type) {
case -EIO:
SWPS_DEBUG("%s: %s smbus return:-5 (I/O error)\n",
__func__, self->swp_name);
return TRANSVR_TYPE_UNPLUGGED;
case -ENXIO:
SWPS_DEBUG("%s: %s smbus return:-6 (No such device or address)\n",
__func__, self->swp_name);
return TRANSVR_TYPE_UNPLUGGED;
default:
break;
}
SWPS_INFO("%s: %s unexpected smbus return:%d \n",
__func__, self->swp_name, type);
return TRANSVR_TYPE_ERROR;
}
/* Identify valid transceiver type */
switch (type){
case TRANSVR_TYPE_SFP:
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
break;
case TRANSVR_TYPE_UNKNOW_1:
case TRANSVR_TYPE_UNKNOW_2:
type = TRANSVR_TYPE_UNKNOW_2;
break;
default:
SWPS_DEBUG("%s: unknow type:0x%02x \n", __func__, type);
type = TRANSVR_TYPE_ERROR;
break;
}
return type;
}
static int
detect_transvr_state(struct transvr_obj_s *self,
int result[2]){
/* [return] [result-0] [result-1]
* 0 STATE_TRANSVR_CONNECTED TRANSVR_TYPE_FAKE
* 0 STATE_TRANSVR_DISCONNECTED TRANSVR_TYPE_UNPLUGGED
* 0 STATE_TRANSVR_ISOLATED TRANSVR_TYPE_ERROR
* 0 STATE_TRANSVR_INIT <NEW_TYPE>/<OLD_TYPE>
* 0 STATE_TRANSVR_SWAPPED <NEW_TYPE>
* 0 STATE_TRANSVR_CONNECTED <OLD_TYPE>
* ERR_TRNASVR_BE_ISOLATED STATE_TRANSVR_ISOLATED TRANSVR_TYPE_ERROR <Isolated>
* ERR_TRANSVR_I2C_CRASH STATE_TRANSVR_UNEXCEPTED TRANSVR_TYPE_ERROR <New event>
* ERR_TRANSVR_UNEXCPT STATE_TRANSVR_UNEXCEPTED TRANSVR_TYPE_UNKNOW_1/2
*/
result[0] = STATE_TRANSVR_UNEXCEPTED; /* For return state */
result[1] = TRANSVR_TYPE_ERROR; /* For return type */
/* Case1: Fake type */
if (self->type == TRANSVR_TYPE_FAKE){
result[0] = STATE_TRANSVR_CONNECTED;
result[1] = TRANSVR_TYPE_FAKE;
return 0;
}
/* Case2: Transceiver unplugged */
if (!is_plugged(self)){
result[0] = STATE_TRANSVR_DISCONNECTED;
result[1] = TRANSVR_TYPE_UNPLUGGED;
return 0;
}
/* Case3: Transceiver be isolated */
if (self->state == STATE_TRANSVR_ISOLATED){
result[0] = STATE_TRANSVR_ISOLATED;
result[1] = TRANSVR_TYPE_ERROR;
return ERR_TRNASVR_BE_ISOLATED;
}
/* Case4: Transceiver plugged */
result[1] = detect_transvr_type(self);
/* Case4.1: I2C topology crash
* Note : There are some I2C issues cause by transceiver/cables.
* We need to check topology status when user insert it.
* But in this step, we can't not ensure this is the issues
* port. So, it return the ERR_TRANSVR_I2C_CRASH, then upper
* layer will diagnostic I2C topology.
*/
if (check_channel_tier_1() < 0) {
SWPS_INFO("%s: %s detect I2C crash <obj-state>:%d\n",
__func__, self->swp_name, self->state);
result[0] = STATE_TRANSVR_UNEXCEPTED;
result[1] = TRANSVR_TYPE_ERROR;
return ERR_TRANSVR_I2C_CRASH;
}
/* Case4.2: System initial not ready,
* Note : Sometime i2c channel or transceiver EEPROM will delay that will
* cause system in inconsistent state between EEPROM and IOEXP.
* In this case, SWP transceiver object keep state at LINK_DOWN
* to wait system ready.
* By the way, State Machine will handle these case.
*/
if (result[1] == TRANSVR_TYPE_UNPLUGGED){
result[0] = STATE_TRANSVR_DISCONNECTED;
return 0;
}
/* Case4.3: Error transceiver type */
if (result[1] == TRANSVR_TYPE_ERROR){
result[0] = STATE_TRANSVR_ISOLATED;
SWPS_INFO("%s: %s detect error type\n", __func__, self->swp_name);
alarm_msg_2_user(self, "detected transceiver/cables not meet SFF standard!");
return ERR_TRNASVR_BE_ISOLATED;
}
/* Case3.3: Unknow transceiver type */
if ((result[1] == TRANSVR_TYPE_UNKNOW_1) ||
(result[1] == TRANSVR_TYPE_UNKNOW_2) ){
result[0] = STATE_TRANSVR_UNEXCEPTED;
return ERR_TRANSVR_UNEXCPT;
}
/* Case3.4: During initial process */
if (self->state == STATE_TRANSVR_INIT){
result[0] = STATE_TRANSVR_INIT;
return 0;
}
/* Case3.5: Transceiver be swapped */
if (self->type != result[1]){
result[0] = STATE_TRANSVR_SWAPPED;
return 0;
}
/* Case3.6: Link up state */
result[0] = STATE_TRANSVR_CONNECTED;
return 0;
}
int
common_fsm_4_direct_mode(struct transvr_obj_s* self,
char *caller_name){
int err;
int detect_result[2];
int current_state = STATE_TRANSVR_UNEXCEPTED;
int current_type = TRANSVR_TYPE_ERROR;
if (self->state == STATE_TRANSVR_NEW) {
if (_transvr_init_handler(self) < 0){
return ERR_TRANSVR_INIT_FAIL;
}
}
err = detect_transvr_state(self, detect_result);
if (err < 0) {
return err;
}
/* In Direct mode, driver only detect transceiver when user call driver interface
* which on sysfs. So it only need consider the state of Transceiver.
*/
current_state = detect_result[0];
current_type = detect_result[1];
switch (current_state){
case STATE_TRANSVR_DISCONNECTED: /* Transceiver is not plugged */
self->state = current_state;
self->type = current_type;
return ERR_TRANSVR_UNPLUGGED;
case STATE_TRANSVR_INIT: /* Transceiver is plugged, system not ready */
return ERR_TRANSVR_UNINIT;
case STATE_TRANSVR_ISOLATED: /* Transceiver is plugged, but has some issues */
return ERR_TRNASVR_BE_ISOLATED;
case STATE_TRANSVR_CONNECTED: /* Transceiver is plugged, system is ready */
self->state = current_state;
self->type = current_type;
return 0;
case STATE_TRANSVR_SWAPPED: /* Transceiver is plugged, system detect user changed */
self->type = current_type;
if (reload_transvr_obj(self, current_type) < 0){
self->state = STATE_TRANSVR_UNEXCEPTED;
return ERR_TRANSVR_UNEXCPT;
}
self->state = current_state;
return 0;
case STATE_TRANSVR_UNEXCEPTED: /* Transceiver type or state is unexpected case */
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = TRANSVR_TYPE_ERROR;
return ERR_TRANSVR_UNEXCPT;
default:
SWPS_INFO("%s: state:%d not in define.\n", __func__, current_state);
break;
}
return ERR_TRANSVR_UNEXCPT;
}
int
fake_fsm_4_direct_mode(struct transvr_obj_s* self,
char *caller_name){
self->state = STATE_TRANSVR_CONNECTED;
self->type = TRANSVR_TYPE_FAKE;
return 0;
}
/* ========== Object Initial handler ==========
*/
static int
_is_transvr_valid(struct transvr_obj_s *self,
int type,
int state) {
/* [Return]
* 0 : OK, inserted
* EVENT_TRANSVR_INIT_DOWN : OK, removed
* EVENT_TRANSVR_INIT_FAIL : Outside error, type doesn't supported
* EVENT_TRANSVR_EXCEP_INIT : Internal error, state undefined
*/
switch (type) {
case TRANSVR_TYPE_SFP:
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
case TRANSVR_TYPE_UNPLUGGED:
case TRANSVR_TYPE_FAKE:
break;
default:
SWPS_INFO("detect undefined type:0x%02x on %s\n",
type, self->swp_name);
return EVENT_TRANSVR_INIT_FAIL;
}
switch (state) {
case STATE_TRANSVR_DISCONNECTED:
return EVENT_TRANSVR_INIT_DOWN;
case STATE_TRANSVR_INIT:
case STATE_TRANSVR_CONNECTED:
case STATE_TRANSVR_SWAPPED:
break;
default:
SWPS_INFO("detect undefined state:%d on %s\n",
state, self->swp_name);
return EVENT_TRANSVR_EXCEP_INIT;
}
return 0;
}
static int
_is_transvr_hw_ready(struct transvr_obj_s *self,
int type){
/* [Return]
* EVENT_TRANSVR_TASK_DONE : Ready
* EVENT_TRANSVR_TASK_WAIT : Not ready
* EVENT_TRANSVR_INIT_FAIL : Error
*/
int addr = DEBUG_TRANSVR_INT_VAL;
int page = DEBUG_TRANSVR_INT_VAL;
int offs = DEBUG_TRANSVR_INT_VAL;
int bit = DEBUG_TRANSVR_INT_VAL;
int ready = DEBUG_TRANSVR_INT_VAL;
int err = DEBUG_TRANSVR_INT_VAL;
char *emsg = DEBUG_TRANSVR_STR_VAL;
uint8_t ab_val = DEBUG_TRANSVR_HEX_VAL;
switch (type) {
case TRANSVR_TYPE_SFP:
addr = VAL_TRANSVR_8472_READY_ADDR;
page = VAL_TRANSVR_8472_READY_PAGE;
offs = VAL_TRANSVR_8472_READY_OFFSET;
bit = VAL_TRANSVR_8472_READY_BIT;
ready = VAL_TRANSVR_8472_READY_VALUE;
ab_val = VAL_TRANSVR_8472_READY_ABNORMAL;
break;
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
case TRANSVR_TYPE_QSFP_28:
addr = VAL_TRANSVR_8436_READY_ADDR;
page = VAL_TRANSVR_8436_READY_PAGE;
offs = VAL_TRANSVR_8436_READY_OFFSET;
bit = VAL_TRANSVR_8436_READY_BIT;
ready = VAL_TRANSVR_8436_READY_VALUE;
ab_val = VAL_TRANSVR_8436_READY_ABNORMAL;
break;
case TRANSVR_TYPE_UNPLUGGED:
case TRANSVR_TYPE_FAKE:
return EVENT_TRANSVR_TASK_DONE;
default:
emsg = "unexpected case";
goto err_is_transvr_hw_ready;
}
/* Select target page */
err = _common_setup_page(self, addr, page, offs, 1, 0);
if (err < 0) {
emsg = "setup page fail";
goto err_is_transvr_hw_ready;
}
/* Check feature supported
* [Note]
* Some of transceiver/cables doesn't support "Status Indicators"
* (ex:DAC, RJ45 copper SFP ...etc). In these case, we bypass the
* step of checking Status Indicators, then state machine will take
* the following handle procedure.
*/
err = i2c_smbus_read_byte_data(self->i2c_client_p,
VAL_TRANSVR_COMID_OFFSET);
if (err < 0) {
emsg = "doesn't support Status Indicators";
goto bypass_is_transvr_hw_ready;
}
/* Filter abnormal case */
if (err == ab_val) {
emsg = "detect using unusual definition.";
goto bypass_is_transvr_hw_ready;
}
/* Get Status Indicators */
err = i2c_smbus_read_byte_data(self->i2c_client_p, offs);
if (err < 0) {
emsg = "detect current value fail";
goto err_is_transvr_hw_ready;
}
if ((err & (1<<bit)) == ready) {
return EVENT_TRANSVR_TASK_DONE;
}
return EVENT_TRANSVR_TASK_WAIT;
bypass_is_transvr_hw_ready:
SWPS_DEBUG("%s: %s <type>:%d\n", __func__, emsg, type);
return EVENT_TRANSVR_TASK_DONE;
err_is_transvr_hw_ready:
SWPS_DEBUG("%s: %s <type>:%d\n", __func__, emsg, type);
return EVENT_TRANSVR_INIT_FAIL;
}
static int
_transvr_init_handler(struct transvr_obj_s *self){
int detect[2];
int d_state = STATE_TRANSVR_UNEXCEPTED;
int d_type = TRANSVR_TYPE_ERROR;
int result = ERR_TRANSVR_UNINIT;
int retry = 6; /* (6+1) x 0.3 = 2.1s > spec:2.0s */
int elimit = 63;
char emsg[64] = DEBUG_TRANSVR_STR_VAL;
/* Clean and check callback */
self->state = STATE_TRANSVR_INIT;
if (self->init == NULL) {
snprintf(emsg, elimit, "init() is null");
goto initer_err_case_unexcept_0;
}
/* Detect transceiver information */
result = detect_transvr_state(self, detect);
if (result < 0) {
snprintf(emsg, elimit, "detect_transvr_state() fail");
switch (result) {
case ERR_TRANSVR_I2C_CRASH:
goto initer_err_case_i2c_ceash;
case ERR_TRNASVR_BE_ISOLATED:
goto initer_err_case_be_isolated;
case ERR_TRANSVR_UNEXCPT:
default:
break;
}
goto initer_err_case_retry_1;
}
d_state = detect[0];
d_type = detect[1];
/* Verify transceiver type and state */
switch (_is_transvr_valid(self, d_type, d_state)) {
case 0:
break;
case EVENT_TRANSVR_INIT_DOWN:
goto initer_ok_case_down;;
case EVENT_TRANSVR_INIT_FAIL:
snprintf(emsg, elimit, "transceiver type doesn't support");
goto initer_err_case_alarm_to_user;
case EVENT_TRANSVR_EXCEP_INIT:
default:
goto initer_err_case_unexcept_0;
}
/* Handle reload case */
if (self->type != d_type){
/* This is the protect mechanism. Normally, This case will not happen.
* When State machine detect swap event during initial, It will trigger
* reload function to ensure type correct. */
if (_reload_transvr_obj(self, d_type) < 0){
snprintf(emsg, elimit, "reload object fail");
goto initer_err_case_unexcept_0;
}
}
/* Check transceiver HW initial ready */
switch (_is_transvr_hw_ready(self, d_type)) {
case EVENT_TRANSVR_TASK_DONE:
break;
case EVENT_TRANSVR_TASK_WAIT:
goto initer_err_case_retry_1;
case EVENT_TRANSVR_INIT_FAIL:
default:
goto initer_err_case_unexcept_0;
}
/* Try to update all and check */
if (self->update_all(self, 1) < 0){
/* For some transceiver, EEPROME has lag issues during initial stage.
* In this case, we set status back to STATE_TRANSVR_NEW, than it will
* be checked in next polling cycle. */
goto initer_err_case_retry_1;
}
/* Execute init() call back */
result = self->init(self);
switch (result) {
case EVENT_TRANSVR_TASK_DONE:
break;
case EVENT_TRANSVR_TASK_WAIT:
goto initer_ok_case_wait;
default:
snprintf(emsg, elimit, "undefined init() return:%d\n", result);
goto initer_err_case_unexcept_0;
}
goto initer_ok_case_up;
initer_ok_case_wait:
return EVENT_TRANSVR_TASK_WAIT;
initer_ok_case_up:
self->state = STATE_TRANSVR_CONNECTED;
self->temp = 0;
return EVENT_TRANSVR_INIT_UP;
initer_ok_case_down:
self->temp = 0;
self->state = STATE_TRANSVR_DISCONNECTED;
return EVENT_TRANSVR_INIT_DOWN;
initer_err_case_i2c_ceash:
SWPS_DEBUG("%s: %s <port>:%s <case>:I2C crash\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_UNEXCEPTED;
return EVENT_TRANSVR_I2C_CRASH;
initer_err_case_be_isolated:
SWPS_DEBUG("%s: %s <port>:%s <case>:isolated\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_ISOLATED;
return EVENT_TRANSVR_EXCEP_ISOLATED;
initer_err_case_retry_1:
SWPS_DEBUG("%s: %s <port>:%s <case>:retry\n",
__func__, emsg, self->swp_name);
if (_transvr_handle_retry(self, retry) == 0) {
self->state = STATE_TRANSVR_NEW;
return EVENT_TRANSVR_INIT_REINIT;
}
goto initer_err_case_alarm_to_user;
initer_err_case_unexcept_0:
self->state = STATE_TRANSVR_UNEXCEPTED;
return EVENT_TRANSVR_INIT_FAIL;
initer_err_case_alarm_to_user:
SWPS_DEBUG("%s: %s <port>:%s <case>:alarm_to_user\n",
__func__, emsg, self->swp_name);
self->state = STATE_TRANSVR_UNEXCEPTED;
alarm_msg_2_user(self, "detected transceiver/cables not meet SFF standard");
return EVENT_TRANSVR_INIT_FAIL;
}
static int
setup_transvr_private_cb(struct transvr_obj_s *self,
int transvr_type){
switch (transvr_type){
case TRANSVR_TYPE_SFP:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_QSFP_28:
self->fsm_4_direct = common_fsm_4_direct_mode;
return 0;
case TRANSVR_TYPE_FAKE:
self->fsm_4_direct = fake_fsm_4_direct_mode;
return 0;
default:
break;
}
SWPS_WARN("%s: Detect non-defined type:%d\n", __func__, transvr_type);
return ERR_TRANSVR_UNEXCPT;
}
static struct eeprom_map_s *
get_eeprom_map(int transvr_type){
switch (transvr_type){
case TRANSVR_TYPE_SFP:
return &eeprom_map_sfp;
case TRANSVR_TYPE_QSFP:
case TRANSVR_TYPE_QSFP_PLUS:
return &eeprom_map_qsfp;
case TRANSVR_TYPE_QSFP_28:
return &eeprom_map_qsfp28;
default:
break;
}
SWPS_WARN("%s: Detect non-defined type:%d\n", __func__, transvr_type);
return NULL;
}
static int
setup_transvr_ssize_attr(char *swp_name,
struct transvr_obj_s *self,
struct eeprom_map_s *map_p,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int chan_id,
int run_mode){
switch (run_mode){
case TRANSVR_MODE_DIRECT: /* Direct access device mode */
self->mode = run_mode;
break;
default:
SWPS_ERR("%s: non-defined run_mode:%d\n",
__func__, run_mode);
self->mode = DEBUG_TRANSVR_INT_VAL;
return -1;
}
self->eeprom_map_p = map_p;
self->ioexp_obj_p = ioexp_obj_p;
self->ioexp_virt_offset = ioexp_virt_offset;
self->chan_id = chan_id;
self->layout = transvr_type;
self->type = transvr_type;
self->chipset_type = chipset_type;
self->state = STATE_TRANSVR_NEW;
self->info = STATE_TRANSVR_NEW;
self->auto_tx_disable = VAL_TRANSVR_FUNCTION_DISABLE;
strncpy(self->swp_name, swp_name, 32);
mutex_init(&self->lock);
return 0;
}
static int
setup_i2c_client(struct transvr_obj_s *self){
struct i2c_adapter *adap = NULL;
struct i2c_client *client = NULL;
char err_msg[64] = DEBUG_TRANSVR_STR_VAL;
adap = i2c_get_adapter(self->chan_id);
if(!adap){
snprintf(err_msg, sizeof(err_msg),
"can not get adap:%d", self->chan_id);
goto err_setup_i2c_client;
}
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client){
snprintf(err_msg, sizeof(err_msg),
"can not kzalloc client:%d", self->chan_id);
goto err_setup_i2c_client;
}
client->adapter = adap;
self->i2c_client_p = client;
self->i2c_client_p->addr = VAL_TRANSVR_COMID_ARREESS;
return 0;
err_setup_i2c_client:
SWPS_ERR("%s: %s\n", __func__, err_msg);
return ERR_TRANSVR_UNEXCPT;
}
struct transvr_obj_s *
create_transvr_obj(char *swp_name,
int chan_id,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int run_mode){
struct transvr_obj_s *result_p;
struct eeprom_map_s *map_p;
char err_msg[64] = DEBUG_TRANSVR_STR_VAL;
/* Allocate transceiver object */
map_p = get_eeprom_map(transvr_type);
if (!map_p){
snprintf(err_msg, sizeof(err_msg),
"Invalid transvr_type:%d", transvr_type);
goto err_create_transvr_fail;
}
result_p = kzalloc(sizeof(*result_p), GFP_KERNEL);
if (!result_p){
snprintf(err_msg, sizeof(err_msg), "kzalloc fail");
goto err_create_transvr_fail;
}
/* Prepare static size attributes */
if (setup_transvr_ssize_attr(swp_name,
result_p,
map_p,
ioexp_obj_p,
ioexp_virt_offset,
transvr_type,
chipset_type,
chan_id,
run_mode) < 0){
goto err_create_transvr_sattr_fail;
}
/* Prepare call back functions of object */
if (setup_transvr_private_cb(result_p, transvr_type) < 0){
goto err_create_transvr_sattr_fail;
}
/* Prepare i2c client object */
if (setup_i2c_client(result_p) < 0){
goto err_create_transvr_sattr_fail;
}
return result_p;
err_create_transvr_sattr_fail:
kfree(result_p);
err_create_transvr_fail:
SWPS_ERR("%s: %s <chan>:%d <voff>:%d <type>:%d\n",
__func__, err_msg, chan_id, ioexp_virt_offset, transvr_type);
return NULL;
}
static int
_reload_transvr_obj(struct transvr_obj_s *self,
int new_type){
struct eeprom_map_s *new_map_p;
struct eeprom_map_s *old_map_p = self->eeprom_map_p;
struct i2c_client *old_i2c_p = self->i2c_client_p;
int old_type = self->type;
/* Change state to STATE_TRANSVR_INIT */
self->state = STATE_TRANSVR_INIT;
self->type = new_type;
/* Replace EEPROME map */
new_map_p = get_eeprom_map(new_type);
if (!new_map_p){
goto err_private_reload_func_1;
}
self->eeprom_map_p = new_map_p;
/* Reload i2c client */
if (setup_i2c_client(self) < 0){
goto err_private_reload_func_2;
}
if (setup_transvr_private_cb(self, new_type) < 0){
goto err_private_reload_func_3;
}
kfree(old_i2c_p);
return 0;
err_private_reload_func_3:
SWPS_INFO("%s: init() fail!\n", __func__);
kfree(old_i2c_p);
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = TRANSVR_TYPE_ERROR;
return -2;
err_private_reload_func_2:
self->eeprom_map_p = old_map_p;
self->i2c_client_p = old_i2c_p;
err_private_reload_func_1:
self->state = STATE_TRANSVR_UNEXCEPTED;
self->type = old_type;
SWPS_INFO("%s fail! <type>:0x%02x\n", __func__, new_type);
return -1;
}
static int
reload_transvr_obj(struct transvr_obj_s *self,
int new_type){
int result_val = ERR_TRANSVR_UNEXCPT;
/* Reload phase */
result_val = _reload_transvr_obj(self, new_type);
if (result_val < 0){
SWPS_INFO("%s: reload phase fail! <err>:%d\n",
__func__, result_val);
return EVENT_TRANSVR_RELOAD_FAIL;
}
/* Initial phase */
result_val = _transvr_init_handler(self);
if (result_val < 0){
SWPS_INFO("%s: initial phase fail! <err>:%d\n",
__func__, result_val);
}
return result_val;
}

View File

@ -0,0 +1,168 @@
#ifndef TRANSCEIVER_H
#define TRANSCEIVER_H
#include <linux/types.h>
/* Transceiver type define */
#define TRANSVR_TYPE_UNKNOW_1 (0x00)
#define TRANSVR_TYPE_UNKNOW_2 (0xff)
#define TRANSVR_TYPE_SFP (0x03) /* Define for SFP, SFP+, SFP28 */
#define TRANSVR_TYPE_QSFP (0x0c)
#define TRANSVR_TYPE_QSFP_PLUS (0x0d)
#define TRANSVR_TYPE_QSFP_28 (0x11)
#define TRANSVR_TYPE_UNPLUGGED (0xfa) /* Define for ERROR handle */
#define TRANSVR_TYPE_FAKE (0xfc) /* Define for ERROR handle */
#define TRANSVR_TYPE_INCONSISTENT (0xfd) /* Define for ERROR handle */
#define TRANSVR_TYPE_ERROR (0xfe) /* Define for ERROR handle */
/* Transceiver mode define */
#define TRANSVR_MODE_DIRECT (21000)
/* Transceiver state define
* [Note]
* 1. State is used to represent the state of "Transceiver" and "Object".
* 2. State for different target has different means. The description as following:
*/
#define STATE_TRANSVR_CONNECTED (0) /* [Transvr]:Be plugged in. [Obj]:Link up, and work normally. */
#define STATE_TRANSVR_NEW (-100) /* [Transvr]:(Not used) [Obj]:Create */
#define STATE_TRANSVR_INIT (-101) /* [Transvr]:Be plugged in. [Obj]:Link up, and in initial process. */
#define STATE_TRANSVR_ISOLATED (-102) /* [Transvr]:Be plugged in. [Obj]:Isolate, and not provide service. */
#define STATE_TRANSVR_SWAPPED (-200) /* [Transvr]:Be plugged in. [Obj]:(Not used) */
#define STATE_TRANSVR_DISCONNECTED (-300) /* [Transvr]:Un-plugged. [Obj]:Link down, and not provide service. */
#define STATE_TRANSVR_UNEXCEPTED (-901) /* [Transvr]:Any [Obj]:Any, and not in expect case. */
/* Event for task handling */
#define EVENT_TRANSVR_TASK_WAIT (2101)
#define EVENT_TRANSVR_TASK_DONE (0)
#define EVENT_TRANSVR_TASK_FAIL (-2101)
/* Event for initial handling */
#define EVENT_TRANSVR_INIT_UP (2201)
#define EVENT_TRANSVR_INIT_DOWN (1)
#define EVENT_TRANSVR_INIT_REINIT (-2201)
#define EVENT_TRANSVR_INIT_FAIL (-2202)
/* Event for others */
#define EVENT_TRANSVR_RELOAD_FAIL (-2301)
#define EVENT_TRANSVR_EXCEP_INIT (-2401)
#define EVENT_TRANSVR_EXCEP_UP (-2402)
#define EVENT_TRANSVR_EXCEP_DOWN (-2403)
#define EVENT_TRANSVR_EXCEP_SWAP (-2404)
#define EVENT_TRANSVR_EXCEP_EXCEP (-2405)
#define EVENT_TRANSVR_EXCEP_ISOLATED (-2406)
#define EVENT_TRANSVR_I2C_CRASH (-2501)
/* Transceiver error code define */
#define ERR_TRANSVR_UNINIT (-201)
#define ERR_TRANSVR_UNPLUGGED (-202)
#define ERR_TRANSVR_ABNORMAL (-203)
#define ERR_TRANSVR_NOSTATE (-204)
#define ERR_TRANSVR_NOTSUPPORT (-205)
#define ERR_TRANSVR_BADINPUT (-206)
#define ERR_TRANSVR_UPDATE_FAIL (-207)
#define ERR_TRANSVR_RELOAD_FAIL (-208)
#define ERR_TRANSVR_INIT_FAIL (-209)
#define ERR_TRANSVR_UNDEFINED (-210)
#define ERR_TRANSVR_TASK_FAIL (-211)
#define ERR_TRANSVR_TASK_BUSY (-212)
#define ERR_TRANSVR_FUNC_DISABLE (-214)
#define ERR_TRANSVR_I2C_CRASH (-297)
#define ERR_TRNASVR_BE_ISOLATED (-298)
#define ERR_TRANSVR_UNEXCPT (-299)
/* For debug */
#define DEBUG_TRANSVR_INT_VAL (-99)
#define DEBUG_TRANSVR_HEX_VAL (0xfe)
#define DEBUG_TRANSVR_STR_VAL "ERROR"
/* For system internal */
#define VAL_TRANSVR_COMID_ARREESS (0x50)
#define VAL_TRANSVR_COMID_OFFSET (0x00)
#define VAL_TRANSVR_8472_READY_ADDR (0x51)
#define VAL_TRANSVR_8472_READY_PAGE (-1)
#define VAL_TRANSVR_8472_READY_OFFSET (110)
#define VAL_TRANSVR_8472_READY_BIT (0)
#define VAL_TRANSVR_8472_READY_VALUE (0)
#define VAL_TRANSVR_8472_READY_ABNORMAL (0xff)
#define VAL_TRANSVR_8436_READY_ADDR (0x50)
#define VAL_TRANSVR_8436_READY_PAGE (-1)
#define VAL_TRANSVR_8436_READY_OFFSET (2)
#define VAL_TRANSVR_8436_READY_BIT (0)
#define VAL_TRANSVR_8436_READY_VALUE (0)
#define VAL_TRANSVR_8436_READY_ABNORMAL (0xff)
#define VAL_TRANSVR_8436_PWD_ADDR (0x50)
#define VAL_TRANSVR_8436_PWD_PAGE (-1)
#define VAL_TRANSVR_8436_PWD_OFFSET (123)
#define VAL_TRANSVR_PAGE_FREE (-99)
#define VAL_TRANSVR_PAGE_SELECT_OFFSET (127)
#define VAL_TRANSVR_PAGE_SELECT_DELAY (5)
#define VAL_TRANSVR_TASK_RETRY_FOREVER (-999)
#define VAL_TRANSVR_FUNCTION_DISABLE (-1)
#define STR_TRANSVR_SFP "SFP"
#define STR_TRANSVR_QSFP "QSFP"
#define STR_TRANSVR_QSFP_PLUS "QSFP+"
#define STR_TRANSVR_QSFP28 "QSFP28"
/* BCM chip type define */
#define BCM_CHIP_TYPE_TOMAHAWK (31002) /* Redwood, Cypress */
/* Info from transceiver EEPROM */
struct eeprom_map_s {
int addr_rx_los; int page_rx_los; int offset_rx_los; int length_rx_los;
int addr_tx_disable; int page_tx_disable; int offset_tx_disable; int length_tx_disable;
int addr_tx_fault; int page_tx_fault; int offset_tx_fault; int length_tx_fault;
};
/* Class of transceiver object */
struct transvr_obj_s {
/* ========== Object private property ==========
*/
struct device *transvr_dev_p;
struct eeprom_map_s *eeprom_map_p;
struct i2c_client *i2c_client_p;
struct ioexp_obj_s *ioexp_obj_p;
struct mutex lock;
char swp_name[32];
int auto_tx_disable;
int chan_id;
int chipset_type;
int curr_page;
int info;
int ioexp_virt_offset;
int lane_id[8];
int layout;
int mode;
int retry;
int state;
int temp;
int type;
/* ========== Object private functions ==========
*/
int (*init)(struct transvr_obj_s *self);
int (*update_all)(struct transvr_obj_s *self, int show_err);
int (*fsm_4_direct)(struct transvr_obj_s* self, char *caller_name);
};
/* For AVL Mapping */
struct transvr_avl_s {
char vendor_name[32];
char vendor_pn[32];
int (*init)(struct transvr_obj_s *self);
};
struct transvr_obj_s *
create_transvr_obj(char *swp_name,
int chan_id,
struct ioexp_obj_s *ioexp_obj_p,
int ioexp_virt_offset,
int transvr_type,
int chipset_type,
int run_mode);
void alarm_msg_2_user(struct transvr_obj_s *self, char *emsg);
#endif /* TRANSCEIVER_H */

View File

@ -0,0 +1,234 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 Inventec, 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 3 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, see <http://www.gnu.org/licenses/>.
"""
Usage: %(scriptName)s [options] command object
options:
-h | --help : this help message
-d | --debug : run with debug mode
-f | --force : ignore error during installation or clean
command:
install : install drivers and generate related sysfs nodes
clean : uninstall drivers and remove related sysfs nodes
"""
import os
import commands
import sys, getopt
import logging
import re
import time
from collections import namedtuple
DEBUG = False
args = []
FORCE = 0
i2c_prefix = '/sys/bus/i2c/devices/'
if DEBUG == True:
print sys.argv[0]
print 'ARGV :', sys.argv[1:]
def main():
global DEBUG
global args
global FORCE
if len(sys.argv)<2:
show_help()
options, args = getopt.getopt(sys.argv[1:], 'hdf', ['help',
'debug',
'force',
])
if DEBUG == True:
print options
print args
print len(sys.argv)
for opt, arg in options:
if opt in ('-h', '--help'):
show_help()
elif opt in ('-d', '--debug'):
DEBUG = True
logging.basicConfig(level=logging.INFO)
elif opt in ('-f', '--force'):
FORCE = 1
else:
logging.info('no option')
for arg in args:
if arg == 'install':
install()
elif arg == 'clean':
uninstall()
else:
show_help()
return 0
def show_help():
print __doc__ % {'scriptName' : sys.argv[0].split("/")[-1]}
sys.exit(0)
def show_log(txt):
if DEBUG == True:
print "[D7032]"+txt
return
def exec_cmd(cmd, show):
logging.info('Run :'+cmd)
status, output = commands.getstatusoutput(cmd)
show_log (cmd +"with result:" + str(status))
show_log (" output:"+output)
if status:
logging.info('Failed :'+cmd)
if show:
print('Failed :'+cmd)
return status, output
instantiate =[
#'echo pca9548 0x71> /sys/bus/i2c/devices/i2c-0/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-2/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-3/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-4/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-5/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-6/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-7/new_device',
#'echo pca9548 0x72> /sys/bus/i2c/devices/i2c-0/i2c-8/new_device',
'echo inv_eeprom 0x53 > /sys/bus/i2c/devices/i2c-0/new_device']
#'echo inv_psoc 0x66> /sys/bus/i2c/devices/i2c-0/new_device',
#'echo inv_cpld 0x55> /sys/bus/i2c/devices/i2c-0/new_device']
drivers =[
'lpc_ich',
'i2c-i801',
'i2c-mux',
'i2c-mux-pca954x',
'i2c-dev',
'inv_eeprom',
'inv_platform',
'inv_psoc',
'inv_cpld',
'swps']
def system_install():
global FORCE
#install drivers
for i in range(0,len(drivers)):
status, output = exec_cmd("modprobe "+drivers[i], 1)
if status:
print output
if FORCE == 0:
return status
#instantiate devices
for i in range(0,len(instantiate)):
#time.sleep(1)
status, output = exec_cmd(instantiate[i], 1)
if status:
print output
if FORCE == 0:
return status
for i in range(10,18):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-2/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(18,26):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-3/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(26,34):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-4/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(34,42):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-5/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(42,50):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-6/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(50,58):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-7/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
for i in range(58,64):
status, output =exec_cmd("echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-8/i2c-"+str(i)+"/new_device", 1)
if status:
print output
if FORCE == 0:
return status
return
def system_ready():
if not device_found():
return False
return True
def install():
if not device_found():
print "No device, installing...."
status = system_install()
if status:
if FORCE == 0:
return status
else:
print " D7032 devices detected...."
return
def uninstall():
global FORCE
#uninstall drivers
for i in range(len(drivers)-1,-1,-1):
status, output = exec_cmd("rmmod "+drivers[i], 1)
if status:
print output
if FORCE == 0:
return status
return
def device_found():
ret1, log = exec_cmd("ls "+i2c_prefix+"*0072", 0)
ret2, log = exec_cmd("ls "+i2c_prefix+"i2c-2", 0)
return not(ret1 or ret2)
if __name__ == "__main__":
main()

View File

@ -1,3 +1,8 @@
sonic-inventec-platform-modules (1.1.0) unstable; urgency=low
* Add support for Inventec d7054
-- developer <swsp@inventec.com> Tue, 18 Jul 2017 16:30:45 +0800
sonic-inventec-platform-modules (1.0.0) unstable; urgency=low
* Add support for Inventec d7032

View File

@ -10,3 +10,8 @@ Architecture: amd64
Depends: linux-image-3.16.0-4-amd64
Description: kernel modules for platform devices such as fan, led
Package: platform-modules-d7054q28b
Architecture: amd64
Depends: linux-image-3.16.0-4-amd64
Description: kernel modules for platform devices such as fan, led

View File

@ -15,43 +15,14 @@ case "$1" in
start)
echo -n "Setting up board... "
depmod -a
modprobe inv_platform
modprobe inv_psoc
modprobe inv_cpld
/usr/local/bin/onie-syseeprom -S /tmp/eeprom
# Attach 32 instances of EEPROM driver QSFP ports on IO module 1
#eeprom can dump data using below command
for ((i=22;i<=29;i++));
do
echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-4/i2c-$i/new_device
done
for ((i=30;i<=37;i++));
do
echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-5/i2c-$i/new_device
done
for ((i=6;i<=13;i++));
do
echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-2/i2c-$i/new_device
done
for ((i=14;i<=21;i++));
do
echo sff8436 0x50 > /sys/bus/i2c/devices/i2c-0/i2c-3/i2c-$i/new_device
done
depmod -a
/usr/local/bin/inventec_d7032_util.py -f install
echo "done."
;;
stop)
rmmod inv_cpld
rmmod inv_psoc
rmmod inv_platform
/usr/local/bin/inventec_d7032_util.py -f clean
echo "done."
;;

View File

@ -1 +1 @@
d7032q28b/conf/d7032q28b-modules.conf etc/modules-load.d
d7032q28b/utils/inventec_d7032_util.py usr/local/bin

View File

@ -0,0 +1,5 @@
description "SONiC platform service"
respawn
exec /usr/local/bin/inventec_d7032_util.py -f install

View File

@ -0,0 +1,40 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: setup-board
# Required-Start:
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start: S
# Default-Stop: 0 6
# Short-Description: Setup Inventec d7054q28b board.
### END INIT INFO
case "$1" in
start)
echo -n "Setting up board... "
depmod -a
/usr/local/bin/inventec_d7054_util.py -f install
echo "done."
;;
stop)
/usr/local/bin/inventec_d7054_util.py -f clean
echo "done."
;;
force-reload|restart)
echo "Not supported"
;;
*)
echo "Usage: /etc/init.d/platform-modules-d7054q28b.init {start|stop}"
exit 1
;;
esac
exit 0

View File

@ -0,0 +1 @@
d7054q28b/utils/inventec_d7054_util.py usr/local/bin

View File

@ -0,0 +1,5 @@
description "SONiC platform service"
respawn
exec /usr/local/bin/inventec_d7054_util.py -f install

View File

@ -14,7 +14,7 @@ export INSTALL_MOD_DIR:=extra
KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= d7032q28b
MODULE_DIRS:= d7032q28b d7054q28b
%:
dh $@
@ -31,7 +31,8 @@ override_dh_auto_install:
cp $(MOD_SRC_DIR)/$${mod}/modules/*.ko \
debian/platform-modules-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
dh_installdirs -pplatform-modules-$${mod} usr/local/bin; \
cp -r $(MOD_SRC_DIR)/$${mod}/utils/onie-syseeprom debian/platform-modules-$${mod}/usr/local/bin/; \
cp $(MOD_SRC_DIR)/$${mod}/utils/* \
debian/platform-modules-$${mod}/usr/local/bin; \
done)
override_dh_usrlocal: