[Inventec]: Add Inventec D7054 platform support and update D7032 platform support (#1052)
This commit is contained in:
parent
2a56479187
commit
1ee8ed9216
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -0,0 +1,2 @@
|
||||
SAI_INIT_CONFIG_FILE=/etc/bcm/th-d7054q28b-48x10g-6x100g.config.bcm
|
||||
SAI_NUM_ECMP_MEMBERS=32
|
@ -0,0 +1,4 @@
|
||||
CONSOLE_PORT=0x3f8
|
||||
CONSOLE_DEV=0
|
||||
CONSOLE_SPEED=115200
|
||||
VAR_LOG_SIZE=1024
|
146
device/inventec/x86_64-inventec_d7054q28b-r0/minigraph.xml
Normal file
146
device/inventec/x86_64-inventec_d7054q28b-r0/minigraph.xml
Normal 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>
|
@ -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)
|
209
device/inventec/x86_64-inventec_d7054q28b-r0/plugins/sfputil.py
Executable file
209
device/inventec/x86_64-inventec_d7054q28b-r0/plugins/sfputil.py
Executable 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
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -195,3 +195,4 @@ module_init(plat_redwood_x86_init);
|
||||
MODULE_AUTHOR("Inventec");
|
||||
MODULE_DESCRIPTION("Redwood_x86 Platform devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
Binary file not shown.
@ -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
|
||||
|
@ -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);
|
@ -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");
|
||||
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -16,42 +16,13 @@ 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
|
||||
|
||||
/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."
|
||||
|
||||
;;
|
||||
|
@ -1 +1 @@
|
||||
d7032q28b/conf/d7032q28b-modules.conf etc/modules-load.d
|
||||
d7032q28b/utils/inventec_d7032_util.py usr/local/bin
|
||||
|
@ -0,0 +1,5 @@
|
||||
description "SONiC platform service"
|
||||
|
||||
respawn
|
||||
|
||||
exec /usr/local/bin/inventec_d7032_util.py -f install
|
@ -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
|
@ -0,0 +1 @@
|
||||
d7054q28b/utils/inventec_d7054_util.py usr/local/bin
|
@ -0,0 +1,5 @@
|
||||
description "SONiC platform service"
|
||||
|
||||
respawn
|
||||
|
||||
exec /usr/local/bin/inventec_d7054_util.py -f install
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user