[Juniper] Re-organizing sonic_platform modules (#4448)
This patch set implements the following: - Fixes the conflicts in chassis.py / platform.py in sonic_platfrom - Consolidating the common library files in sonic_platform - Moving QFX5210 specific drivers to qfx5210/modules - Moving Juniper common fpga drivers to common/modules - Cleaning up the platform driver files - Bug fixes in QFX5210 platform monitor & initialiazation script - Fixing the bugs in QFX5210 eeprom parsing Signed-off-by: Ciju Rajan K <crajank@juniper.net>
This commit is contained in:
parent
13bef09889
commit
d1940b2cf4
0
device/juniper/x86_64-juniper_qfx5200-r0/media_settings.json
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/media_settings.json
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/eeprom.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/eeprom.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/led_control.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/led_control.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/psuutil.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/psuutil.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/qfx5200_eeprom_data.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/qfx5200_eeprom_data.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/qfx5200_sfp_init.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/qfx5200_sfp_init.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/sfputil.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5200-r0/plugins/sfputil.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5210-r0/led_proc_init.soc
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5210-r0/led_proc_init.soc
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5210-r0/plugins/psuutil.py
Executable file → Normal file
0
device/juniper/x86_64-juniper_qfx5210-r0/plugins/psuutil.py
Executable file → Normal file
@ -0,0 +1,331 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Name: juniper_qfx5210_eepromconv.py version: 1.0
|
||||
#
|
||||
# Description: This file contains the code to store the contents of Board EEPROM in file
|
||||
#
|
||||
# Copyright (c) 2020, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Notice and Disclaimer: This code is licensed to you under the GNU General
|
||||
# Public License as published by the Free Software Foundation, version 3 or
|
||||
# any later version. This code is not an official Juniper product. You can
|
||||
# obtain a copy of the License at <https://www.gnu.org/licenses/>
|
||||
#
|
||||
# OSS License:
|
||||
#
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Third-Party Code: This code may depend on other components under separate
|
||||
# copyright notice and license terms. Your use of the source code for those
|
||||
# components is subject to the terms and conditions of the respective license
|
||||
# as noted in the Third-Party source code file.
|
||||
|
||||
import os
|
||||
import commands
|
||||
import binascii
|
||||
from sonic_eeprom import eeprom_tlvinfo
|
||||
|
||||
def main():
|
||||
eeprom_qfx5210 = Eeprom()
|
||||
|
||||
FANTYPE_PATH = '/sys/bus/i2c/devices/17-0068/fan1_direction'
|
||||
isPlatformAFO = False
|
||||
try:
|
||||
fan_type_file = open(FANTYPE_PATH)
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
fan_type = -1
|
||||
else:
|
||||
fan_type = fan_type_file.read()
|
||||
fan_type_file.close()
|
||||
|
||||
if (int(fan_type) == -1 or int(fan_type) == 0):
|
||||
if (int(fan_type) == -1):
|
||||
print "unable to open sys file for fan handling, defaulting it to AFO"
|
||||
|
||||
isPlatformAFO = True
|
||||
else:
|
||||
isPlatformAFO = False
|
||||
|
||||
# creating the "/var/run/eeprom" file and storing values of CPU board EEPROM in this file.
|
||||
eeprom_file = open ("/var/run/eeprom", "a+")
|
||||
eeprom_file.write("\n")
|
||||
if isPlatformAFO == True:
|
||||
eeprom_file.write("Fan Type=AFO\r\n")
|
||||
else:
|
||||
eeprom_file.write("Fan Type=AFI\r\n")
|
||||
eeprom_file.write("\n")
|
||||
|
||||
# Write the contents of CPU Board EEPROM to file
|
||||
eeprom_file.write("CPU Board EEPROM (0x56)\r\n")
|
||||
eeprom_file.write("===============================\r\n")
|
||||
eeprom_file.write("Product Name=%s\r\n" % eeprom_qfx5210.modelstr())
|
||||
eeprom_file.write("Part Number=%s\r\n" % eeprom_qfx5210.part_number_str())
|
||||
eeprom_file.write("Serial Number=%s\r\n" % eeprom_qfx5210.serial_number_str())
|
||||
eeprom_file.write("MAC Address=%s\r\n" % eeprom_qfx5210.base_mac_address())
|
||||
eeprom_file.write("Manufacture Date=%s\r\n" % eeprom_qfx5210.manuDate_str())
|
||||
eeprom_file.write("Platform Name=%s\r\n" % eeprom_qfx5210.platform_str())
|
||||
eeprom_file.write("Number of MAC Addresses=%s\r\n" % eeprom_qfx5210.MACsize_str())
|
||||
eeprom_file.write("Vendor Name=%s\r\n" % eeprom_qfx5210.vendor_name_str())
|
||||
eeprom_file.write("Manufacture Name=%s\r\n" % eeprom_qfx5210.manufacture_name_str())
|
||||
|
||||
CPUeepromFileCmd = 'cat /sys/devices/pci0000:00/0000:00:1f.3/i2c-0/0-0056/eeprom > /etc/init.d/eeprom_qfx5210_ascii'
|
||||
# Write the contents of CPU EEPROM to file
|
||||
try:
|
||||
os.system(CPUeepromFileCmd)
|
||||
except OSError:
|
||||
print 'Error: Execution of "%s" failed', CPUeepromFileCmd
|
||||
return False
|
||||
|
||||
eeprom_ascii = '/etc/init.d/eeprom_qfx5210_ascii'
|
||||
# Read file contents in Hex format
|
||||
with open(eeprom_ascii, 'rb') as Hexformat:
|
||||
content = Hexformat.read()
|
||||
Hexformatoutput = binascii.hexlify(content)
|
||||
|
||||
eeprom_hex = '/etc/init.d/eeprom_qfx5210_hex'
|
||||
with open(eeprom_hex, 'wb+') as Hexfile:
|
||||
Hexfile.write(Hexformatoutput)
|
||||
|
||||
#Write contents of CPU EEPROM to new file in hexa format
|
||||
with open(eeprom_hex, 'rb') as eeprom_hexfile:
|
||||
eeprom_hexfile.seek(350, 0)
|
||||
vendorext_read = eeprom_hexfile.read(124)
|
||||
vendorext=""
|
||||
vendorext += "0x" + vendorext_read[0:2]
|
||||
for i in range(2,124,2):
|
||||
vendorext += " 0x" + vendorext_read[i:i+2]
|
||||
eeprom_file.write("Vendor Extension=%s\r\n" % str(vendorext))
|
||||
|
||||
eeprom_hexfile.seek(350, 0)
|
||||
IANA_read = eeprom_hexfile.read(8)
|
||||
IANAName = binascii.unhexlify(IANA_read)
|
||||
eeprom_file.write("IANA=%s\r\n" % str(IANAName))
|
||||
|
||||
eeprom_hexfile.seek(358, 0)
|
||||
ASMpartrev_read = eeprom_hexfile.read(4)
|
||||
ASMpartrev = binascii.unhexlify(ASMpartrev_read)
|
||||
eeprom_file.write("Assembly Part Number Rev=%s\r\n" % str(ASMpartrev))
|
||||
|
||||
eeprom_hexfile.seek(374, 0)
|
||||
ASMpartnum_read = eeprom_hexfile.read(20)
|
||||
ASMpartnum_read = binascii.unhexlify(ASMpartnum_read)
|
||||
eeprom_file.write("Assembly Part Number=%s\r\n" % str(ASMpartnum_read))
|
||||
|
||||
eeprom_hexfile.seek(402, 0)
|
||||
ASMID_read = eeprom_hexfile.read(4)
|
||||
ASMID_read_upper = ASMID_read.upper()
|
||||
eeprom_file.write("Assembly ID=0x%s\r\n" % str(ASMID_read_upper))
|
||||
|
||||
ASMHWMajRev_position = eeprom_hexfile.seek(410, 0)
|
||||
ASMHWMajRev_read = eeprom_hexfile.read(2)
|
||||
eeprom_file.write("Assembly Major Revision=0x%s\r\n" % str(ASMHWMajRev_read))
|
||||
|
||||
eeprom_hexfile.seek(416, 0)
|
||||
ASMHWMinRev_read = eeprom_hexfile.read(2)
|
||||
eeprom_file.write("Assembly Minor Revision=0x%s\r\n" % str(ASMHWMinRev_read))
|
||||
|
||||
eeprom_hexfile.seek(422, 0)
|
||||
Deviation_read = eeprom_hexfile.read(28)
|
||||
Deviation_read_upper = Deviation_read.upper()
|
||||
eeprom_file.write("Deviation=0x%s\r\n" % str(Deviation_read_upper))
|
||||
|
||||
eeprom_hexfile.seek(450, 0)
|
||||
CLEI_read = eeprom_hexfile.read(20)
|
||||
CLEI_name = binascii.unhexlify(CLEI_read)
|
||||
eeprom_file.write("CLEI code=%s\r\n" % str(CLEI_name))
|
||||
|
||||
eeprom_dict = eeprom_qfx5210.system_eeprom_info()
|
||||
key = '0x29'
|
||||
if key in eeprom_dict.keys():
|
||||
onie_version_str = eeprom_dict.get('0x29', None)
|
||||
else:
|
||||
onie_version_str = "N/A"
|
||||
eeprom_file.write("ONIE Version=%s\r\n" % onie_version_str)
|
||||
|
||||
crc_str = eeprom_dict.get('0xFE', None)
|
||||
eeprom_file.write("CRC=%s\r\n" % crc_str)
|
||||
eeprom_file.close()
|
||||
return True
|
||||
|
||||
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
|
||||
def __init__(self):
|
||||
self.__eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0056/eeprom"
|
||||
super(Eeprom, self).__init__(self.__eeprom_path, 0, '', True)
|
||||
self.__eeprom_tlv_dict = dict()
|
||||
try:
|
||||
self.__eeprom_data = self.read_eeprom()
|
||||
except:
|
||||
self.__eeprom_data = "N/A"
|
||||
raise RuntimeError("Eeprom is not Programmed")
|
||||
else:
|
||||
eeprom = self.__eeprom_data
|
||||
|
||||
if not self.is_valid_tlvinfo_header(eeprom):
|
||||
return
|
||||
|
||||
total_length = (ord(eeprom[9]) << 8) | ord(eeprom[10])
|
||||
tlv_index = self._TLV_INFO_HDR_LEN
|
||||
tlv_end = self._TLV_INFO_HDR_LEN + total_length
|
||||
|
||||
while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end:
|
||||
if not self.is_valid_tlv(eeprom[tlv_index:]):
|
||||
break
|
||||
|
||||
tlv = eeprom[tlv_index:tlv_index + 2
|
||||
+ ord(eeprom[tlv_index + 1])]
|
||||
code = "0x%02X" % (ord(tlv[0]))
|
||||
|
||||
if ord(tlv[0]) == self._TLV_CODE_VENDOR_EXT:
|
||||
value = str((ord(tlv[2]) << 24) | (ord(tlv[3]) << 16) |
|
||||
(ord(tlv[4]) << 8) | ord(tlv[5]))
|
||||
value += str(tlv[6:6 + ord(tlv[1])])
|
||||
else:
|
||||
name, value = self.decoder(None, tlv)
|
||||
|
||||
self.__eeprom_tlv_dict[code] = value
|
||||
if ord(eeprom[tlv_index]) == self._TLV_CODE_CRC_32:
|
||||
break
|
||||
|
||||
tlv_index += ord(eeprom[tlv_index+1]) + 2
|
||||
|
||||
def serial_number_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
return results[2]
|
||||
|
||||
def base_mac_address(self):
|
||||
(is_valid, t) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_MAC_BASE)
|
||||
if not is_valid or t[1] != 6:
|
||||
return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(self.__eeprom_data)
|
||||
|
||||
return ":".join([binascii.b2a_hex(T) for T in t[2]])
|
||||
|
||||
def modelstr(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_PRODUCT_NAME)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def part_number_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_PART_NUMBER)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def serial_tag_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_SERVICE_TAG)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def revision_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_DEVICE_VERSION)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def manuDate_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_MANUF_DATE)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def platform_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_PLATFORM_NAME)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def MACsize_str(self):
|
||||
(is_valid, t) = self.get_tlv_field(self.__eeprom_data, self._TLV_CODE_MAC_SIZE)
|
||||
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return str((ord(t[2][0]) << 8) | ord(t[2][1]))
|
||||
|
||||
def vendor_name_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_VENDOR_NAME)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def manufacture_name_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_MANUF_NAME)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
return results[2]
|
||||
|
||||
def onie_version_str(self):
|
||||
value = ""
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_ONIE_VERSION)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
for c in results[2:2 + ord(results[1])]:
|
||||
value += "0x%02X " % (ord(c),)
|
||||
|
||||
return value
|
||||
|
||||
def vendor_ext_str(self):
|
||||
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_VENDOR_EXT)
|
||||
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
vendor_value_formatted = ''.join( [ " 0x" + "%02X " % ord( x ) for x in results[2] ] ).strip()
|
||||
vendor_value_hexvalue = ''.join( ["%02X" % ord( x ) for x in results[2] ] ).strip()
|
||||
|
||||
return vendor_value_formatted, vendor_value_hexvalue
|
||||
|
||||
def crc_str(self):
|
||||
(is_valid, results) = self.get_tlv_field(
|
||||
self.__eeprom_data, self._TLV_CODE_CRC_32)
|
||||
if not is_valid:
|
||||
return "N/A"
|
||||
|
||||
def system_eeprom_info(self):
|
||||
"""
|
||||
Returns a dictionary, where keys are the type code defined in
|
||||
ONIE EEPROM format and values are their corresponding values
|
||||
found in the system EEPROM.
|
||||
"""
|
||||
return self.__eeprom_tlv_dict
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"skip_thermalctld": true,
|
||||
"skip_ledd": true
|
||||
}
|
||||
|
@ -0,0 +1,661 @@
|
||||
/*
|
||||
* Juniper Networks TMC GPIO driver
|
||||
*
|
||||
* Copyright (C) 2020 Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This driver implement the GPIO set/get functionality
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include "jnx-tmc.h"
|
||||
|
||||
#define TMC_GPIO_MAX_BITS_PER_REG 16
|
||||
#define TMC_GPIO_SFP_MAX_BITS_PER_REG 2
|
||||
#define TMC_GPIO_PTPCFG_MAX_BITS_PER_REG 8
|
||||
|
||||
#define TMC_GPIO_FIND_GROUP(gpio) \
|
||||
((gpio) / TMC_GPIO_MAX_BITS_PER_REG)
|
||||
#define TMC_GPIO_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_SFP_FIND_GROUP(gpio) \
|
||||
((gpio) / TMC_GPIO_SFP_MAX_BITS_PER_REG)
|
||||
#define TMC_GPIO_SFP_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_SFP_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_PTPCFG_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_PTPCFG_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_MAX_NGPIO_PER_GROUP 320
|
||||
|
||||
#define TMC_PFE_QSFP_RESET_OFFSET 0x4
|
||||
#define TMC_PFE_QSFP_PRESENT_OFFSET 0x8
|
||||
#define TMC_PFE_QSFP_PHY_RESET_OFFSET 0x10
|
||||
#define TMC_PFE_QSFP_LPMOD_OFFSET 0x78
|
||||
#define TMC_PFE_QSFP_LED_CTRL_OFFSET 0x20
|
||||
|
||||
#define TMC_PFE_LANES_GREEN_LED_VALUE 0x3
|
||||
#define TMC_PFE_LANE0_GREEN_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_GREEN_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_GREEN_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_GREEN_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_LANES_BEACON_LED_VALUE 0x2
|
||||
#define TMC_PFE_LANE0_BEACON_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_BEACON_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_BEACON_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_BEACON_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_LANES_FAULT_LED_VALUE 0x1
|
||||
#define TMC_PFE_LANE0_FAULT_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_FAULT_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_FAULT_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_FAULT_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_SFPSB0_TX_DISABLE_OFFSET 0x0
|
||||
#define TMC_PFE_SFPSB0_LED_CTRL_OFFSET 0xC
|
||||
#define TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET 0x14
|
||||
#define TMC_PFE_SFPSB0_PRESENT_OFFSET 0x18
|
||||
#define TMC_PFE_SFPSB0_LOSS_OFFSET 0x1C
|
||||
#define TMC_PFE_SFPSB0_TX_FAULT_OFFSET 0x20
|
||||
|
||||
#define TMC_PFE_SFPSB1_TX_DISABLE_OFFSET 0x0
|
||||
#define TMC_PFE_SFPSB1_LED_CTRL_OFFSET 0x8
|
||||
#define TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET 0x10
|
||||
#define TMC_PFE_SFPSB1_PRESENT_OFFSET 0x14
|
||||
#define TMC_PFE_SFPSB1_LOSS_OFFSET 0x18
|
||||
#define TMC_PFE_SFPSB1_TX_FAULT_OFFSET 0x1C
|
||||
|
||||
/*
|
||||
* Index 4 to 15 is used for QSFP starting with
|
||||
* QSFP_LED_LANE0_GREEN. To keep multibit set/get common
|
||||
* starting SFP_LED_LANE0_GREEN with 16 which will avoid
|
||||
* conflict with QSFP enums.
|
||||
*/
|
||||
#define SFP_LED_OP_START_INDEX 16
|
||||
|
||||
/*
|
||||
* Used for off-setting SFP led op index
|
||||
*/
|
||||
#define SFP_LED_OP_OFFSET 0xB
|
||||
|
||||
/*
|
||||
* SFP slave blocks
|
||||
*/
|
||||
#define SFP_SLAVE0_BLOCK 0x1
|
||||
#define SFP_SLAVE1_BLOCK 0x2
|
||||
|
||||
/*
|
||||
* each group represent the 16 gpios.
|
||||
* QSFP_RST - QSFP_LPMODE
|
||||
* each bit represent the one gpio
|
||||
* exemple: bits[0:15] - bit0 - gpio0
|
||||
* QSFP_LED_LANE0_GREEN - QSFP_LED_LANE3_FAULT
|
||||
* here, number represent the one gpio
|
||||
* exemple: bits[0:1]
|
||||
* 00 - gpio off, 01 - gpio on [ gpio0]
|
||||
* 00 - gpio off, 10 - gpio on [ gpio1]
|
||||
* 00 - gpio off, 11 - gpio on [ gpio2]
|
||||
*
|
||||
*/
|
||||
enum {
|
||||
QSFP_RST,
|
||||
QSFP_PRESENT,
|
||||
QSFP_PHY_RST,
|
||||
QSFP_LPMOD,
|
||||
QSFP_LED_LANE0_GREEN,
|
||||
QSFP_LED_LANE1_GREEN,
|
||||
QSFP_LED_LANE2_GREEN,
|
||||
QSFP_LED_LANE3_GREEN,
|
||||
QSFP_LED_LANE0_BEACON,
|
||||
QSFP_LED_LANE1_BEACON,
|
||||
QSFP_LED_LANE2_BEACON,
|
||||
QSFP_LED_LANE3_BEACON,
|
||||
QSFP_LED_LANE0_FAULT,
|
||||
QSFP_LED_LANE1_FAULT,
|
||||
QSFP_LED_LANE2_FAULT,
|
||||
QSFP_LED_LANE3_FAULT,
|
||||
TMC_PFE_GPIO_GROUP_MAX
|
||||
};
|
||||
|
||||
enum sfp_op {
|
||||
SFP_TX_DISABLE,
|
||||
SFP_LED_ACTIVITY,
|
||||
SFP_PRESENT,
|
||||
SFP_SFP_LOS,
|
||||
SFP_TX_FAULT,
|
||||
SFP_LED_LANE0_GREEN = SFP_LED_OP_START_INDEX,
|
||||
SFP_LED_LANE1_GREEN,
|
||||
SFP_LED_LANE2_GREEN,
|
||||
SFP_LED_LANE3_GREEN,
|
||||
SFP_LED_LANE0_BEACON,
|
||||
SFP_LED_LANE1_BEACON,
|
||||
SFP_LED_LANE2_BEACON,
|
||||
SFP_LED_LANE3_BEACON,
|
||||
SFP_LED_LANE0_FAULT,
|
||||
SFP_LED_LANE1_FAULT,
|
||||
SFP_LED_LANE2_FAULT,
|
||||
SFP_LED_LANE3_FAULT,
|
||||
TMC_PFE_SFP_GPIO_GROUP_MAX
|
||||
};
|
||||
|
||||
static const u32 group_offset[TMC_PFE_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_QSFP_RESET_OFFSET,
|
||||
TMC_PFE_QSFP_PRESENT_OFFSET,
|
||||
TMC_PFE_QSFP_PHY_RESET_OFFSET,
|
||||
TMC_PFE_QSFP_LPMOD_OFFSET,
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
static const u32 sfp_slaveb0_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_SFPSB0_TX_DISABLE_OFFSET,
|
||||
TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET,
|
||||
TMC_PFE_SFPSB0_PRESENT_OFFSET,
|
||||
TMC_PFE_SFPSB0_LOSS_OFFSET,
|
||||
TMC_PFE_SFPSB0_TX_FAULT_OFFSET,
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
static const u32 sfp_slaveb1_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_SFPSB1_TX_DISABLE_OFFSET,
|
||||
TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET,
|
||||
TMC_PFE_SFPSB1_PRESENT_OFFSET,
|
||||
TMC_PFE_SFPSB1_LOSS_OFFSET,
|
||||
TMC_PFE_SFPSB1_TX_FAULT_OFFSET,
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
struct tmc_gpio_info {
|
||||
int (*get)(struct gpio_chip *, unsigned int);
|
||||
void (*set)(struct gpio_chip *, unsigned int, int);
|
||||
int (*dirin)(struct gpio_chip *, unsigned int);
|
||||
int (*dirout)(struct gpio_chip *, unsigned int, int);
|
||||
};
|
||||
|
||||
struct tmc_gpio_chip {
|
||||
const struct tmc_gpio_info *info;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct gpio_chip gpio;
|
||||
int ngpio;
|
||||
spinlock_t gpio_lock; /* gpio lock */
|
||||
int sfp_slave_block;
|
||||
};
|
||||
|
||||
/* slave gpio max */
|
||||
static int gpio_max = 320;
|
||||
module_param(gpio_max, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
MODULE_PARM_DESC(gpio_max, "Maximum number of gpio for SLAVE TMC GPIO");
|
||||
|
||||
/*
|
||||
* generic bit operation functions
|
||||
*/
|
||||
static u32 tmc_gpio_reset_bits(u32 state, u32 val, u32 shift)
|
||||
{
|
||||
state &= ~(val << shift);
|
||||
return state;
|
||||
};
|
||||
|
||||
static u32 tmc_gpio_set_bits(u32 state, u32 val, u32 shift)
|
||||
{
|
||||
state |= (val << shift);
|
||||
return state;
|
||||
};
|
||||
|
||||
static u32 tmc_gpio_find_bits_val(u32 state, u32 shift, u32 mask)
|
||||
{
|
||||
return ((state >> shift)) & mask;
|
||||
};
|
||||
|
||||
#define to_tmc_chip(chip) \
|
||||
container_of((chip), struct tmc_gpio_chip, gpio)
|
||||
|
||||
/*
|
||||
* tmc_gpio_multiple_bitsop - Generic TMC GPIO multiple bits operation
|
||||
*/
|
||||
static void tmc_gpio_multiple_bitsop(struct tmc_gpio_chip *chip,
|
||||
unsigned int gpiono, u32 group, u32 offset, bool set)
|
||||
{
|
||||
u32 gpio_state, led_val, bit_shift;
|
||||
unsigned long flags;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO multiple bitop group=%u, "
|
||||
"gpiono=%u, offet:=%u, set=%u\n", group, gpiono, offset, set);
|
||||
|
||||
spin_lock_irqsave(&chip->gpio_lock, flags);
|
||||
|
||||
switch (group) {
|
||||
case QSFP_LED_LANE0_GREEN:
|
||||
case SFP_LED_LANE0_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_GREEN:
|
||||
case SFP_LED_LANE1_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_GREEN:
|
||||
case SFP_LED_LANE2_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_GREEN:
|
||||
case SFP_LED_LANE3_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE0_BEACON:
|
||||
case SFP_LED_LANE0_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_BEACON:
|
||||
case SFP_LED_LANE1_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_BEACON:
|
||||
case SFP_LED_LANE2_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_BEACON:
|
||||
case SFP_LED_LANE3_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE0_FAULT:
|
||||
case SFP_LED_LANE0_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_FAULT:
|
||||
case SFP_LED_LANE1_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_FAULT:
|
||||
case SFP_LED_LANE2_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_FAULT:
|
||||
case SFP_LED_LANE3_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
|
||||
default:
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
|
||||
gpio_state = tmc_gpio_set_bits(gpio_state, led_val, bit_shift);
|
||||
} else {
|
||||
gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
|
||||
}
|
||||
|
||||
iowrite32(gpio_state, (iobase+(0x004*gpiono)));
|
||||
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_gpio_one_bitop - Generic TMC GPIO single bit operation
|
||||
*/
|
||||
static void tmc_gpio_one_bitop(struct tmc_gpio_chip *chip,
|
||||
unsigned int bit, u32 offset, bool set)
|
||||
{
|
||||
u32 gpio_state;
|
||||
unsigned long flags;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO one bitop bit=%u, offset=%x, "
|
||||
"set=%u\n", bit, offset, set);
|
||||
|
||||
spin_lock_irqsave(&chip->gpio_lock, flags);
|
||||
|
||||
gpio_state = ioread32(iobase);
|
||||
if (set)
|
||||
gpio_state |= BIT(bit);
|
||||
else
|
||||
gpio_state &= ~BIT(bit);
|
||||
|
||||
iowrite32(gpio_state, iobase);
|
||||
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* tmc_gpio_get_multiple_bitsop - Generic TMC get GPIO multiple bits operation
|
||||
*/
|
||||
static int tmc_gpio_get_multiple_bitsop(struct tmc_gpio_chip *chip,
|
||||
unsigned int gpiono, u32 group, u32 offset)
|
||||
{
|
||||
u32 gpio_state;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO get multiple bitsop group=%u, "
|
||||
"gpiono=%u, offset=%u\n", group, gpiono, offset);
|
||||
|
||||
switch (group) {
|
||||
case QSFP_LED_LANE0_GREEN:
|
||||
case SFP_LED_LANE0_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_GREEN:
|
||||
case SFP_LED_LANE1_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_GREEN:
|
||||
case SFP_LED_LANE2_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_GREEN:
|
||||
case SFP_LED_LANE3_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE0_BEACON:
|
||||
case SFP_LED_LANE0_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_BEACON:
|
||||
case SFP_LED_LANE1_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_BEACON:
|
||||
case SFP_LED_LANE2_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_BEACON:
|
||||
case SFP_LED_LANE3_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE0_FAULT:
|
||||
case SFP_LED_LANE0_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_FAULT:
|
||||
case SFP_LED_LANE1_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_FAULT:
|
||||
case SFP_LED_LANE2_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_FAULT:
|
||||
case SFP_LED_LANE3_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_FAULT_LED_BIT_POSITION, 0x3));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_gpio_get - Read the specified signal of the GPIO device.
|
||||
*/
|
||||
static int tmc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = to_tmc_chip(gc);
|
||||
unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
|
||||
unsigned int bit = TMC_GPIO_FIND_GPIO(gpio);
|
||||
|
||||
if (group >= TMC_PFE_GPIO_GROUP_MAX)
|
||||
return 0;
|
||||
|
||||
switch (group) {
|
||||
case QSFP_RST:
|
||||
case QSFP_PRESENT:
|
||||
case QSFP_PHY_RST:
|
||||
case QSFP_LPMOD:
|
||||
dev_dbg(chip->dev, "TMC GPIO get one bitop group=%u, gpio=%u, "
|
||||
"bit=%u\n", group, gpio, bit);
|
||||
return !!(ioread32(chip->base + group_offset[group])
|
||||
& BIT(bit));
|
||||
default:
|
||||
return tmc_gpio_get_multiple_bitsop(chip, bit, group, group_offset[group]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tmc_gpio_set - Write the specified signal of the GPIO device.
|
||||
*/
|
||||
static void tmc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = to_tmc_chip(gc);
|
||||
unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
|
||||
unsigned int bit = TMC_GPIO_FIND_GPIO(gpio);
|
||||
|
||||
if (group >= TMC_PFE_GPIO_GROUP_MAX)
|
||||
return;
|
||||
|
||||
switch (group) {
|
||||
case QSFP_RST:
|
||||
case QSFP_PRESENT:
|
||||
case QSFP_PHY_RST:
|
||||
case QSFP_LPMOD:
|
||||
dev_dbg(chip->dev, "TMC GPIO one bitop group=%d\n", group);
|
||||
tmc_gpio_one_bitop(chip, bit, group_offset[group], val);
|
||||
break;
|
||||
default:
|
||||
tmc_gpio_multiple_bitsop(chip, bit, group, group_offset[group], val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tmc_gpio_info tmc_gpios[] = {
|
||||
{
|
||||
.get = tmc_gpio_get,
|
||||
.set = tmc_gpio_set,
|
||||
},
|
||||
};
|
||||
|
||||
static void tmc_gpio_setup(struct tmc_gpio_chip *sgc, int id)
|
||||
{
|
||||
struct gpio_chip *chip = &sgc->gpio;
|
||||
const struct tmc_gpio_info *info = sgc->info;
|
||||
|
||||
chip->get = info->get;
|
||||
chip->set = info->set;
|
||||
chip->direction_input = info->dirin;
|
||||
chip->direction_output = info->dirout;
|
||||
chip->dbg_show = NULL;
|
||||
chip->can_sleep = 0;
|
||||
|
||||
if (id == 0) {
|
||||
chip->base = 0;
|
||||
} else if (id == 1) {
|
||||
chip->base = (gpio_max * id);
|
||||
} else {
|
||||
chip->base = -1;
|
||||
}
|
||||
|
||||
chip->ngpio = sgc->ngpio;
|
||||
chip->label = dev_name(sgc->dev);
|
||||
chip->parent = sgc->dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
}
|
||||
|
||||
static int tmc_gpio_of_init(struct device *dev,
|
||||
struct tmc_gpio_chip *chip)
|
||||
{
|
||||
chip->info = &tmc_gpios[0];
|
||||
chip->ngpio = gpio_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tmc_gpio_chip *chip;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
|
||||
dev_dbg(dev, "TMC GPIO probe\n");
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(dev, "TMC GPIO resource 0x%llx, %llu\n",
|
||||
res->start, resource_size(res));
|
||||
|
||||
chip->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!chip->base)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = tmc_gpio_of_init(dev, chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->dev = dev;
|
||||
spin_lock_init(&chip->gpio_lock);
|
||||
|
||||
tmc_gpio_setup(chip, cell->id);
|
||||
|
||||
ret = gpiochip_add(&chip->gpio);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to register TMC gpiochip : %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
dev_info(dev, "TMC GPIO registered at 0x%lx, gpiobase: %d\n",
|
||||
(long unsigned)chip->base, chip->gpio.base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&chip->gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tmc_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpioslave-tmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tmc_gpio_probe,
|
||||
.remove = tmc_gpio_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(tmc_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks TMC FPGA GPIO driver");
|
||||
MODULE_AUTHOR("Ashish Bhensdadia <bashish@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Juniper Networks RE-FPGA qfx platform specific driver
|
||||
*
|
||||
* Copyright (C) 2020 Juniper Networks
|
||||
* Author: Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* This driver implements various features such as
|
||||
* - ALARM led driver
|
||||
* - Fan full speed reset control
|
||||
* - FAN precense detection
|
||||
* - FAN type detection
|
||||
* - Any new QFX specific features which uses RE-FPGA
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
#define NUM_LEDS 7 /* Max number of Alarm + FAN LEDs */
|
||||
|
||||
#define ALARM_MINOR_LED 0
|
||||
#define ALARM_MAJOR_LED 1
|
||||
|
||||
#define REFPGA_PCIE_RESET_CTRL 0x13
|
||||
#define REFPGA_PCIE_ALARM 0x33
|
||||
#define REFPGA_FAN0_CTRL_STAT 0x28
|
||||
|
||||
#define REFPGA_RESET_FAN_SPEED BIT(3)
|
||||
#define REFPGA_OPER_TYPE BIT(0)
|
||||
#define REFPGA_OPER_START BIT(1)
|
||||
#define REFPGA_OPER_DONE BIT(2)
|
||||
|
||||
#define TMC_REFPGA_ADDR_REG 0x0 /* TMC offset: 0x228 */
|
||||
#define TMC_REFPGA_DATA_REG 0x4 /* TMC offset: 0x22C */
|
||||
#define TMC_REFPGA_CTRL_REG 0x8 /* TMC offset: 0x230 */
|
||||
|
||||
#define TMC_REFPGA_READ_CMD 0x3
|
||||
#define TMC_REFPGA_WRITE_CMD 0x2
|
||||
|
||||
#define REFPGA_INTR_NR_GROUPS 1
|
||||
#define REFPGA_INTR_MAX_IRQS_PG 32
|
||||
|
||||
#define MAX_FANS 5
|
||||
|
||||
#define REFPGA_IRQ_MAX_BITS_PER_REG 32
|
||||
|
||||
#define POLL_INTERVAL 5000
|
||||
|
||||
#define AFI_MASK (0x01)
|
||||
#define AFO_MASK (0x02)
|
||||
#define AFI_AFO_MASK (0x03)
|
||||
/*
|
||||
* LED specific data structures
|
||||
*/
|
||||
struct refpga_led {
|
||||
struct led_classdev lc;
|
||||
struct work_struct work;
|
||||
int blink;
|
||||
int on;
|
||||
int bit;
|
||||
void __iomem *addr;
|
||||
};
|
||||
|
||||
struct refpga_led_data {
|
||||
int num_leds;
|
||||
struct refpga_led *leds;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(alarm_led_lock);
|
||||
|
||||
/*
|
||||
* Common routines
|
||||
*/
|
||||
struct refpga_chip {
|
||||
struct refpga_led_data *led;
|
||||
};
|
||||
|
||||
static struct refpga_chip *refpga;
|
||||
|
||||
static DEFINE_MUTEX(refpga_lock);
|
||||
|
||||
static void __iomem *tmc_membase;
|
||||
|
||||
static void wait_for_refpga_oper(void __iomem *base_addr)
|
||||
{
|
||||
volatile u32 done = ~(-1);
|
||||
unsigned long int timeout;
|
||||
void __iomem *addr;
|
||||
|
||||
addr = base_addr + (TMC_REFPGA_CTRL_REG);
|
||||
/*
|
||||
* Wait till the transaction is complete
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
usleep_range(50, 100);
|
||||
done = ioread32(addr);
|
||||
if (done & (REFPGA_OPER_DONE))
|
||||
break;
|
||||
} while(time_before(jiffies, timeout));
|
||||
}
|
||||
static u32 refpga_read(void __iomem *base_addr, u32 refpga_offset)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
mutex_lock(&refpga_lock);
|
||||
iowrite32(refpga_offset, base_addr + (TMC_REFPGA_ADDR_REG));
|
||||
iowrite32(TMC_REFPGA_READ_CMD, base_addr + (TMC_REFPGA_CTRL_REG));
|
||||
wait_for_refpga_oper(base_addr);
|
||||
value = ioread32(base_addr + (TMC_REFPGA_DATA_REG));
|
||||
mutex_unlock(&refpga_lock);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void refpga_write(void __iomem *base_addr, u32 refpga_offset, u32 val)
|
||||
{
|
||||
mutex_lock(&refpga_lock);
|
||||
iowrite32(refpga_offset, base_addr + (TMC_REFPGA_ADDR_REG));
|
||||
iowrite32(val, base_addr + (TMC_REFPGA_DATA_REG));
|
||||
iowrite32(TMC_REFPGA_WRITE_CMD, base_addr + (TMC_REFPGA_CTRL_REG));
|
||||
wait_for_refpga_oper(base_addr);
|
||||
mutex_unlock(&refpga_lock);
|
||||
}
|
||||
|
||||
static bool get_fan_presense(u8 idx)
|
||||
{
|
||||
u8 value = 0x00;
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT;
|
||||
bool ret = 0;
|
||||
|
||||
value = refpga_read(tmc_membase, (offset + (idx * 2)));
|
||||
/*
|
||||
* Get the last two bits of REFPGA_FANx_CTRL_STAT.
|
||||
* REFPGA_FANx_CTRL_STAT register of REFPGA gives the fan airflow
|
||||
* status. There are 5 fans in QFX5200. Last two bits give the AFI
|
||||
* & AFO status. If any of these bits are set, fan is present.
|
||||
*/
|
||||
value = (value & BIT(0)) | (value & BIT(1));
|
||||
if (value)
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_fan_type(u8 idx)
|
||||
{
|
||||
u8 value = 0x00;
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT;
|
||||
int ret = -1;
|
||||
|
||||
value = refpga_read(tmc_membase, (offset + (idx * 2)));
|
||||
/*
|
||||
* Get the last two bits of REFPGA_FANx_CTRL_STAT.
|
||||
* REFPGA_FANx_CTRL_STAT register of REFPGA gives the fan airflow
|
||||
* status. There are 5 fans in QFX5200. Last two bits give the AFI
|
||||
* & AFO status. If bit1 is set, it's AFO and if bit 0 is set,
|
||||
* it's AFI.
|
||||
*
|
||||
* This function will return '1' for AFO, '0' for AFI, and '-1'
|
||||
* if there is no fan or if both AFI & AFO bits are set.
|
||||
*/
|
||||
value &= AFI_AFO_MASK;
|
||||
|
||||
switch(value) {
|
||||
case AFI_MASK:
|
||||
ret = 0;
|
||||
break;
|
||||
case AFO_MASK:
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum sysfs_fan_attributes {
|
||||
FAN0_PRESENT,
|
||||
FAN1_PRESENT,
|
||||
FAN2_PRESENT,
|
||||
FAN3_PRESENT,
|
||||
FAN4_PRESENT,
|
||||
};
|
||||
|
||||
enum sysfs_fan_type_attributes {
|
||||
FAN0_TYPE,
|
||||
FAN1_TYPE,
|
||||
FAN2_TYPE,
|
||||
FAN3_TYPE,
|
||||
FAN4_TYPE,
|
||||
};
|
||||
|
||||
/*
|
||||
* The sysfs files will be present in this path
|
||||
* /sys/devices/pci0000:00/0000:00:1c.0/0000:0f:00.0/refpga-tmc.15/fan*_present
|
||||
* /sys/devices/pci0000:00/0000:00:1c.0/0000:0f:00.0/refpga-tmc.15/fan*_type
|
||||
*/
|
||||
|
||||
#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, refpga_fan_presense_show, NULL, FAN##index##_PRESENT)
|
||||
#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr
|
||||
|
||||
#define DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(fan##index##_type, S_IRUGO, refpga_fan_type_show, NULL, FAN##index##_TYPE)
|
||||
#define DECLARE_FAN_TYPE_ATTR(index) &sensor_dev_attr_fan##index##_type.dev_attr.attr
|
||||
|
||||
static ssize_t refpga_fan_presense_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *s_attr = to_sensor_dev_attr(attr);
|
||||
|
||||
return sprintf(buf, "%d\n", get_fan_presense(s_attr->index));
|
||||
|
||||
}
|
||||
|
||||
static ssize_t refpga_fan_type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *s_attr = to_sensor_dev_attr(attr);
|
||||
|
||||
return sprintf(buf, "%d\n", get_fan_type(s_attr->index));
|
||||
|
||||
}
|
||||
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(0);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4);
|
||||
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(0);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(1);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(2);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(3);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(4);
|
||||
|
||||
static struct attribute *refpga_fan_attrs[] = {
|
||||
DECLARE_FAN_PRESENT_ATTR(0),
|
||||
DECLARE_FAN_PRESENT_ATTR(1),
|
||||
DECLARE_FAN_PRESENT_ATTR(2),
|
||||
DECLARE_FAN_PRESENT_ATTR(3),
|
||||
DECLARE_FAN_PRESENT_ATTR(4),
|
||||
DECLARE_FAN_TYPE_ATTR(0),
|
||||
DECLARE_FAN_TYPE_ATTR(1),
|
||||
DECLARE_FAN_TYPE_ATTR(2),
|
||||
DECLARE_FAN_TYPE_ATTR(3),
|
||||
DECLARE_FAN_TYPE_ATTR(4),
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group refpga_fan_attr_group = {
|
||||
.attrs = refpga_fan_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* There is only a single ALARM led in QFX5200 and that
|
||||
* is used for both Major & Minor alarm indicator.
|
||||
* These are represented by two different bits in RE-FPGA
|
||||
* PCIE_ALARM register. Only one of the bit (either Red or
|
||||
* Yellow) should be set a time. If both the bits are set,
|
||||
* it's an undefined behaviour.
|
||||
*
|
||||
* The following table describes how the conditions are
|
||||
* handled in the driver as there can be both Major & Minor
|
||||
* alarms can be triggered from userspace.
|
||||
*
|
||||
* Major Minor Colour
|
||||
*
|
||||
* 0 0 Nil
|
||||
* 0 1 Yellow
|
||||
* 1 1 Red
|
||||
* 1 0 Red
|
||||
*
|
||||
*/
|
||||
static void manage_alarm_led(void __iomem *addr, int led_type, int value)
|
||||
{
|
||||
static int alarm_major = 0, alarm_minor = 0;
|
||||
u32 reg = 0x0;
|
||||
|
||||
mutex_lock(&alarm_led_lock);
|
||||
reg = refpga_read(addr, REFPGA_PCIE_ALARM);
|
||||
|
||||
(led_type == ALARM_MAJOR_LED) ?
|
||||
((value == 1) ? (alarm_major = 1) : (alarm_major = 0)) :
|
||||
((value == 1) ? (alarm_minor = 1) : (alarm_minor = 0));
|
||||
if (alarm_major) {
|
||||
reg &= ~BIT(ALARM_MINOR_LED);
|
||||
reg |= BIT(ALARM_MAJOR_LED);
|
||||
} else {
|
||||
if (alarm_minor) {
|
||||
reg &= ~BIT(ALARM_MAJOR_LED);
|
||||
reg |= BIT(ALARM_MINOR_LED);
|
||||
} else {
|
||||
reg &= ~BIT(ALARM_MINOR_LED);
|
||||
reg &= ~BIT(ALARM_MAJOR_LED);
|
||||
}
|
||||
}
|
||||
refpga_write(addr, REFPGA_PCIE_ALARM, reg);
|
||||
mutex_unlock(&alarm_led_lock);
|
||||
}
|
||||
|
||||
static void manage_fan_led(void __iomem *addr, int fan_slot, int value)
|
||||
{
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT + (fan_slot * 2);
|
||||
u32 reg = 0x0;
|
||||
|
||||
reg = refpga_read(addr, offset);
|
||||
if(value) {
|
||||
/* Turn on s/w control */
|
||||
reg = reg | BIT(4);
|
||||
/* Turn off green led */
|
||||
reg &= ~BIT(5);
|
||||
/* Turn on yellow led & make it blink */
|
||||
reg |= (BIT(6) | BIT(7));
|
||||
} else {
|
||||
/* Clear yellow led & stop blink */
|
||||
reg &= ~(BIT(6) | BIT(7));
|
||||
/* Stop s/w control */
|
||||
reg &= ~BIT(4);
|
||||
}
|
||||
refpga_write(addr, offset, reg);
|
||||
}
|
||||
|
||||
static void refpga_led_work(struct work_struct *work)
|
||||
{
|
||||
struct refpga_led *led = container_of(work, struct refpga_led, work);
|
||||
void __iomem *addr;
|
||||
|
||||
addr = led->addr;
|
||||
|
||||
if(strstr(led->lc.name, "fan"))
|
||||
manage_fan_led(addr, led->bit, led->on);
|
||||
else
|
||||
manage_alarm_led(addr, led->bit, led->on);
|
||||
}
|
||||
|
||||
static void refpga_led_brightness_set(struct led_classdev *lc,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct refpga_led *led = container_of(lc, struct refpga_led, lc);
|
||||
|
||||
led->on = (brightness != LED_OFF);
|
||||
led->blink = 0; /* always turn off hw blink on brightness_set() */
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
struct led_table
|
||||
{
|
||||
const char *name;
|
||||
int reg;
|
||||
};
|
||||
|
||||
static struct led_table qfx5200_led_data[] = {
|
||||
{
|
||||
.name = "alarm-minor",
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "alarm-major",
|
||||
.reg = 1,
|
||||
},
|
||||
{
|
||||
.name = "fan0-fault",
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "fan1-fault",
|
||||
.reg = 1,
|
||||
},
|
||||
{
|
||||
.name = "fan2-fault",
|
||||
.reg = 2,
|
||||
},
|
||||
{
|
||||
.name = "fan3-fault",
|
||||
.reg = 3,
|
||||
},
|
||||
{
|
||||
.name = "fan4-fault",
|
||||
.reg = 4,
|
||||
}
|
||||
};
|
||||
|
||||
static int refpga_led_init_one(struct device *dev,
|
||||
struct refpga_led_data *ild,
|
||||
int num)
|
||||
{
|
||||
struct refpga_led *led;
|
||||
int ret = 0;
|
||||
|
||||
led = &ild->leds[num];
|
||||
led->addr = tmc_membase;
|
||||
|
||||
led->lc.name = qfx5200_led_data[num].name;
|
||||
led->bit = qfx5200_led_data[num].reg;
|
||||
led->lc.brightness = LED_OFF;
|
||||
led->lc.brightness_set = refpga_led_brightness_set;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &led->lc);
|
||||
if (ret) {
|
||||
dev_err(dev, "devm_led_classdev_register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_WORK(&led->work, refpga_led_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refpga_led_qfx5200_init(struct device *dev, struct refpga_led_data *ild)
|
||||
{
|
||||
int ret = 0, idx = 0;
|
||||
|
||||
|
||||
if (!dev->parent) {
|
||||
dev_err(dev, "dev->parent is null\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ild->num_leds = NUM_LEDS;
|
||||
ild->leds = devm_kzalloc(dev, sizeof(struct refpga_led) * NUM_LEDS,
|
||||
GFP_KERNEL);
|
||||
if (!ild->leds) {
|
||||
dev_err(dev, "LED allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for(idx=0; idx<NUM_LEDS; idx++){
|
||||
ret = refpga_led_init_one(dev, ild, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct refpga_led_data *ild;
|
||||
int ret;
|
||||
|
||||
ild = devm_kzalloc(dev, sizeof(*ild), GFP_KERNEL);
|
||||
if (!ild) {
|
||||
dev_err(dev, "ild allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = refpga_led_qfx5200_init(dev, ild);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
refpga->led = ild;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct refpga_chip *drv_data = platform_get_drvdata(pdev);
|
||||
struct refpga_led_data *ild = drv_data->led;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ild->num_leds; i++) {
|
||||
devm_led_classdev_unregister(&pdev->dev, &ild->leds[i].lc);
|
||||
cancel_work_sync(&ild->leds[i].work);
|
||||
}
|
||||
if (ild) {
|
||||
if (ild->leds)
|
||||
devm_kfree(&pdev->dev, ild->leds);
|
||||
devm_kfree(&pdev->dev, ild);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_fan_full_speed(struct device *dev)
|
||||
{
|
||||
u32 val = ~(-1), tmp = ~(-1);
|
||||
|
||||
/*
|
||||
* Reading the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
val = refpga_read(tmc_membase, REFPGA_PCIE_RESET_CTRL);
|
||||
/*
|
||||
* Clearing the fan full_speed bit
|
||||
*/
|
||||
val &= ~(REFPGA_RESET_FAN_SPEED);
|
||||
/*
|
||||
* Writing the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
refpga_write(tmc_membase, REFPGA_PCIE_RESET_CTRL, val);
|
||||
/*
|
||||
* Reading the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
tmp = refpga_read(tmc_membase, REFPGA_PCIE_RESET_CTRL);
|
||||
dev_info(dev, "After resetting fan full speed control: %X\n", tmp);
|
||||
}
|
||||
|
||||
static int jnx_refpga_tmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "resource allocation failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tmc_membase = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!tmc_membase) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
refpga = devm_kzalloc(dev, sizeof(*refpga), GFP_KERNEL);
|
||||
if (!refpga) {
|
||||
dev_err(dev, "refpga memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
reset_fan_full_speed(dev);
|
||||
|
||||
ret = jnx_refpga_led_probe(pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Refpga LED probe failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "Refpga LED probe successful: TMC memoy base: %p\n",
|
||||
tmc_membase);
|
||||
|
||||
ret = sysfs_create_group(&dev->kobj, &refpga_fan_attr_group);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "sysfs_create_group failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, refpga);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_tmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
jnx_refpga_led_remove(pdev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &refpga_fan_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver jnx_refpga_tmc_driver = {
|
||||
.driver = {
|
||||
.name = "refpga-tmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = jnx_refpga_tmc_probe,
|
||||
.remove = jnx_refpga_tmc_remove,
|
||||
};
|
||||
|
||||
static int __init jnx_refpga_tmc_driver_init(void)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
ret = platform_driver_register(&jnx_refpga_tmc_driver);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void __exit jnx_refpga_tmc_driver_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&jnx_refpga_tmc_driver);
|
||||
}
|
||||
|
||||
module_init(jnx_refpga_tmc_driver_init);
|
||||
module_exit(jnx_refpga_tmc_driver_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks REFPGA / TMC driver");
|
||||
MODULE_AUTHOR("Ciju Rajan K <crajank@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Juniper Networks TMC-FPGA MFD Core driver for qfx platform
|
||||
*
|
||||
* Copyright (c) 2020, Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This driver implement the resource publish for below devices
|
||||
* - I2C
|
||||
* - GPIO
|
||||
* - RE FPGA
|
||||
* - PSU
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include "jnx-tmc.h"
|
||||
|
||||
#define TMC_DO_SCRATCH_TEST 1
|
||||
|
||||
/*
|
||||
* TMC FPGA Device IDs
|
||||
*/
|
||||
#define PCI_VENDOR_ID_JUNIPER 0x1304
|
||||
|
||||
#define PCI_DEVICE_ID_JNX_TMC_CHD 0x007B
|
||||
#define PCI_DEVICE_ID_JNX_TMC_PFE 0x007C
|
||||
|
||||
/*
|
||||
* TMC resources
|
||||
*/
|
||||
static struct resource tmc_resource_i2c[] = {
|
||||
/* I2C AUTOMATION Block */
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.start = TMC_I2C_AUTOMATION_I2C_CONTROL_START,
|
||||
.end = TMC_I2C_AUTOMATION_I2C_CONTROL_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
|
||||
/* I2C DPMEM */
|
||||
{
|
||||
.name = "i2c-tmc-mem",
|
||||
.start = TMC_I2C_DPMEM_ENTRY_START,
|
||||
.end = TMC_I2C_DPMEM_ENTRY_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_I2C_NR ARRAY_SIZE(tmc_resource_i2c)
|
||||
|
||||
/*
|
||||
* LED resources
|
||||
*/
|
||||
static struct resource tmc_resource_leds[] = {
|
||||
{
|
||||
.name = "leds-tmc",
|
||||
.start = TMC_LED_CONTROL_START,
|
||||
.end = TMC_LED_CONTROL_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_LEDS_NR ARRAY_SIZE(tmc_resource_leds)
|
||||
|
||||
/*
|
||||
* TMC RE-FPGA devices
|
||||
*/
|
||||
static struct resource tmc_resource_refpga[] = {
|
||||
{
|
||||
.name = "refpga-tmc",
|
||||
.start = TMC_REFPGA_ACCESS_START,
|
||||
.end = TMC_REFPGA_ACCESS_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_REFPGA_NR ARRAY_SIZE(tmc_resource_refpga)
|
||||
|
||||
static struct resource tmc_resource_gpioslave0[] = {
|
||||
/* SLAVE0 Block */
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.start = TMC_GPIO_SLAVE0_START,
|
||||
.end = TMC_GPIO_SLAVE0_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_GPIOSLAVE0_NR ARRAY_SIZE(tmc_resource_gpioslave0)
|
||||
|
||||
static struct resource tmc_resource_gpioslave1[] = {
|
||||
/* SLAVE1 Block */
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.start = TMC_GPIO_SLAVE1_START,
|
||||
.end = TMC_GPIO_SLAVE1_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_GPIOSLAVE1_NR ARRAY_SIZE(tmc_resource_gpioslave1)
|
||||
|
||||
static struct resource tmc_resource_psu[] = {
|
||||
/* PSU Block */
|
||||
{
|
||||
.name = "psu-tmc",
|
||||
.start = TMC_PSU_START,
|
||||
.end = TMC_PSU_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_PSU_NR ARRAY_SIZE(tmc_resource_psu)
|
||||
|
||||
/*
|
||||
* CHASSISD TMC MFD devices
|
||||
*/
|
||||
static struct mfd_cell chassisd_tmc_mfd_devs[] = {
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_i2c),
|
||||
.resources = &tmc_resource_i2c[0],
|
||||
.of_compatible = "jnx,i2c-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "leds-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_leds),
|
||||
.resources = &tmc_resource_leds[0],
|
||||
.of_compatible = "jnx,leds-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "refpga-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_refpga),
|
||||
.resources = &tmc_resource_refpga[0],
|
||||
.of_compatible = "jnx,refpga-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "psu-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_psu),
|
||||
.resources = &tmc_resource_psu[0],
|
||||
.of_compatible = "jnx,psu-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* PFE TMC MFD devices
|
||||
*/
|
||||
static struct mfd_cell pfe_tmc_mfd_devs[] = {
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_i2c),
|
||||
.resources = &tmc_resource_i2c[0],
|
||||
.of_compatible = "jnx,i2c-tmc",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_gpioslave0),
|
||||
.resources = &tmc_resource_gpioslave0[0],
|
||||
.of_compatible = "jnx,gpioslave-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_gpioslave1),
|
||||
.resources = &tmc_resource_gpioslave1[0],
|
||||
.of_compatible = "jnx,gpioslave-tmc",
|
||||
.id = 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
struct tmc_fpga_data {
|
||||
void __iomem *membase;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
u32 major; /* Device id & Major version*/
|
||||
u32 minor; /* Minor version */
|
||||
|
||||
u32 optic_cpld_major; /* optic cpld major version */
|
||||
u32 optic_cpld_minor; /* optic cpld minor version */
|
||||
u32 optic_cpld_devid; /* optic cpld device id */
|
||||
};
|
||||
|
||||
/* sysfs entries */
|
||||
static ssize_t major_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "0x%02X_%06X\n",
|
||||
(tmc->major >> 24) & 0xff,
|
||||
tmc->major & 0xffffff);
|
||||
}
|
||||
|
||||
static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%02X\n", (tmc->minor) & 0xff);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_major_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%01X\n", tmc->optic_cpld_major & 0xf);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_devid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%01X\n",
|
||||
(tmc->optic_cpld_major >> 4) & 0xf);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_minor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%02X\n", tmc->optic_cpld_minor & 0xff);
|
||||
}
|
||||
|
||||
static ssize_t set_sys_shutdown(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Unlock the shutdown register */
|
||||
iowrite32(0x12345678, tmc->membase + TMC_SYS_SHUTDOWN_LOCK);
|
||||
iowrite32(0x1, tmc->membase + TMC_SYS_SHUTDOWN);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR(major, S_IRUGO, major_show, NULL);
|
||||
static DEVICE_ATTR(minor, S_IRUGO, minor_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_major, S_IRUGO, optic_cpld_major_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_devid, S_IRUGO, optic_cpld_devid_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_minor, S_IRUGO, optic_cpld_minor_show, NULL);
|
||||
static DEVICE_ATTR(shutdown, S_IWUSR, NULL, set_sys_shutdown);
|
||||
|
||||
static struct attribute *tmc_attrs[] = {
|
||||
&dev_attr_major.attr,
|
||||
&dev_attr_minor.attr,
|
||||
&dev_attr_optic_cpld_major.attr,
|
||||
&dev_attr_optic_cpld_devid.attr,
|
||||
&dev_attr_optic_cpld_minor.attr,
|
||||
&dev_attr_shutdown.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group tmc_attr_group = {
|
||||
.attrs = tmc_attrs,
|
||||
};
|
||||
|
||||
#if defined TMC_DO_SCRATCH_TEST
|
||||
/* Do a quick scratch access test */
|
||||
static int tmc_do_test_scratch(struct tmc_fpga_data *tmc)
|
||||
{
|
||||
struct pci_dev *pdev = tmc->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int offset = TMC_SCRATCH;
|
||||
u32 acc, val = 0xdeadbeaf;
|
||||
|
||||
/*
|
||||
* Check rw register access -> use the scratch reg.
|
||||
*/
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
acc = ioread32(tmc->membase + offset);
|
||||
if (acc != val) {
|
||||
dev_err(dev, "Tmc scratch(0x%x) failed: %08x.%08x!\n",
|
||||
offset, val, acc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (val = 0; val < 0xf0000000; val += 0x01010101) {
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
acc = ioread32(tmc->membase + offset);
|
||||
if (acc != val) {
|
||||
dev_err(dev, "Tmc scratch(0x%x) failed: %08x.%08x!\n",
|
||||
offset, val, acc);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a sig before leaving..
|
||||
*/
|
||||
val = 0xcafebabe;
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
dev_dbg(dev, "Tmc scratch result: 0x%08x\n",
|
||||
ioread32(tmc->membase + offset));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TMC_DO_SCRATCH_TEST */
|
||||
|
||||
static int tmc_fpga_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int err;
|
||||
struct tmc_fpga_data *tmc;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
dev_dbg(dev, "Tmc FPGA Probe called\n");
|
||||
|
||||
tmc = devm_kzalloc(dev, sizeof(*tmc), GFP_KERNEL);
|
||||
if (!tmc)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to enable device %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pcim_iomap_regions(pdev, 1 << 0, "tmc-core");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to iomap regions %d\n", err);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
tmc->membase = pcim_iomap_table(pdev)[0];
|
||||
if (IS_ERR(tmc->membase)) {
|
||||
dev_err(dev, "pci_ioremap_bar() failed\n");
|
||||
err = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
tmc->pdev = pdev;
|
||||
pci_set_drvdata(pdev, tmc);
|
||||
|
||||
/* All Tmc uses MSI interrupts - enable bus mastering */
|
||||
pci_set_master(pdev);
|
||||
|
||||
#if defined TMC_DO_SCRATCH_TEST
|
||||
/* Check IO before proceeding */
|
||||
dev_dbg(dev, "Tmc FPGA starting scratch test\n");
|
||||
err = tmc_do_test_scratch(tmc);
|
||||
if (err)
|
||||
goto err_unmap;
|
||||
|
||||
dev_dbg(dev, "Tmc FPGA scratch test passed !!!\n");
|
||||
#endif /* TMC_DO_SCRATCH_TEST */
|
||||
|
||||
switch (id->device) {
|
||||
case PCI_DEVICE_ID_JNX_TMC_CHD:
|
||||
err = mfd_add_devices(dev, pdev->bus->number,
|
||||
&chassisd_tmc_mfd_devs[0],
|
||||
ARRAY_SIZE(chassisd_tmc_mfd_devs),
|
||||
&pdev->resource[0],
|
||||
0, NULL /* tmc->irq_domain */);
|
||||
break;
|
||||
case PCI_DEVICE_ID_JNX_TMC_PFE:
|
||||
err = mfd_add_devices(dev, pdev->bus->number,
|
||||
&pfe_tmc_mfd_devs[0],
|
||||
ARRAY_SIZE(pfe_tmc_mfd_devs),
|
||||
&pdev->resource[0],
|
||||
0, NULL /* tmc->irq_domain */);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid PCI Device ID id:%d\n",
|
||||
id->device);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Failed to add mfd devices %d\n", err);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
if (err) {
|
||||
sysfs_remove_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
dev_err(&pdev->dev, "Failed to create attr group\n");
|
||||
goto err_remove_mfd;
|
||||
}
|
||||
|
||||
tmc->major = ioread32(tmc->membase + TMC_REVISION);
|
||||
tmc->minor = ioread32(tmc->membase + TMC_MINOR);
|
||||
|
||||
tmc->optic_cpld_major = ioread32(tmc->membase + TMC_OPTIC_CPLD_MAJOR);
|
||||
tmc->optic_cpld_minor = ioread32(tmc->membase + TMC_OPTIC_CPLD_MINOR);
|
||||
|
||||
dev_info(dev, "Tmc FPGA Revision: 0x%02X_%06X, Minor: %02X\n",
|
||||
(tmc->major >> 24) & 0xff,
|
||||
tmc->major & 0xffffff,
|
||||
(tmc->minor) & 0xff);
|
||||
dev_info(dev, "Tmc FPGA optic cpld Major: 0x%01X, Minor: 0x%02X "
|
||||
"Devid: 0x%01X\n", (tmc->optic_cpld_major) & 0xf,
|
||||
(tmc->optic_cpld_minor) & 0xff,
|
||||
(tmc->optic_cpld_major >> 4) & 0xf);
|
||||
dev_info(dev, "Tmc FPGA mem:0x%lx\n",
|
||||
(unsigned long)tmc->membase);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_mfd:
|
||||
mfd_remove_devices(dev);
|
||||
err_unmap:
|
||||
pci_iounmap(pdev, tmc->membase);
|
||||
err_release:
|
||||
pci_release_regions(pdev);
|
||||
err_disable:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tmc_fpga_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id tmc_fpga_id_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TMC_CHD) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TMC_PFE) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, tmc_fpga_id_tbl);
|
||||
|
||||
static struct pci_driver tmc_fpga_driver = {
|
||||
.name = "tmc-core",
|
||||
.id_table = tmc_fpga_id_tbl,
|
||||
.probe = tmc_fpga_probe,
|
||||
.remove = tmc_fpga_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(tmc_fpga_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks TMC FPGA MFD core driver");
|
||||
MODULE_AUTHOR("Ashish Bhensdadia <bashish@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Juniper Tmc FPGA register definitions
|
||||
*
|
||||
* Copyright (C) 2018 Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __JNX_TMC_H__
|
||||
#define __JNX_TMC_H__
|
||||
|
||||
|
||||
#define TMC_REVISION 0x00064
|
||||
#define TMC_MINOR 0x00068
|
||||
#define TMC_SCRATCH 0x00098
|
||||
|
||||
#define TMC_OPTIC_CPLD_MAJOR 0x00104
|
||||
#define TMC_OPTIC_CPLD_MINOR 0x00108
|
||||
|
||||
/*
|
||||
* I2C Master Block
|
||||
*/
|
||||
#define TMC_I2C_AUTOMATION_I2C_CONTROL_START 0x07000
|
||||
#define TMC_I2C_AUTOMATION_I2C_CONTROL_END 0x07500
|
||||
|
||||
#define TMC_I2C_DPMEM_ENTRY_START 0x10000
|
||||
#define TMC_I2C_DPMEM_ENTRY_END 0x13FFC
|
||||
|
||||
#define TMC_LED_CONTROL_START 0x58
|
||||
#define TMC_LED_CONTROL_END 0x5B
|
||||
|
||||
/*
|
||||
* RE-FPGA block
|
||||
*/
|
||||
#define TMC_REFPGA_ACCESS_START 0x228
|
||||
#define TMC_REFPGA_ACCESS_END 0x233
|
||||
|
||||
#define TMC_I2C_MASTER_NR_MSTRS 16
|
||||
#define TMC_I2C_MSTR_MAX_GROUPS 66
|
||||
|
||||
|
||||
/*
|
||||
* TMC GPIO SLAVE Block
|
||||
*/
|
||||
#define TMC_GPIO_PTP_RESET_START 0x94
|
||||
#define TMC_GPIO_PTP_RESET_END 0x97
|
||||
|
||||
#define TMC_GPIO_PTP_CFG_START 0xa4
|
||||
#define TMC_GPIO_PTP_CFG_END 0xa7
|
||||
|
||||
#define TMC_GPIO_PTP_DATA_START 0xa8
|
||||
#define TMC_GPIO_PTP_DATA_END 0xab
|
||||
|
||||
#define TMC_GPIO_SLAVE0_START 0xf0
|
||||
#define TMC_GPIO_SLAVE0_END 0x16b
|
||||
|
||||
#define TMC_GPIO_SLAVE1_START 0x170
|
||||
#define TMC_GPIO_SLAVE1_END 0x1eb
|
||||
|
||||
#define TMC_GPIO_SLAVE2_START 0x1f0
|
||||
#define TMC_GPIO_SLAVE2_END 0x213
|
||||
|
||||
#define TMC_GPIO_SLAVE3_START 0x280
|
||||
#define TMC_GPIO_SLAVE3_END 0x2eb
|
||||
|
||||
#define TMC_GPIO_SFP_SLAVE0_START 0x308
|
||||
#define TMC_GPIO_SFP_SLAVE0_END 0x32b
|
||||
|
||||
#define TMC_GPIO_SFP_SLAVE1_START 0x32c
|
||||
#define TMC_GPIO_SFP_SLAVE1_END 0x34b
|
||||
|
||||
/*
|
||||
* TMC PSU Block
|
||||
*/
|
||||
#define TMC_PSU_START 0x240
|
||||
#define TMC_PSU_END 0x243
|
||||
|
||||
/*
|
||||
* TMC SHUTDOWN REG
|
||||
*/
|
||||
#define TMC_SYS_SHUTDOWN_LOCK 0x254
|
||||
#define TMC_SYS_SHUTDOWN 0x250
|
||||
|
||||
/*
|
||||
* TMC DS100 MUX Block
|
||||
*/
|
||||
#define TMC_GPIO_MUX_SLAVE_START 0x26c
|
||||
#define TMC_GPIO_MUX_SLAVE_END 0x26f
|
||||
|
||||
#endif /* __JNX_TMC_H__ */
|
@ -1,892 +0,0 @@
|
||||
/*
|
||||
* A hwmon driver for the juniper_i2c_cpld
|
||||
*
|
||||
* Tested and validated on Juniper QFX5210
|
||||
* Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* Copyright (C) 2013 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
|
||||
#define MAX_PORT_NUM 64
|
||||
#define I2C_RW_RETRY_COUNT 10
|
||||
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
|
||||
|
||||
#define I2C_ADDR_CPLD1 0x60
|
||||
#define I2C_ADDR_CPLD2 0x62
|
||||
#define I2C_ADDR_CPLD3 0x64
|
||||
#define CPLD_ADDRS {I2C_ADDR_CPLD1, I2C_ADDR_CPLD2, I2C_ADDR_CPLD3}
|
||||
|
||||
|
||||
/*
|
||||
* Number of additional attribute pointers to allocate
|
||||
* with each call to krealloc
|
||||
*/
|
||||
#define ATTR_ALLOC_SIZE 1 /*For last attribute which is NUll.*/
|
||||
|
||||
#define NAME_SIZE 24
|
||||
#define MAX_RESP_LENGTH 48
|
||||
|
||||
typedef ssize_t (*show_func)( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
typedef ssize_t (*store_func)(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
enum models {
|
||||
AS7712_32X,
|
||||
AS7716_32X,
|
||||
qfx5210_64X,
|
||||
AS7312_54X,
|
||||
PLAIN_CPLD, /*No attribute but add i2c addr to the list.*/
|
||||
NUM_MODEL
|
||||
};
|
||||
|
||||
enum sfp_func {
|
||||
HAS_SFP = 1<<0 ,
|
||||
HAS_QSFP = 1<<1 ,
|
||||
};
|
||||
|
||||
enum common_attrs {
|
||||
CMN_VERSION,
|
||||
CMN_ACCESS,
|
||||
CMN_PRESENT_ALL,
|
||||
NUM_COMMON_ATTR
|
||||
};
|
||||
|
||||
enum sfp_attrs {
|
||||
SFP_PRESENT,
|
||||
SFP_RESET,
|
||||
SFP_LP_MODE,
|
||||
NUM_SFP_ATTR
|
||||
};
|
||||
|
||||
struct cpld_sensor {
|
||||
struct cpld_sensor *next;
|
||||
char name[NAME_SIZE+1]; /* sysfs sensor name */
|
||||
struct device_attribute attribute;
|
||||
bool update; /* runtime sensor update needed */
|
||||
int data; /* Sensor data. Negative if there was a read error */
|
||||
|
||||
u8 reg; /* register */
|
||||
u8 mask; /* bit mask */
|
||||
bool invert; /* inverted value*/
|
||||
|
||||
};
|
||||
|
||||
#define to_cpld_sensor(_attr) \
|
||||
container_of(_attr, struct cpld_sensor, attribute)
|
||||
|
||||
struct cpld_data {
|
||||
struct device *dev;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
int num_attributes;
|
||||
struct attribute_group group;
|
||||
|
||||
enum models model;
|
||||
struct cpld_sensor *sensors;
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
int attr_index;
|
||||
u16 sfp_num;
|
||||
u8 sfp_types;
|
||||
struct model_attrs *cmn_attr;
|
||||
};
|
||||
|
||||
struct cpld_client_node {
|
||||
struct i2c_client *client;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
||||
struct base_attrs {
|
||||
const char *name;
|
||||
umode_t mode;
|
||||
show_func get;
|
||||
store_func set;
|
||||
};
|
||||
|
||||
struct attrs {
|
||||
int reg;
|
||||
bool invert;
|
||||
struct base_attrs *base;
|
||||
};
|
||||
|
||||
struct model_attrs {
|
||||
struct attrs **cmn;
|
||||
struct attrs **portly;
|
||||
};
|
||||
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf);
|
||||
static ssize_t show_presnet_all(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf);
|
||||
static ssize_t set_1bit(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t set_byte(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
|
||||
int juniper_i2c_cpld_read(u8 cpld_addr, u8 reg);
|
||||
int juniper_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
|
||||
struct base_attrs common_attrs[NUM_COMMON_ATTR] =
|
||||
{
|
||||
[CMN_VERSION] = {"version", S_IRUGO, show_bit, NULL},
|
||||
[CMN_ACCESS] = {"access", S_IWUSR, NULL, set_byte},
|
||||
[CMN_PRESENT_ALL] = {"module_present_all", S_IRUGO, show_presnet_all, NULL},
|
||||
};
|
||||
|
||||
struct attrs as7712_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs qfx5210_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs as7312_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {-1, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs plain_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
};
|
||||
|
||||
struct base_attrs portly_attrs[] =
|
||||
{
|
||||
[SFP_PRESENT] = {"module_present", S_IRUGO, show_bit, NULL},
|
||||
// Only root user will have the privilege to write to sysfs
|
||||
// [SFP_RESET] = {"module_reset", S_IRUGO|S_IWUGO, show_bit, set_1bit},
|
||||
[SFP_RESET] = {"module_reset", S_IRUGO|S_IWUSR, show_bit, set_1bit},
|
||||
};
|
||||
|
||||
struct attrs as7712_port[] = {
|
||||
{0x30, true, &portly_attrs[SFP_PRESENT]},
|
||||
{0x04, true, &portly_attrs[SFP_RESET]},
|
||||
};
|
||||
|
||||
struct attrs qfx5210_port[] = {
|
||||
{0x70, true, &portly_attrs[SFP_PRESENT]},
|
||||
{0x40, true, &portly_attrs[SFP_RESET]},
|
||||
};
|
||||
|
||||
struct attrs *as7712_cmn_list[] = {
|
||||
&as7712_common[CMN_VERSION],
|
||||
&as7712_common[CMN_ACCESS],
|
||||
&as7712_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *qfx5210_cmn_list[] = {
|
||||
&qfx5210_common[CMN_VERSION],
|
||||
&qfx5210_common[CMN_ACCESS],
|
||||
&qfx5210_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *as7312_cmn_list[] = {
|
||||
&as7312_common[CMN_VERSION],
|
||||
&as7312_common[CMN_ACCESS],
|
||||
&as7312_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *plain_cmn_list[] = {
|
||||
&plain_common[CMN_VERSION],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *as7712_port_list[] = {
|
||||
&as7712_port[SFP_PRESENT],
|
||||
&as7712_port[SFP_RESET],
|
||||
NULL
|
||||
};
|
||||
struct attrs *qfx5210_port_list[] = {
|
||||
&qfx5210_port[SFP_PRESENT],
|
||||
&qfx5210_port[SFP_RESET],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct model_attrs models_attr[NUM_MODEL] = {
|
||||
{.cmn = as7712_cmn_list, .portly=as7712_port_list},
|
||||
{.cmn = as7712_cmn_list, .portly=as7712_port_list}, /*7716's as 7712*/
|
||||
{.cmn = qfx5210_cmn_list, .portly=qfx5210_port_list},
|
||||
{.cmn = as7312_cmn_list, .portly=qfx5210_port_list},
|
||||
{.cmn = plain_cmn_list, .portly=NULL},
|
||||
};
|
||||
|
||||
static LIST_HEAD(cpld_client_list);
|
||||
static struct mutex list_lock;
|
||||
/* Addresses scanned for juniper_i2c_cpld
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||
|
||||
static int get_sfp_spec(int model, u16 *num, u8 *types)
|
||||
{
|
||||
switch (model) {
|
||||
case AS7712_32X:
|
||||
case AS7716_32X:
|
||||
*num = 32;
|
||||
*types = HAS_QSFP;
|
||||
break;
|
||||
case qfx5210_64X:
|
||||
*num = 64;
|
||||
*types = HAS_QSFP;
|
||||
break;
|
||||
case AS7312_54X:
|
||||
*num = 54;
|
||||
*types = HAS_QSFP|HAS_SFP;
|
||||
default:
|
||||
*types = 0;
|
||||
*num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_present_reg(int model, u8 port, u8 *cpld_addr, u8 *reg, u8 *num)
|
||||
{
|
||||
u8 cpld_address[] = CPLD_ADDRS;
|
||||
|
||||
switch (model) {
|
||||
case AS7312_54X:
|
||||
if (port < 48) {
|
||||
*cpld_addr = cpld_address[1 + port/24];
|
||||
*reg = 0x09 + (port%24)/8;
|
||||
*num = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
*reg = 0x18;
|
||||
*num = 4;
|
||||
*cpld_addr = ( port < 52)? cpld_address[1]: cpld_address[2];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*Assume the bits for ports are listed in-a-row.*/
|
||||
static int get_reg_bit(u8 reg_start, int port,
|
||||
u8 *reg ,u8 *mask)
|
||||
{
|
||||
*reg = reg_start + ((port)/8);
|
||||
*mask = 1 << ((port)%8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpld_write_internal(
|
||||
struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_write_byte_data(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int cpld_read_internal(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_read_byte_data(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*Turn a numberic array into string with " " between each element.
|
||||
* e.g., {0x11, 0x33, 0xff, 0xf1} => "11 33 ff f1"
|
||||
*/
|
||||
static ssize_t array_stringify(char *buf, u8 *input, size_t size) {
|
||||
|
||||
int i;
|
||||
char t[MAX_RESP_LENGTH+1];
|
||||
|
||||
buf[0] = '\0';
|
||||
for (i = 0; i < size; i++) {
|
||||
snprintf(t, MAX_RESP_LENGTH, "%x ", input[i]);
|
||||
strncat(buf, t, MAX_RESP_LENGTH);
|
||||
}
|
||||
|
||||
if (strlen(buf) > 0)
|
||||
buf[strlen(buf)-1] = '\0'; /*Remove tailing blank*/
|
||||
|
||||
return snprintf(buf, MAX_RESP_LENGTH, "%s\n", buf);
|
||||
}
|
||||
|
||||
static ssize_t show_presnet_all_distinct(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
u8 i, value, reg;
|
||||
u8 cpld_addr, num;
|
||||
u8 _value[8];
|
||||
u64 *values = (u64 *)_value;
|
||||
|
||||
values = 0;
|
||||
mutex_lock(&data->update_lock);
|
||||
while(i < data->sfp_num)
|
||||
{
|
||||
get_present_reg(data->model, i, &cpld_addr, ®, &num);
|
||||
if(cpld_addr == client->addr)
|
||||
value = cpld_read_internal(client, reg);
|
||||
else
|
||||
value = juniper_i2c_cpld_read(cpld_addr, reg);
|
||||
|
||||
if (unlikely(value < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*values |= (value&((1<<(num))-1)) << i;
|
||||
i += num;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
*values = cpu_to_le64(*values);
|
||||
return array_stringify(buf, _value, i);
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return value;
|
||||
}
|
||||
|
||||
static ssize_t show_presnet_all(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
u8 i, values[MAX_RESP_LENGTH/8];
|
||||
|
||||
if (sensor->reg < 0) {
|
||||
return show_presnet_all_distinct(dev, devattr, buf);
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
for (i = 0; i < ((data->sfp_num+7)/8); i++) {
|
||||
values[i] = cpld_read_internal(client, sensor->reg + i);
|
||||
if (unlikely(values[i] < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return array_stringify(buf, values, i);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return values[i];
|
||||
}
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int value;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
value = cpld_read_internal(client, sensor->reg);
|
||||
value = value & sensor->mask;
|
||||
if (sensor->invert)
|
||||
value = !value;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", value);
|
||||
}
|
||||
|
||||
static ssize_t set_1bit(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
long is_reset;
|
||||
int value, status;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
u8 cpld_bit, reg;
|
||||
|
||||
status = kstrtol(buf, 10, &is_reset);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
reg = sensor->reg;
|
||||
cpld_bit = sensor->mask;
|
||||
mutex_lock(&data->update_lock);
|
||||
value = cpld_read_internal(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (sensor->invert)
|
||||
is_reset = !is_reset;
|
||||
|
||||
if (is_reset) {
|
||||
value |= cpld_bit;
|
||||
}
|
||||
else {
|
||||
value &= ~cpld_bit;
|
||||
}
|
||||
|
||||
status = cpld_write_internal(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t set_byte(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return access(dev, da, buf, count);
|
||||
}
|
||||
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int status;
|
||||
u32 addr, val;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr > 0xFF || val > 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = cpld_write_internal(client, addr, val);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void juniper_i2c_cpld_add_client(struct i2c_client *client)
|
||||
{
|
||||
struct cpld_client_node *node =
|
||||
kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
|
||||
|
||||
if (!node) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n",
|
||||
client->addr);
|
||||
return;
|
||||
}
|
||||
node->client = client;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_add(&node->list, &cpld_client_list);
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static void juniper_i2c_cpld_remove_client(struct i2c_client *client)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client == client) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_del(list_node);
|
||||
kfree(cpld_node);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static int cpld_add_attribute(struct cpld_data *data, struct attribute *attr)
|
||||
{
|
||||
int new_max_attrs = ++data->num_attributes + ATTR_ALLOC_SIZE;
|
||||
void *new_attrs = krealloc(data->group.attrs,
|
||||
new_max_attrs * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!new_attrs)
|
||||
return -ENOMEM;
|
||||
data->group.attrs = new_attrs;
|
||||
|
||||
|
||||
data->group.attrs[data->num_attributes-1] = attr;
|
||||
data->group.attrs[data->num_attributes] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpld_dev_attr_init(struct device_attribute *dev_attr,
|
||||
const char *name, umode_t mode,
|
||||
show_func show, store_func store)
|
||||
{
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
dev_attr->attr.name = name;
|
||||
dev_attr->attr.mode = mode;
|
||||
dev_attr->show = show;
|
||||
dev_attr->store = store;
|
||||
}
|
||||
|
||||
static struct cpld_sensor * add_sensor(struct cpld_data *data,
|
||||
const char *name,
|
||||
u8 reg, u8 mask, bool invert,
|
||||
bool update, umode_t mode,
|
||||
show_func get, store_func set)
|
||||
{
|
||||
struct cpld_sensor *sensor;
|
||||
struct device_attribute *a;
|
||||
|
||||
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor)
|
||||
return NULL;
|
||||
a = &sensor->attribute;
|
||||
|
||||
snprintf(sensor->name, sizeof(sensor->name), name);
|
||||
sensor->reg = reg;
|
||||
sensor->mask = mask;
|
||||
sensor->update = update;
|
||||
sensor->invert = invert;
|
||||
cpld_dev_attr_init(a, sensor->name,
|
||||
mode,
|
||||
get, set);
|
||||
|
||||
if (cpld_add_attribute(data, &a->attr))
|
||||
return NULL;
|
||||
|
||||
sensor->next = data->sensors;
|
||||
data->sensors = sensor;
|
||||
|
||||
return sensor;
|
||||
}
|
||||
|
||||
static int add_attributes_cmn(struct cpld_data *data, struct attrs **cmn)
|
||||
{
|
||||
u8 reg, i ;
|
||||
bool invert;
|
||||
struct attrs *a;
|
||||
struct base_attrs *b;
|
||||
|
||||
if (NULL == cmn)
|
||||
return -1;
|
||||
|
||||
for (i = 0; cmn[i]; i++)
|
||||
{
|
||||
a = cmn[i];
|
||||
|
||||
reg = a->reg;
|
||||
invert = a->invert;
|
||||
|
||||
b = a->base;
|
||||
if (NULL == b)
|
||||
break;
|
||||
|
||||
if (add_sensor(data, b->name,
|
||||
reg, 0xff, invert,
|
||||
true, b->mode,
|
||||
b->get, b->set) == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_attributes_portly(struct cpld_data *data, struct attrs **pa)
|
||||
{
|
||||
char name[NAME_SIZE+1];
|
||||
int i, j;
|
||||
u8 reg, mask, invert;
|
||||
struct attrs *a;
|
||||
struct base_attrs *b;
|
||||
|
||||
if (NULL == pa)
|
||||
return -1;
|
||||
|
||||
|
||||
for (i = 0; pa[i]; i++) {
|
||||
a = pa[i];
|
||||
|
||||
invert = a->invert;
|
||||
b = a->base;
|
||||
if (b == NULL)
|
||||
break;
|
||||
|
||||
for (j = 0; j < data->sfp_num; j++)
|
||||
{
|
||||
snprintf(name, NAME_SIZE, "%s_%d", b->name, j+1);
|
||||
get_reg_bit(a->reg, j, ®, &mask);
|
||||
|
||||
if (add_sensor(data, name, reg, mask, invert,
|
||||
true, b->mode, b->get, b->set) == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_attributes(struct i2c_client *client,
|
||||
struct cpld_data *data)
|
||||
{
|
||||
struct model_attrs *m = data->cmn_attr;
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Common attributes.*/
|
||||
add_attributes_cmn(data, m->cmn);
|
||||
|
||||
/* Port-wise attributes.*/
|
||||
add_attributes_portly(data, m->portly);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int juniper_i2c_cpld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
int status;
|
||||
struct cpld_data *data = NULL;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_dbg(dev, "i2c_check_functionality failed (0x%x)\n", client->addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->model = dev_id->driver_data;
|
||||
data->cmn_attr = &models_attr[data->model];
|
||||
get_sfp_spec(data->model, &data->sfp_num, &data->sfp_types);
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->dev = dev;
|
||||
dev_info(dev, "chip found\n");
|
||||
|
||||
status = add_attributes(client, data);
|
||||
if (status)
|
||||
goto out_kfree;
|
||||
|
||||
/*
|
||||
* If there are no attributes, something is wrong.
|
||||
* Bail out instead of trying to register nothing.
|
||||
*/
|
||||
if (!data->num_attributes) {
|
||||
dev_err(dev, "No attributes found\n");
|
||||
status = -ENODEV;
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &data->group);
|
||||
if (status) {
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
status = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
juniper_i2c_cpld_add_client(client);
|
||||
dev_info(dev, "%s: cpld '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &data->group);
|
||||
out_kfree:
|
||||
kfree(data->group.attrs);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static int juniper_i2c_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, &data->group);
|
||||
kfree(data->group.attrs);
|
||||
juniper_i2c_cpld_remove_client(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int juniper_i2c_cpld_read(u8 cpld_addr, u8 reg)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EPERM;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_read_byte_data(cpld_node->client, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(juniper_i2c_cpld_read);
|
||||
|
||||
int juniper_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EIO;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(juniper_i2c_cpld_write);
|
||||
|
||||
|
||||
static const struct i2c_device_id juniper_i2c_cpld_id[] = {
|
||||
{ "cpld_as7712", AS7712_32X},
|
||||
{ "cpld_as7716", AS7716_32X},
|
||||
{ "cpld_qfx5210", qfx5210_64X},
|
||||
{ "cpld_as7312", AS7312_54X},
|
||||
{ "cpld_plain", PLAIN_CPLD},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, juniper_i2c_cpld_id);
|
||||
|
||||
static struct i2c_driver juniper_i2c_cpld_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "juniper_i2c_cpld",
|
||||
},
|
||||
.probe = juniper_i2c_cpld_probe,
|
||||
.remove = juniper_i2c_cpld_remove,
|
||||
.id_table = juniper_i2c_cpld_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
|
||||
static int __init juniper_i2c_cpld_init(void)
|
||||
{
|
||||
mutex_init(&list_lock);
|
||||
return i2c_add_driver(&juniper_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
static void __exit juniper_i2c_cpld_exit(void)
|
||||
{
|
||||
i2c_del_driver(&juniper_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
module_init(juniper_i2c_cpld_init);
|
||||
module_exit(juniper_i2c_cpld_exit);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("juniper_i2c_cpld driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,622 +0,0 @@
|
||||
/*
|
||||
* An hwmon driver for the 3Y Power YM-2651Y Power Module
|
||||
*
|
||||
* Tested and validated on Juniper QFX5210
|
||||
* Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* Copyright (C) 2014 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MAX_FAN_DUTY_CYCLE 100
|
||||
|
||||
/* Addresses scanned
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { 0x58, 0x5b, I2C_CLIENT_END };
|
||||
|
||||
enum chips {
|
||||
YM2651,
|
||||
YM2401,
|
||||
YM2851,
|
||||
};
|
||||
|
||||
/* Each client has this additional data
|
||||
*/
|
||||
struct ym2651y_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if registers are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
u8 capability; /* Register value */
|
||||
u16 status_word; /* Register value */
|
||||
u8 fan_fault; /* Register value */
|
||||
u8 over_temp; /* Register value */
|
||||
u16 v_out; /* Register value */
|
||||
u16 i_out; /* Register value */
|
||||
u16 p_out; /* Register value */
|
||||
u16 temp; /* Register value */
|
||||
u16 fan_speed; /* Register value */
|
||||
u16 fan_duty_cycle[2]; /* Register value */
|
||||
u8 fan_dir[4]; /* Register value */
|
||||
u8 pmbus_revision; /* Register value */
|
||||
u8 mfr_id[10]; /* Register value */
|
||||
u8 mfr_model[10]; /* Register value */
|
||||
u8 mfr_revsion[3]; /* Register value */
|
||||
u16 mfr_vin_min; /* Register value */
|
||||
u16 mfr_vin_max; /* Register value */
|
||||
u16 mfr_iin_max; /* Register value */
|
||||
u16 mfr_iout_max; /* Register value */
|
||||
u16 mfr_pin_max; /* Register value */
|
||||
u16 mfr_pout_max; /* Register value */
|
||||
u16 mfr_vout_min; /* Register value */
|
||||
u16 mfr_vout_max; /* Register value */
|
||||
};
|
||||
|
||||
static ssize_t show_byte(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_word(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_linear(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_over_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_ascii(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static struct ym2651y_data *ym2651y_update_device(struct device *dev);
|
||||
static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value);
|
||||
|
||||
enum ym2651y_sysfs_attributes {
|
||||
PSU_POWER_ON = 0,
|
||||
PSU_TEMP_FAULT,
|
||||
PSU_POWER_GOOD,
|
||||
PSU_FAN1_FAULT,
|
||||
PSU_FAN_DIRECTION,
|
||||
PSU_OVER_TEMP,
|
||||
PSU_V_OUT,
|
||||
PSU_I_OUT,
|
||||
PSU_P_OUT,
|
||||
PSU_P_OUT_UV, /*In Unit of microVolt, instead of mini.*/
|
||||
PSU_TEMP1_INPUT,
|
||||
PSU_FAN1_SPEED,
|
||||
PSU_FAN1_DUTY_CYCLE,
|
||||
PSU_PMBUS_REVISION,
|
||||
PSU_MFR_ID,
|
||||
PSU_MFR_MODEL,
|
||||
PSU_MFR_REVISION,
|
||||
PSU_MFR_VIN_MIN,
|
||||
PSU_MFR_VIN_MAX,
|
||||
PSU_MFR_VOUT_MIN,
|
||||
PSU_MFR_VOUT_MAX,
|
||||
PSU_MFR_IIN_MAX,
|
||||
PSU_MFR_IOUT_MAX,
|
||||
PSU_MFR_PIN_MAX,
|
||||
PSU_MFR_POUT_MAX
|
||||
};
|
||||
|
||||
/* sysfs attributes for hwmon
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON);
|
||||
static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT);
|
||||
static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT);
|
||||
static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP);
|
||||
static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_linear, NULL, PSU_V_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION);
|
||||
static SENSOR_DEVICE_ATTR(psu_pmbus_revision, S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX);
|
||||
|
||||
/*Duplicate nodes for lm-sensors.*/
|
||||
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_linear, NULL, PSU_V_OUT);
|
||||
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, show_linear, NULL, PSU_I_OUT);
|
||||
static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, show_linear, NULL, PSU_P_OUT_UV);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED);
|
||||
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT);
|
||||
|
||||
static struct attribute *ym2651y_attributes[] = {
|
||||
&sensor_dev_attr_psu_power_on.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_temp_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_power_good.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_over_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_v_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_i_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_p_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan_dir.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_pmbus_revision.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_id.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_model.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_revision.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr,
|
||||
/*Duplicate nodes for lm-sensors.*/
|
||||
&sensor_dev_attr_curr2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_fault.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t show_byte(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) :
|
||||
sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t show_word(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
u16 status = 0;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */
|
||||
status = (data->status_word & 0x40) ? 0 : 1;
|
||||
break;
|
||||
case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */
|
||||
status = (data->status_word & 0x4) >> 2;
|
||||
break;
|
||||
case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */
|
||||
status = (data->status_word & 0x800) ? 0 : 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static int two_complement_to_int(u16 data, u8 valid_bit, int mask)
|
||||
{
|
||||
u16 valid_data = data & mask;
|
||||
bool is_negative = valid_data >> (valid_bit - 1);
|
||||
|
||||
return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data;
|
||||
}
|
||||
|
||||
static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1;
|
||||
long speed;
|
||||
int error;
|
||||
|
||||
error = kstrtol(buf, 10, &speed);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_duty_cycle[nr] = speed;
|
||||
ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_linear(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
u16 value = 0;
|
||||
int exponent, mantissa;
|
||||
int multiplier = 1000;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_V_OUT:
|
||||
value = data->v_out;
|
||||
break;
|
||||
case PSU_I_OUT:
|
||||
value = data->i_out;
|
||||
break;
|
||||
case PSU_P_OUT_UV:
|
||||
multiplier = 1000000; /*For lm-sensors, unit is micro-Volt.*/
|
||||
/*Passing through*/
|
||||
case PSU_P_OUT:
|
||||
value = data->p_out;
|
||||
break;
|
||||
case PSU_TEMP1_INPUT:
|
||||
value = data->temp;
|
||||
break;
|
||||
case PSU_FAN1_SPEED:
|
||||
value = data->fan_speed;
|
||||
multiplier = 1;
|
||||
break;
|
||||
case PSU_FAN1_DUTY_CYCLE:
|
||||
value = data->fan_duty_cycle[0];
|
||||
multiplier = 1;
|
||||
break;
|
||||
case PSU_MFR_VIN_MIN:
|
||||
value = data->mfr_vin_min;
|
||||
break;
|
||||
case PSU_MFR_VIN_MAX:
|
||||
value = data->mfr_vin_max;
|
||||
break;
|
||||
case PSU_MFR_VOUT_MIN:
|
||||
value = data->mfr_vout_min;
|
||||
break;
|
||||
case PSU_MFR_VOUT_MAX:
|
||||
value = data->mfr_vout_max;
|
||||
break;
|
||||
case PSU_MFR_PIN_MAX:
|
||||
value = data->mfr_pin_max;
|
||||
break;
|
||||
case PSU_MFR_POUT_MAX:
|
||||
value = data->mfr_pout_max;
|
||||
break;
|
||||
case PSU_MFR_IOUT_MAX:
|
||||
value = data->mfr_iout_max;
|
||||
break;
|
||||
case PSU_MFR_IIN_MAX:
|
||||
value = data->mfr_iin_max;
|
||||
break;
|
||||
}
|
||||
|
||||
exponent = two_complement_to_int(value >> 11, 5, 0x1f);
|
||||
mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff);
|
||||
return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) :
|
||||
sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent));
|
||||
}
|
||||
|
||||
static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6;
|
||||
|
||||
return sprintf(buf, "%d\n", data->fan_fault >> shift);
|
||||
}
|
||||
|
||||
static ssize_t show_over_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", data->over_temp >> 7);
|
||||
}
|
||||
|
||||
static ssize_t show_ascii(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
u8 *ptr = NULL;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_FAN_DIRECTION: /* psu_fan_dir */
|
||||
ptr = data->fan_dir;
|
||||
break;
|
||||
case PSU_MFR_ID: /* psu_mfr_id */
|
||||
ptr = data->mfr_id;
|
||||
break;
|
||||
case PSU_MFR_MODEL: /* psu_mfr_model */
|
||||
ptr = data->mfr_model;
|
||||
break;
|
||||
case PSU_MFR_REVISION: /* psu_mfr_revision */
|
||||
ptr = data->mfr_revsion;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", ptr);
|
||||
}
|
||||
|
||||
static const struct attribute_group ym2651y_group = {
|
||||
.attrs = ym2651y_attributes,
|
||||
};
|
||||
|
||||
static int ym2651y_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct ym2651y_data *data;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
status = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
dev_info(&client->dev, "chip found\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &ym2651y_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: psu '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &ym2651y_group);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ym2651y_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &ym2651y_group);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ym2651y_id[] = {
|
||||
{ "ym2651", YM2651 },
|
||||
{ "ym2401", YM2401 },
|
||||
{ "ym2851", YM2851 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ym2651y_id);
|
||||
|
||||
static struct i2c_driver ym2651y_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ym2651",
|
||||
},
|
||||
.probe = ym2651y_probe,
|
||||
.remove = ym2651y_remove,
|
||||
.id_table = ym2651y_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int ym2651y_read_byte(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int ym2651y_read_word(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
|
||||
static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, value);
|
||||
}
|
||||
|
||||
static int ym2651y_read_block(struct i2c_client *client, u8 command, u8 *data,
|
||||
int data_len)
|
||||
{
|
||||
int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data);
|
||||
|
||||
if (unlikely(result < 0))
|
||||
goto abort;
|
||||
if (unlikely(result != data_len)) {
|
||||
result = -EIO;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
abort:
|
||||
return result;
|
||||
}
|
||||
|
||||
struct reg_data_byte {
|
||||
u8 reg;
|
||||
u8 *value;
|
||||
};
|
||||
|
||||
struct reg_data_word {
|
||||
u8 reg;
|
||||
u16 *value;
|
||||
};
|
||||
|
||||
static struct ym2651y_data *ym2651y_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
int i, status;
|
||||
u8 command;
|
||||
u8 fan_dir[5] = {0};
|
||||
struct reg_data_byte regs_byte[] = { {0x19, &data->capability},
|
||||
{0x7d, &data->over_temp},
|
||||
{0x81, &data->fan_fault},
|
||||
{0x98, &data->pmbus_revision}
|
||||
};
|
||||
struct reg_data_word regs_word[] = { {0x79, &data->status_word},
|
||||
{0x8b, &data->v_out},
|
||||
{0x8c, &data->i_out},
|
||||
{0x96, &data->p_out},
|
||||
{0x8d, &data->temp},
|
||||
{0x3b, &(data->fan_duty_cycle[0])},
|
||||
{0x3c, &(data->fan_duty_cycle[1])},
|
||||
{0x90, &data->fan_speed},
|
||||
{0xa0, &data->mfr_vin_min},
|
||||
{0xa1, &data->mfr_vin_max},
|
||||
{0xa2, &data->mfr_iin_max},
|
||||
{0xa3, &data->mfr_pin_max},
|
||||
{0xa4, &data->mfr_vout_min},
|
||||
{0xa5, &data->mfr_vout_max},
|
||||
{0xa6, &data->mfr_iout_max},
|
||||
{0xa7, &data->mfr_pout_max}
|
||||
};
|
||||
|
||||
dev_dbg(&client->dev, "Starting ym2651 update\n");
|
||||
|
||||
/* Read byte data */
|
||||
for (i = 0; i < ARRAY_SIZE(regs_byte); i++) {
|
||||
status = ym2651y_read_byte(client, regs_byte[i].reg);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_byte[i].reg, status);
|
||||
*(regs_byte[i].value) = 0;
|
||||
}
|
||||
else {
|
||||
*(regs_byte[i].value) = status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read word data */
|
||||
for (i = 0; i < ARRAY_SIZE(regs_word); i++) {
|
||||
status = ym2651y_read_word(client, regs_word[i].reg);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_word[i].reg, status);
|
||||
*(regs_word[i].value) = 0;
|
||||
}
|
||||
else {
|
||||
*(regs_word[i].value) = status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read fan_direction */
|
||||
command = 0xC3;
|
||||
status = ym2651y_read_block(client, command, fan_dir, ARRAY_SIZE(fan_dir)-1);
|
||||
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
}
|
||||
|
||||
strncpy(data->fan_dir, fan_dir+1, ARRAY_SIZE(data->fan_dir)-1);
|
||||
data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0';
|
||||
|
||||
/* Read mfr_id */
|
||||
command = 0x99;
|
||||
status = ym2651y_read_block(client, command, data->mfr_id,
|
||||
ARRAY_SIZE(data->mfr_id)-1);
|
||||
data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
/* Read mfr_model */
|
||||
command = 0x9a;
|
||||
status = ym2651y_read_block(client, command, data->mfr_model,
|
||||
ARRAY_SIZE(data->mfr_model)-1);
|
||||
data->mfr_model[ARRAY_SIZE(data->mfr_model)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
/* Read mfr_revsion */
|
||||
command = 0x9b;
|
||||
status = ym2651y_read_block(client, command, data->mfr_revsion,
|
||||
ARRAY_SIZE(data->mfr_revsion)-1);
|
||||
data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
module_i2c_driver(ym2651y_driver);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("3Y Power YM-2651Y driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -23,7 +23,6 @@ MODULE_DIRS:= qfx5210 qfx5200
|
||||
MODULE_DIR := modules
|
||||
UTILS_DIR := utils
|
||||
SERVICE_DIR := service
|
||||
PLATFORM_DIR := sonic_platform
|
||||
CONF_DIR := conf
|
||||
|
||||
%:
|
||||
@ -38,7 +37,7 @@ build:
|
||||
#make modules -C $(KERNEL_SRC)/build M=$(MODULE_SRC)
|
||||
(for mod in $(MODULE_DIRS); do \
|
||||
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules || exit 1; \
|
||||
$(PYTHON) $${mod}/setup.py build; \
|
||||
$(PYTHON) setup.py build; \
|
||||
done)
|
||||
|
||||
binary: binary-arch binary-indep
|
||||
@ -65,7 +64,7 @@ binary-indep:
|
||||
cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
|
||||
cp $(MOD_SRC_DIR)/$${mod}/$(UTILS_DIR)/* debian/$(PACKAGE_PRE_NAME)-$${mod}/usr/local/bin/; \
|
||||
cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \
|
||||
$(PYTHON) $${mod}/setup.py install --root=$(MOD_SRC_DIR)/debian/$(PACKAGE_PRE_NAME)-$${mod} --install-layout=deb; \
|
||||
$(PYTHON) setup.py install --root=$(MOD_SRC_DIR)/debian/$(PACKAGE_PRE_NAME)-$${mod} --install-layout=deb; \
|
||||
done)
|
||||
# Resuming debhelper scripts
|
||||
dh_testroot
|
||||
|
@ -1,5 +1,3 @@
|
||||
qfx5200/utils/juniper_qfx5200_util.py usr/local/bin
|
||||
qfx5200/utils/juniper_qfx5200_monitor.py usr/local/bin
|
||||
qfx5200/sonic_platform/chassis.py usr/lib/python2.7/dist-packages/sonic_platform
|
||||
qfx5200/sonic_platform/platform.py usr/lib/python2.7/dist-packages/sonic_platform
|
||||
qfx5200/service/qfx5200-platform-init.service etc/systemd/system
|
||||
|
@ -1,661 +0,0 @@
|
||||
/*
|
||||
* Juniper Networks TMC GPIO driver
|
||||
*
|
||||
* Copyright (C) 2020 Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This driver implement the GPIO set/get functionality
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include "jnx-tmc.h"
|
||||
|
||||
#define TMC_GPIO_MAX_BITS_PER_REG 16
|
||||
#define TMC_GPIO_SFP_MAX_BITS_PER_REG 2
|
||||
#define TMC_GPIO_PTPCFG_MAX_BITS_PER_REG 8
|
||||
|
||||
#define TMC_GPIO_FIND_GROUP(gpio) \
|
||||
((gpio) / TMC_GPIO_MAX_BITS_PER_REG)
|
||||
#define TMC_GPIO_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_SFP_FIND_GROUP(gpio) \
|
||||
((gpio) / TMC_GPIO_SFP_MAX_BITS_PER_REG)
|
||||
#define TMC_GPIO_SFP_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_SFP_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_PTPCFG_FIND_GPIO(gpio) \
|
||||
((gpio) % TMC_GPIO_PTPCFG_MAX_BITS_PER_REG)
|
||||
|
||||
#define TMC_GPIO_MAX_NGPIO_PER_GROUP 320
|
||||
|
||||
#define TMC_PFE_QSFP_RESET_OFFSET 0x4
|
||||
#define TMC_PFE_QSFP_PRESENT_OFFSET 0x8
|
||||
#define TMC_PFE_QSFP_PHY_RESET_OFFSET 0x10
|
||||
#define TMC_PFE_QSFP_LPMOD_OFFSET 0x78
|
||||
#define TMC_PFE_QSFP_LED_CTRL_OFFSET 0x20
|
||||
|
||||
#define TMC_PFE_LANES_GREEN_LED_VALUE 0x3
|
||||
#define TMC_PFE_LANE0_GREEN_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_GREEN_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_GREEN_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_GREEN_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_LANES_BEACON_LED_VALUE 0x2
|
||||
#define TMC_PFE_LANE0_BEACON_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_BEACON_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_BEACON_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_BEACON_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_LANES_FAULT_LED_VALUE 0x1
|
||||
#define TMC_PFE_LANE0_FAULT_LED_BIT_POSITION 0
|
||||
#define TMC_PFE_LANE1_FAULT_LED_BIT_POSITION 2
|
||||
#define TMC_PFE_LANE2_FAULT_LED_BIT_POSITION 4
|
||||
#define TMC_PFE_LANE3_FAULT_LED_BIT_POSITION 6
|
||||
|
||||
#define TMC_PFE_SFPSB0_TX_DISABLE_OFFSET 0x0
|
||||
#define TMC_PFE_SFPSB0_LED_CTRL_OFFSET 0xC
|
||||
#define TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET 0x14
|
||||
#define TMC_PFE_SFPSB0_PRESENT_OFFSET 0x18
|
||||
#define TMC_PFE_SFPSB0_LOSS_OFFSET 0x1C
|
||||
#define TMC_PFE_SFPSB0_TX_FAULT_OFFSET 0x20
|
||||
|
||||
#define TMC_PFE_SFPSB1_TX_DISABLE_OFFSET 0x0
|
||||
#define TMC_PFE_SFPSB1_LED_CTRL_OFFSET 0x8
|
||||
#define TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET 0x10
|
||||
#define TMC_PFE_SFPSB1_PRESENT_OFFSET 0x14
|
||||
#define TMC_PFE_SFPSB1_LOSS_OFFSET 0x18
|
||||
#define TMC_PFE_SFPSB1_TX_FAULT_OFFSET 0x1C
|
||||
|
||||
/*
|
||||
* Index 4 to 15 is used for QSFP starting with
|
||||
* QSFP_LED_LANE0_GREEN. To keep multibit set/get common
|
||||
* starting SFP_LED_LANE0_GREEN with 16 which will avoid
|
||||
* conflict with QSFP enums.
|
||||
*/
|
||||
#define SFP_LED_OP_START_INDEX 16
|
||||
|
||||
/*
|
||||
* Used for off-setting SFP led op index
|
||||
*/
|
||||
#define SFP_LED_OP_OFFSET 0xB
|
||||
|
||||
/*
|
||||
* SFP slave blocks
|
||||
*/
|
||||
#define SFP_SLAVE0_BLOCK 0x1
|
||||
#define SFP_SLAVE1_BLOCK 0x2
|
||||
|
||||
/*
|
||||
* each group represent the 16 gpios.
|
||||
* QSFP_RST - QSFP_LPMODE
|
||||
* each bit represent the one gpio
|
||||
* exemple: bits[0:15] - bit0 - gpio0
|
||||
* QSFP_LED_LANE0_GREEN - QSFP_LED_LANE3_FAULT
|
||||
* here, number represent the one gpio
|
||||
* exemple: bits[0:1]
|
||||
* 00 - gpio off, 01 - gpio on [ gpio0]
|
||||
* 00 - gpio off, 10 - gpio on [ gpio1]
|
||||
* 00 - gpio off, 11 - gpio on [ gpio2]
|
||||
*
|
||||
*/
|
||||
enum {
|
||||
QSFP_RST,
|
||||
QSFP_PRESENT,
|
||||
QSFP_PHY_RST,
|
||||
QSFP_LPMOD,
|
||||
QSFP_LED_LANE0_GREEN,
|
||||
QSFP_LED_LANE1_GREEN,
|
||||
QSFP_LED_LANE2_GREEN,
|
||||
QSFP_LED_LANE3_GREEN,
|
||||
QSFP_LED_LANE0_BEACON,
|
||||
QSFP_LED_LANE1_BEACON,
|
||||
QSFP_LED_LANE2_BEACON,
|
||||
QSFP_LED_LANE3_BEACON,
|
||||
QSFP_LED_LANE0_FAULT,
|
||||
QSFP_LED_LANE1_FAULT,
|
||||
QSFP_LED_LANE2_FAULT,
|
||||
QSFP_LED_LANE3_FAULT,
|
||||
TMC_PFE_GPIO_GROUP_MAX
|
||||
};
|
||||
|
||||
enum sfp_op {
|
||||
SFP_TX_DISABLE,
|
||||
SFP_LED_ACTIVITY,
|
||||
SFP_PRESENT,
|
||||
SFP_SFP_LOS,
|
||||
SFP_TX_FAULT,
|
||||
SFP_LED_LANE0_GREEN = SFP_LED_OP_START_INDEX,
|
||||
SFP_LED_LANE1_GREEN,
|
||||
SFP_LED_LANE2_GREEN,
|
||||
SFP_LED_LANE3_GREEN,
|
||||
SFP_LED_LANE0_BEACON,
|
||||
SFP_LED_LANE1_BEACON,
|
||||
SFP_LED_LANE2_BEACON,
|
||||
SFP_LED_LANE3_BEACON,
|
||||
SFP_LED_LANE0_FAULT,
|
||||
SFP_LED_LANE1_FAULT,
|
||||
SFP_LED_LANE2_FAULT,
|
||||
SFP_LED_LANE3_FAULT,
|
||||
TMC_PFE_SFP_GPIO_GROUP_MAX
|
||||
};
|
||||
|
||||
static const u32 group_offset[TMC_PFE_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_QSFP_RESET_OFFSET,
|
||||
TMC_PFE_QSFP_PRESENT_OFFSET,
|
||||
TMC_PFE_QSFP_PHY_RESET_OFFSET,
|
||||
TMC_PFE_QSFP_LPMOD_OFFSET,
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_QSFP_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
static const u32 sfp_slaveb0_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_SFPSB0_TX_DISABLE_OFFSET,
|
||||
TMC_PFE_SFPSB0_LED_ACTIVITY_OFFSET,
|
||||
TMC_PFE_SFPSB0_PRESENT_OFFSET,
|
||||
TMC_PFE_SFPSB0_LOSS_OFFSET,
|
||||
TMC_PFE_SFPSB0_TX_FAULT_OFFSET,
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_SFPSB0_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
static const u32 sfp_slaveb1_group_offset[TMC_PFE_SFP_GPIO_GROUP_MAX] = {
|
||||
TMC_PFE_SFPSB1_TX_DISABLE_OFFSET,
|
||||
TMC_PFE_SFPSB1_LED_ACTIVITY_OFFSET,
|
||||
TMC_PFE_SFPSB1_PRESENT_OFFSET,
|
||||
TMC_PFE_SFPSB1_LOSS_OFFSET,
|
||||
TMC_PFE_SFPSB1_TX_FAULT_OFFSET,
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 GREEN */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 BEACON */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE0 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE1 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE2 FAULT */
|
||||
TMC_PFE_SFPSB1_LED_CTRL_OFFSET, /* LANE3 FAULT */
|
||||
};
|
||||
|
||||
struct tmc_gpio_info {
|
||||
int (*get)(struct gpio_chip *, unsigned int);
|
||||
void (*set)(struct gpio_chip *, unsigned int, int);
|
||||
int (*dirin)(struct gpio_chip *, unsigned int);
|
||||
int (*dirout)(struct gpio_chip *, unsigned int, int);
|
||||
};
|
||||
|
||||
struct tmc_gpio_chip {
|
||||
const struct tmc_gpio_info *info;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct gpio_chip gpio;
|
||||
int ngpio;
|
||||
spinlock_t gpio_lock; /* gpio lock */
|
||||
int sfp_slave_block;
|
||||
};
|
||||
|
||||
/* slave gpio max */
|
||||
static int gpio_max = 320;
|
||||
module_param(gpio_max, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
MODULE_PARM_DESC(gpio_max, "Maximum number of gpio for SLAVE TMC GPIO");
|
||||
|
||||
/*
|
||||
* generic bit operation functions
|
||||
*/
|
||||
static u32 tmc_gpio_reset_bits(u32 state, u32 val, u32 shift)
|
||||
{
|
||||
state &= ~(val << shift);
|
||||
return state;
|
||||
};
|
||||
|
||||
static u32 tmc_gpio_set_bits(u32 state, u32 val, u32 shift)
|
||||
{
|
||||
state |= (val << shift);
|
||||
return state;
|
||||
};
|
||||
|
||||
static u32 tmc_gpio_find_bits_val(u32 state, u32 shift, u32 mask)
|
||||
{
|
||||
return ((state >> shift)) & mask;
|
||||
};
|
||||
|
||||
#define to_tmc_chip(chip) \
|
||||
container_of((chip), struct tmc_gpio_chip, gpio)
|
||||
|
||||
/*
|
||||
* tmc_gpio_multiple_bitsop - Generic TMC GPIO multiple bits operation
|
||||
*/
|
||||
static void tmc_gpio_multiple_bitsop(struct tmc_gpio_chip *chip,
|
||||
unsigned int gpiono, u32 group, u32 offset, bool set)
|
||||
{
|
||||
u32 gpio_state, led_val, bit_shift;
|
||||
unsigned long flags;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO multiple bitop group=%u, "
|
||||
"gpiono=%u, offet:=%u, set=%u\n", group, gpiono, offset, set);
|
||||
|
||||
spin_lock_irqsave(&chip->gpio_lock, flags);
|
||||
|
||||
switch (group) {
|
||||
case QSFP_LED_LANE0_GREEN:
|
||||
case SFP_LED_LANE0_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_GREEN:
|
||||
case SFP_LED_LANE1_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_GREEN:
|
||||
case SFP_LED_LANE2_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_GREEN:
|
||||
case SFP_LED_LANE3_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_GREEN_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_GREEN_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE0_BEACON:
|
||||
case SFP_LED_LANE0_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_BEACON:
|
||||
case SFP_LED_LANE1_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_BEACON:
|
||||
case SFP_LED_LANE2_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_BEACON:
|
||||
case SFP_LED_LANE3_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_BEACON_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_BEACON_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE0_FAULT:
|
||||
case SFP_LED_LANE0_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE0_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE1_FAULT:
|
||||
case SFP_LED_LANE1_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE1_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE2_FAULT:
|
||||
case SFP_LED_LANE2_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE2_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
case QSFP_LED_LANE3_FAULT:
|
||||
case SFP_LED_LANE3_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
led_val = TMC_PFE_LANES_FAULT_LED_VALUE;
|
||||
bit_shift = TMC_PFE_LANE3_FAULT_LED_BIT_POSITION;
|
||||
break;
|
||||
|
||||
default:
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
|
||||
gpio_state = tmc_gpio_set_bits(gpio_state, led_val, bit_shift);
|
||||
} else {
|
||||
gpio_state = tmc_gpio_reset_bits(gpio_state, 0x3, bit_shift);
|
||||
}
|
||||
|
||||
iowrite32(gpio_state, (iobase+(0x004*gpiono)));
|
||||
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_gpio_one_bitop - Generic TMC GPIO single bit operation
|
||||
*/
|
||||
static void tmc_gpio_one_bitop(struct tmc_gpio_chip *chip,
|
||||
unsigned int bit, u32 offset, bool set)
|
||||
{
|
||||
u32 gpio_state;
|
||||
unsigned long flags;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO one bitop bit=%u, offset=%x, "
|
||||
"set=%u\n", bit, offset, set);
|
||||
|
||||
spin_lock_irqsave(&chip->gpio_lock, flags);
|
||||
|
||||
gpio_state = ioread32(iobase);
|
||||
if (set)
|
||||
gpio_state |= BIT(bit);
|
||||
else
|
||||
gpio_state &= ~BIT(bit);
|
||||
|
||||
iowrite32(gpio_state, iobase);
|
||||
|
||||
spin_unlock_irqrestore(&chip->gpio_lock, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* tmc_gpio_get_multiple_bitsop - Generic TMC get GPIO multiple bits operation
|
||||
*/
|
||||
static int tmc_gpio_get_multiple_bitsop(struct tmc_gpio_chip *chip,
|
||||
unsigned int gpiono, u32 group, u32 offset)
|
||||
{
|
||||
u32 gpio_state;
|
||||
void __iomem *iobase;
|
||||
|
||||
iobase = chip->base + offset;
|
||||
|
||||
dev_dbg(chip->dev, "TMC GPIO get multiple bitsop group=%u, "
|
||||
"gpiono=%u, offset=%u\n", group, gpiono, offset);
|
||||
|
||||
switch (group) {
|
||||
case QSFP_LED_LANE0_GREEN:
|
||||
case SFP_LED_LANE0_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_GREEN:
|
||||
case SFP_LED_LANE1_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_GREEN:
|
||||
case SFP_LED_LANE2_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_GREEN:
|
||||
case SFP_LED_LANE3_GREEN:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_GREEN_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_GREEN_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE0_BEACON:
|
||||
case SFP_LED_LANE0_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_BEACON:
|
||||
case SFP_LED_LANE1_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_BEACON:
|
||||
case SFP_LED_LANE2_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_BEACON:
|
||||
case SFP_LED_LANE3_BEACON:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_BEACON_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_BEACON_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE0_FAULT:
|
||||
case SFP_LED_LANE0_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE0_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE1_FAULT:
|
||||
case SFP_LED_LANE1_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE1_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE2_FAULT:
|
||||
case SFP_LED_LANE2_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE2_FAULT_LED_BIT_POSITION, 0x3));
|
||||
case QSFP_LED_LANE3_FAULT:
|
||||
case SFP_LED_LANE3_FAULT:
|
||||
gpio_state = ioread32(iobase+(0x004*gpiono));
|
||||
return (TMC_PFE_LANES_FAULT_LED_VALUE ==
|
||||
tmc_gpio_find_bits_val(gpio_state,
|
||||
TMC_PFE_LANE3_FAULT_LED_BIT_POSITION, 0x3));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_gpio_get - Read the specified signal of the GPIO device.
|
||||
*/
|
||||
static int tmc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = to_tmc_chip(gc);
|
||||
unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
|
||||
unsigned int bit = TMC_GPIO_FIND_GPIO(gpio);
|
||||
|
||||
if (group >= TMC_PFE_GPIO_GROUP_MAX)
|
||||
return 0;
|
||||
|
||||
switch (group) {
|
||||
case QSFP_RST:
|
||||
case QSFP_PRESENT:
|
||||
case QSFP_PHY_RST:
|
||||
case QSFP_LPMOD:
|
||||
dev_dbg(chip->dev, "TMC GPIO get one bitop group=%u, gpio=%u, "
|
||||
"bit=%u\n", group, gpio, bit);
|
||||
return !!(ioread32(chip->base + group_offset[group])
|
||||
& BIT(bit));
|
||||
default:
|
||||
return tmc_gpio_get_multiple_bitsop(chip, bit, group, group_offset[group]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tmc_gpio_set - Write the specified signal of the GPIO device.
|
||||
*/
|
||||
static void tmc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = to_tmc_chip(gc);
|
||||
unsigned int group = TMC_GPIO_FIND_GROUP(gpio);
|
||||
unsigned int bit = TMC_GPIO_FIND_GPIO(gpio);
|
||||
|
||||
if (group >= TMC_PFE_GPIO_GROUP_MAX)
|
||||
return;
|
||||
|
||||
switch (group) {
|
||||
case QSFP_RST:
|
||||
case QSFP_PRESENT:
|
||||
case QSFP_PHY_RST:
|
||||
case QSFP_LPMOD:
|
||||
dev_dbg(chip->dev, "TMC GPIO one bitop group=%d\n", group);
|
||||
tmc_gpio_one_bitop(chip, bit, group_offset[group], val);
|
||||
break;
|
||||
default:
|
||||
tmc_gpio_multiple_bitsop(chip, bit, group, group_offset[group], val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tmc_gpio_info tmc_gpios[] = {
|
||||
{
|
||||
.get = tmc_gpio_get,
|
||||
.set = tmc_gpio_set,
|
||||
},
|
||||
};
|
||||
|
||||
static void tmc_gpio_setup(struct tmc_gpio_chip *sgc, int id)
|
||||
{
|
||||
struct gpio_chip *chip = &sgc->gpio;
|
||||
const struct tmc_gpio_info *info = sgc->info;
|
||||
|
||||
chip->get = info->get;
|
||||
chip->set = info->set;
|
||||
chip->direction_input = info->dirin;
|
||||
chip->direction_output = info->dirout;
|
||||
chip->dbg_show = NULL;
|
||||
chip->can_sleep = 0;
|
||||
|
||||
if (id == 0) {
|
||||
chip->base = 0;
|
||||
} else if (id == 1) {
|
||||
chip->base = (gpio_max * id);
|
||||
} else {
|
||||
chip->base = -1;
|
||||
}
|
||||
|
||||
chip->ngpio = sgc->ngpio;
|
||||
chip->label = dev_name(sgc->dev);
|
||||
chip->parent = sgc->dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
}
|
||||
|
||||
static int tmc_gpio_of_init(struct device *dev,
|
||||
struct tmc_gpio_chip *chip)
|
||||
{
|
||||
chip->info = &tmc_gpios[0];
|
||||
chip->ngpio = gpio_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tmc_gpio_chip *chip;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
|
||||
dev_dbg(dev, "TMC GPIO probe\n");
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(dev, "TMC GPIO resource 0x%llx, %llu\n",
|
||||
res->start, resource_size(res));
|
||||
|
||||
chip->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!chip->base)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = tmc_gpio_of_init(dev, chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->dev = dev;
|
||||
spin_lock_init(&chip->gpio_lock);
|
||||
|
||||
tmc_gpio_setup(chip, cell->id);
|
||||
|
||||
ret = gpiochip_add(&chip->gpio);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to register TMC gpiochip : %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
dev_info(dev, "TMC GPIO registered at 0x%lx, gpiobase: %d\n",
|
||||
(long unsigned)chip->base, chip->gpio.base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tmc_gpio_chip *chip = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&chip->gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tmc_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpioslave-tmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tmc_gpio_probe,
|
||||
.remove = tmc_gpio_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(tmc_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks TMC FPGA GPIO driver");
|
||||
MODULE_AUTHOR("Ashish Bhensdadia <bashish@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1 @@
|
||||
../../common/modules/gpio-tmc.c
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
||||
../../common/modules/i2c-tmc.c
|
@ -1,603 +0,0 @@
|
||||
/*
|
||||
* Juniper Networks RE-FPGA qfx platform specific driver
|
||||
*
|
||||
* Copyright (C) 2020 Juniper Networks
|
||||
* Author: Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* This driver implements various features such as
|
||||
* - ALARM led driver
|
||||
* - Fan full speed reset control
|
||||
* - FAN precense detection
|
||||
* - FAN type detection
|
||||
* - Any new QFX specific features which uses RE-FPGA
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
#define NUM_LEDS 7 /* Max number of Alarm + FAN LEDs */
|
||||
|
||||
#define ALARM_MINOR_LED 0
|
||||
#define ALARM_MAJOR_LED 1
|
||||
|
||||
#define REFPGA_PCIE_RESET_CTRL 0x13
|
||||
#define REFPGA_PCIE_ALARM 0x33
|
||||
#define REFPGA_FAN0_CTRL_STAT 0x28
|
||||
|
||||
#define REFPGA_RESET_FAN_SPEED BIT(3)
|
||||
#define REFPGA_OPER_TYPE BIT(0)
|
||||
#define REFPGA_OPER_START BIT(1)
|
||||
#define REFPGA_OPER_DONE BIT(2)
|
||||
|
||||
#define TMC_REFPGA_ADDR_REG 0x0 /* TMC offset: 0x228 */
|
||||
#define TMC_REFPGA_DATA_REG 0x4 /* TMC offset: 0x22C */
|
||||
#define TMC_REFPGA_CTRL_REG 0x8 /* TMC offset: 0x230 */
|
||||
|
||||
#define TMC_REFPGA_READ_CMD 0x3
|
||||
#define TMC_REFPGA_WRITE_CMD 0x2
|
||||
|
||||
#define REFPGA_INTR_NR_GROUPS 1
|
||||
#define REFPGA_INTR_MAX_IRQS_PG 32
|
||||
|
||||
#define MAX_FANS 5
|
||||
|
||||
#define REFPGA_IRQ_MAX_BITS_PER_REG 32
|
||||
|
||||
#define POLL_INTERVAL 5000
|
||||
|
||||
#define AFI_MASK (0x01)
|
||||
#define AFO_MASK (0x02)
|
||||
#define AFI_AFO_MASK (0x03)
|
||||
/*
|
||||
* LED specific data structures
|
||||
*/
|
||||
struct refpga_led {
|
||||
struct led_classdev lc;
|
||||
struct work_struct work;
|
||||
int blink;
|
||||
int on;
|
||||
int bit;
|
||||
void __iomem *addr;
|
||||
};
|
||||
|
||||
struct refpga_led_data {
|
||||
int num_leds;
|
||||
struct refpga_led *leds;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(alarm_led_lock);
|
||||
|
||||
/*
|
||||
* Common routines
|
||||
*/
|
||||
struct refpga_chip {
|
||||
struct refpga_led_data *led;
|
||||
};
|
||||
|
||||
static struct refpga_chip *refpga;
|
||||
|
||||
static DEFINE_MUTEX(refpga_lock);
|
||||
|
||||
static void __iomem *tmc_membase;
|
||||
|
||||
static void wait_for_refpga_oper(void __iomem *base_addr)
|
||||
{
|
||||
volatile u32 done = ~(-1);
|
||||
unsigned long int timeout;
|
||||
void __iomem *addr;
|
||||
|
||||
addr = base_addr + (TMC_REFPGA_CTRL_REG);
|
||||
/*
|
||||
* Wait till the transaction is complete
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
do {
|
||||
usleep_range(50, 100);
|
||||
done = ioread32(addr);
|
||||
if (done & (REFPGA_OPER_DONE))
|
||||
break;
|
||||
} while(time_before(jiffies, timeout));
|
||||
}
|
||||
static u32 refpga_read(void __iomem *base_addr, u32 refpga_offset)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
mutex_lock(&refpga_lock);
|
||||
iowrite32(refpga_offset, base_addr + (TMC_REFPGA_ADDR_REG));
|
||||
iowrite32(TMC_REFPGA_READ_CMD, base_addr + (TMC_REFPGA_CTRL_REG));
|
||||
wait_for_refpga_oper(base_addr);
|
||||
value = ioread32(base_addr + (TMC_REFPGA_DATA_REG));
|
||||
mutex_unlock(&refpga_lock);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void refpga_write(void __iomem *base_addr, u32 refpga_offset, u32 val)
|
||||
{
|
||||
mutex_lock(&refpga_lock);
|
||||
iowrite32(refpga_offset, base_addr + (TMC_REFPGA_ADDR_REG));
|
||||
iowrite32(val, base_addr + (TMC_REFPGA_DATA_REG));
|
||||
iowrite32(TMC_REFPGA_WRITE_CMD, base_addr + (TMC_REFPGA_CTRL_REG));
|
||||
wait_for_refpga_oper(base_addr);
|
||||
mutex_unlock(&refpga_lock);
|
||||
}
|
||||
|
||||
static bool get_fan_presense(u8 idx)
|
||||
{
|
||||
u8 value = 0x00;
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT;
|
||||
bool ret = 0;
|
||||
|
||||
value = refpga_read(tmc_membase, (offset + (idx * 2)));
|
||||
/*
|
||||
* Get the last two bits of REFPGA_FANx_CTRL_STAT.
|
||||
* REFPGA_FANx_CTRL_STAT register of REFPGA gives the fan airflow
|
||||
* status. There are 5 fans in QFX5200. Last two bits give the AFI
|
||||
* & AFO status. If any of these bits are set, fan is present.
|
||||
*/
|
||||
value = (value & BIT(0)) | (value & BIT(1));
|
||||
if (value)
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_fan_type(u8 idx)
|
||||
{
|
||||
u8 value = 0x00;
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT;
|
||||
int ret = -1;
|
||||
|
||||
value = refpga_read(tmc_membase, (offset + (idx * 2)));
|
||||
/*
|
||||
* Get the last two bits of REFPGA_FANx_CTRL_STAT.
|
||||
* REFPGA_FANx_CTRL_STAT register of REFPGA gives the fan airflow
|
||||
* status. There are 5 fans in QFX5200. Last two bits give the AFI
|
||||
* & AFO status. If bit1 is set, it's AFO and if bit 0 is set,
|
||||
* it's AFI.
|
||||
*
|
||||
* This function will return '1' for AFO, '0' for AFI, and '-1'
|
||||
* if there is no fan or if both AFI & AFO bits are set.
|
||||
*/
|
||||
value &= AFI_AFO_MASK;
|
||||
|
||||
switch(value) {
|
||||
case AFI_MASK:
|
||||
ret = 0;
|
||||
break;
|
||||
case AFO_MASK:
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum sysfs_fan_attributes {
|
||||
FAN0_PRESENT,
|
||||
FAN1_PRESENT,
|
||||
FAN2_PRESENT,
|
||||
FAN3_PRESENT,
|
||||
FAN4_PRESENT,
|
||||
};
|
||||
|
||||
enum sysfs_fan_type_attributes {
|
||||
FAN0_TYPE,
|
||||
FAN1_TYPE,
|
||||
FAN2_TYPE,
|
||||
FAN3_TYPE,
|
||||
FAN4_TYPE,
|
||||
};
|
||||
|
||||
/*
|
||||
* The sysfs files will be present in this path
|
||||
* /sys/devices/pci0000:00/0000:00:1c.0/0000:0f:00.0/refpga-tmc.15/fan*_present
|
||||
* /sys/devices/pci0000:00/0000:00:1c.0/0000:0f:00.0/refpga-tmc.15/fan*_type
|
||||
*/
|
||||
|
||||
#define DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(fan##index##_present, S_IRUGO, refpga_fan_presense_show, NULL, FAN##index##_PRESENT)
|
||||
#define DECLARE_FAN_PRESENT_ATTR(index) &sensor_dev_attr_fan##index##_present.dev_attr.attr
|
||||
|
||||
#define DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(index) \
|
||||
static SENSOR_DEVICE_ATTR(fan##index##_type, S_IRUGO, refpga_fan_type_show, NULL, FAN##index##_TYPE)
|
||||
#define DECLARE_FAN_TYPE_ATTR(index) &sensor_dev_attr_fan##index##_type.dev_attr.attr
|
||||
|
||||
static ssize_t refpga_fan_presense_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *s_attr = to_sensor_dev_attr(attr);
|
||||
|
||||
return sprintf(buf, "%d\n", get_fan_presense(s_attr->index));
|
||||
|
||||
}
|
||||
|
||||
static ssize_t refpga_fan_type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *s_attr = to_sensor_dev_attr(attr);
|
||||
|
||||
return sprintf(buf, "%d\n", get_fan_type(s_attr->index));
|
||||
|
||||
}
|
||||
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(0);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(1);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(2);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(3);
|
||||
DECLARE_FAN_PRESENT_SENSOR_DEV_ATTR(4);
|
||||
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(0);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(1);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(2);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(3);
|
||||
DECLARE_FAN_TYPE_SENSOR_DEV_ATTR(4);
|
||||
|
||||
static struct attribute *refpga_fan_attrs[] = {
|
||||
DECLARE_FAN_PRESENT_ATTR(0),
|
||||
DECLARE_FAN_PRESENT_ATTR(1),
|
||||
DECLARE_FAN_PRESENT_ATTR(2),
|
||||
DECLARE_FAN_PRESENT_ATTR(3),
|
||||
DECLARE_FAN_PRESENT_ATTR(4),
|
||||
DECLARE_FAN_TYPE_ATTR(0),
|
||||
DECLARE_FAN_TYPE_ATTR(1),
|
||||
DECLARE_FAN_TYPE_ATTR(2),
|
||||
DECLARE_FAN_TYPE_ATTR(3),
|
||||
DECLARE_FAN_TYPE_ATTR(4),
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group refpga_fan_attr_group = {
|
||||
.attrs = refpga_fan_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* There is only a single ALARM led in QFX5200 and that
|
||||
* is used for both Major & Minor alarm indicator.
|
||||
* These are represented by two different bits in RE-FPGA
|
||||
* PCIE_ALARM register. Only one of the bit (either Red or
|
||||
* Yellow) should be set a time. If both the bits are set,
|
||||
* it's an undefined behaviour.
|
||||
*
|
||||
* The following table describes how the conditions are
|
||||
* handled in the driver as there can be both Major & Minor
|
||||
* alarms can be triggered from userspace.
|
||||
*
|
||||
* Major Minor Colour
|
||||
*
|
||||
* 0 0 Nil
|
||||
* 0 1 Yellow
|
||||
* 1 1 Red
|
||||
* 1 0 Red
|
||||
*
|
||||
*/
|
||||
static void manage_alarm_led(void __iomem *addr, int led_type, int value)
|
||||
{
|
||||
static int alarm_major = 0, alarm_minor = 0;
|
||||
u32 reg = 0x0;
|
||||
|
||||
mutex_lock(&alarm_led_lock);
|
||||
reg = refpga_read(addr, REFPGA_PCIE_ALARM);
|
||||
|
||||
(led_type == ALARM_MAJOR_LED) ?
|
||||
((value == 1) ? (alarm_major = 1) : (alarm_major = 0)) :
|
||||
((value == 1) ? (alarm_minor = 1) : (alarm_minor = 0));
|
||||
if (alarm_major) {
|
||||
reg &= ~BIT(ALARM_MINOR_LED);
|
||||
reg |= BIT(ALARM_MAJOR_LED);
|
||||
} else {
|
||||
if (alarm_minor) {
|
||||
reg &= ~BIT(ALARM_MAJOR_LED);
|
||||
reg |= BIT(ALARM_MINOR_LED);
|
||||
} else {
|
||||
reg &= ~BIT(ALARM_MINOR_LED);
|
||||
reg &= ~BIT(ALARM_MAJOR_LED);
|
||||
}
|
||||
}
|
||||
refpga_write(addr, REFPGA_PCIE_ALARM, reg);
|
||||
mutex_unlock(&alarm_led_lock);
|
||||
}
|
||||
|
||||
static void manage_fan_led(void __iomem *addr, int fan_slot, int value)
|
||||
{
|
||||
u8 offset = REFPGA_FAN0_CTRL_STAT + (fan_slot * 2);
|
||||
u32 reg = 0x0;
|
||||
|
||||
reg = refpga_read(addr, offset);
|
||||
if(value) {
|
||||
/* Turn on s/w control */
|
||||
reg = reg | BIT(4);
|
||||
/* Turn off green led */
|
||||
reg &= ~BIT(5);
|
||||
/* Turn on yellow led & make it blink */
|
||||
reg |= (BIT(6) | BIT(7));
|
||||
} else {
|
||||
/* Clear yellow led & stop blink */
|
||||
reg &= ~(BIT(6) | BIT(7));
|
||||
/* Stop s/w control */
|
||||
reg &= ~BIT(4);
|
||||
}
|
||||
refpga_write(addr, offset, reg);
|
||||
}
|
||||
|
||||
static void refpga_led_work(struct work_struct *work)
|
||||
{
|
||||
struct refpga_led *led = container_of(work, struct refpga_led, work);
|
||||
void __iomem *addr;
|
||||
|
||||
addr = led->addr;
|
||||
|
||||
if(strstr(led->lc.name, "fan"))
|
||||
manage_fan_led(addr, led->bit, led->on);
|
||||
else
|
||||
manage_alarm_led(addr, led->bit, led->on);
|
||||
}
|
||||
|
||||
static void refpga_led_brightness_set(struct led_classdev *lc,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct refpga_led *led = container_of(lc, struct refpga_led, lc);
|
||||
|
||||
led->on = (brightness != LED_OFF);
|
||||
led->blink = 0; /* always turn off hw blink on brightness_set() */
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
struct led_table
|
||||
{
|
||||
const char *name;
|
||||
int reg;
|
||||
};
|
||||
|
||||
static struct led_table qfx5200_led_data[] = {
|
||||
{
|
||||
.name = "alarm-minor",
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "alarm-major",
|
||||
.reg = 1,
|
||||
},
|
||||
{
|
||||
.name = "fan0-fault",
|
||||
.reg = 0,
|
||||
},
|
||||
{
|
||||
.name = "fan1-fault",
|
||||
.reg = 1,
|
||||
},
|
||||
{
|
||||
.name = "fan2-fault",
|
||||
.reg = 2,
|
||||
},
|
||||
{
|
||||
.name = "fan3-fault",
|
||||
.reg = 3,
|
||||
},
|
||||
{
|
||||
.name = "fan4-fault",
|
||||
.reg = 4,
|
||||
}
|
||||
};
|
||||
|
||||
static int refpga_led_init_one(struct device *dev,
|
||||
struct refpga_led_data *ild,
|
||||
int num)
|
||||
{
|
||||
struct refpga_led *led;
|
||||
int ret = 0;
|
||||
|
||||
led = &ild->leds[num];
|
||||
led->addr = tmc_membase;
|
||||
|
||||
led->lc.name = qfx5200_led_data[num].name;
|
||||
led->bit = qfx5200_led_data[num].reg;
|
||||
led->lc.brightness = LED_OFF;
|
||||
led->lc.brightness_set = refpga_led_brightness_set;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &led->lc);
|
||||
if (ret) {
|
||||
dev_err(dev, "devm_led_classdev_register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_WORK(&led->work, refpga_led_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refpga_led_qfx5200_init(struct device *dev, struct refpga_led_data *ild)
|
||||
{
|
||||
int ret = 0, idx = 0;
|
||||
|
||||
|
||||
if (!dev->parent) {
|
||||
dev_err(dev, "dev->parent is null\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ild->num_leds = NUM_LEDS;
|
||||
ild->leds = devm_kzalloc(dev, sizeof(struct refpga_led) * NUM_LEDS,
|
||||
GFP_KERNEL);
|
||||
if (!ild->leds) {
|
||||
dev_err(dev, "LED allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for(idx=0; idx<NUM_LEDS; idx++){
|
||||
ret = refpga_led_init_one(dev, ild, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct refpga_led_data *ild;
|
||||
int ret;
|
||||
|
||||
ild = devm_kzalloc(dev, sizeof(*ild), GFP_KERNEL);
|
||||
if (!ild) {
|
||||
dev_err(dev, "ild allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = refpga_led_qfx5200_init(dev, ild);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
refpga->led = ild;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct refpga_chip *drv_data = platform_get_drvdata(pdev);
|
||||
struct refpga_led_data *ild = drv_data->led;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ild->num_leds; i++) {
|
||||
devm_led_classdev_unregister(&pdev->dev, &ild->leds[i].lc);
|
||||
cancel_work_sync(&ild->leds[i].work);
|
||||
}
|
||||
if (ild) {
|
||||
if (ild->leds)
|
||||
devm_kfree(&pdev->dev, ild->leds);
|
||||
devm_kfree(&pdev->dev, ild);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_fan_full_speed(struct device *dev)
|
||||
{
|
||||
u32 val = ~(-1), tmp = ~(-1);
|
||||
|
||||
/*
|
||||
* Reading the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
val = refpga_read(tmc_membase, REFPGA_PCIE_RESET_CTRL);
|
||||
/*
|
||||
* Clearing the fan full_speed bit
|
||||
*/
|
||||
val &= ~(REFPGA_RESET_FAN_SPEED);
|
||||
/*
|
||||
* Writing the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
refpga_write(tmc_membase, REFPGA_PCIE_RESET_CTRL, val);
|
||||
/*
|
||||
* Reading the REFPGA_PCIE_RESET_CTRL register
|
||||
*/
|
||||
tmp = refpga_read(tmc_membase, REFPGA_PCIE_RESET_CTRL);
|
||||
dev_info(dev, "After resetting fan full speed control: %X\n", tmp);
|
||||
}
|
||||
|
||||
static int jnx_refpga_tmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "resource allocation failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tmc_membase = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!tmc_membase) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
refpga = devm_kzalloc(dev, sizeof(*refpga), GFP_KERNEL);
|
||||
if (!refpga) {
|
||||
dev_err(dev, "refpga memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
reset_fan_full_speed(dev);
|
||||
|
||||
ret = jnx_refpga_led_probe(pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Refpga LED probe failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "Refpga LED probe successful: TMC memoy base: %p\n",
|
||||
tmc_membase);
|
||||
|
||||
ret = sysfs_create_group(&dev->kobj, &refpga_fan_attr_group);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "sysfs_create_group failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, refpga);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jnx_refpga_tmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
jnx_refpga_led_remove(pdev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &refpga_fan_attr_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver jnx_refpga_tmc_driver = {
|
||||
.driver = {
|
||||
.name = "refpga-tmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = jnx_refpga_tmc_probe,
|
||||
.remove = jnx_refpga_tmc_remove,
|
||||
};
|
||||
|
||||
static int __init jnx_refpga_tmc_driver_init(void)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
ret = platform_driver_register(&jnx_refpga_tmc_driver);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void __exit jnx_refpga_tmc_driver_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&jnx_refpga_tmc_driver);
|
||||
}
|
||||
|
||||
module_init(jnx_refpga_tmc_driver_init);
|
||||
module_exit(jnx_refpga_tmc_driver_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks REFPGA / TMC driver");
|
||||
MODULE_AUTHOR("Ciju Rajan K <crajank@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1 @@
|
||||
../../common/modules/jnx-refpga-tmc.c
|
@ -1,477 +0,0 @@
|
||||
/*
|
||||
* Juniper Networks TMC-FPGA MFD Core driver for qfx platform
|
||||
*
|
||||
* Copyright (c) 2020, Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This driver implement the resource publish for below devices
|
||||
* - I2C
|
||||
* - GPIO
|
||||
* - RE FPGA
|
||||
* - PSU
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include "jnx-tmc.h"
|
||||
|
||||
#define TMC_DO_SCRATCH_TEST 1
|
||||
|
||||
/*
|
||||
* TMC FPGA Device IDs
|
||||
*/
|
||||
#define PCI_VENDOR_ID_JUNIPER 0x1304
|
||||
|
||||
#define PCI_DEVICE_ID_JNX_TMC_CHD 0x007B
|
||||
#define PCI_DEVICE_ID_JNX_TMC_PFE 0x007C
|
||||
|
||||
/*
|
||||
* TMC resources
|
||||
*/
|
||||
static struct resource tmc_resource_i2c[] = {
|
||||
/* I2C AUTOMATION Block */
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.start = TMC_I2C_AUTOMATION_I2C_CONTROL_START,
|
||||
.end = TMC_I2C_AUTOMATION_I2C_CONTROL_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
|
||||
/* I2C DPMEM */
|
||||
{
|
||||
.name = "i2c-tmc-mem",
|
||||
.start = TMC_I2C_DPMEM_ENTRY_START,
|
||||
.end = TMC_I2C_DPMEM_ENTRY_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_I2C_NR ARRAY_SIZE(tmc_resource_i2c)
|
||||
|
||||
/*
|
||||
* LED resources
|
||||
*/
|
||||
static struct resource tmc_resource_leds[] = {
|
||||
{
|
||||
.name = "leds-tmc",
|
||||
.start = TMC_LED_CONTROL_START,
|
||||
.end = TMC_LED_CONTROL_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_LEDS_NR ARRAY_SIZE(tmc_resource_leds)
|
||||
|
||||
/*
|
||||
* TMC RE-FPGA devices
|
||||
*/
|
||||
static struct resource tmc_resource_refpga[] = {
|
||||
{
|
||||
.name = "refpga-tmc",
|
||||
.start = TMC_REFPGA_ACCESS_START,
|
||||
.end = TMC_REFPGA_ACCESS_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TMC_RES_REFPGA_NR ARRAY_SIZE(tmc_resource_refpga)
|
||||
|
||||
static struct resource tmc_resource_gpioslave0[] = {
|
||||
/* SLAVE0 Block */
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.start = TMC_GPIO_SLAVE0_START,
|
||||
.end = TMC_GPIO_SLAVE0_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_GPIOSLAVE0_NR ARRAY_SIZE(tmc_resource_gpioslave0)
|
||||
|
||||
static struct resource tmc_resource_gpioslave1[] = {
|
||||
/* SLAVE1 Block */
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.start = TMC_GPIO_SLAVE1_START,
|
||||
.end = TMC_GPIO_SLAVE1_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_GPIOSLAVE1_NR ARRAY_SIZE(tmc_resource_gpioslave1)
|
||||
|
||||
static struct resource tmc_resource_psu[] = {
|
||||
/* PSU Block */
|
||||
{
|
||||
.name = "psu-tmc",
|
||||
.start = TMC_PSU_START,
|
||||
.end = TMC_PSU_END,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
|
||||
#define TMC_RES_PSU_NR ARRAY_SIZE(tmc_resource_psu)
|
||||
|
||||
/*
|
||||
* CHASSISD TMC MFD devices
|
||||
*/
|
||||
static struct mfd_cell chassisd_tmc_mfd_devs[] = {
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_i2c),
|
||||
.resources = &tmc_resource_i2c[0],
|
||||
.of_compatible = "jnx,i2c-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "leds-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_leds),
|
||||
.resources = &tmc_resource_leds[0],
|
||||
.of_compatible = "jnx,leds-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "refpga-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_refpga),
|
||||
.resources = &tmc_resource_refpga[0],
|
||||
.of_compatible = "jnx,refpga-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "psu-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_psu),
|
||||
.resources = &tmc_resource_psu[0],
|
||||
.of_compatible = "jnx,psu-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* PFE TMC MFD devices
|
||||
*/
|
||||
static struct mfd_cell pfe_tmc_mfd_devs[] = {
|
||||
{
|
||||
.name = "i2c-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_i2c),
|
||||
.resources = &tmc_resource_i2c[0],
|
||||
.of_compatible = "jnx,i2c-tmc",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_gpioslave0),
|
||||
.resources = &tmc_resource_gpioslave0[0],
|
||||
.of_compatible = "jnx,gpioslave-tmc",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "gpioslave-tmc",
|
||||
.num_resources = ARRAY_SIZE(tmc_resource_gpioslave1),
|
||||
.resources = &tmc_resource_gpioslave1[0],
|
||||
.of_compatible = "jnx,gpioslave-tmc",
|
||||
.id = 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
struct tmc_fpga_data {
|
||||
void __iomem *membase;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
u32 major; /* Device id & Major version*/
|
||||
u32 minor; /* Minor version */
|
||||
|
||||
u32 optic_cpld_major; /* optic cpld major version */
|
||||
u32 optic_cpld_minor; /* optic cpld minor version */
|
||||
u32 optic_cpld_devid; /* optic cpld device id */
|
||||
};
|
||||
|
||||
/* sysfs entries */
|
||||
static ssize_t major_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "0x%02X_%06X\n",
|
||||
(tmc->major >> 24) & 0xff,
|
||||
tmc->major & 0xffffff);
|
||||
}
|
||||
|
||||
static ssize_t minor_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%02X\n", (tmc->minor) & 0xff);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_major_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%01X\n", tmc->optic_cpld_major & 0xf);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_devid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%01X\n",
|
||||
(tmc->optic_cpld_major >> 4) & 0xf);
|
||||
}
|
||||
|
||||
static ssize_t optic_cpld_minor_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%02X\n", tmc->optic_cpld_minor & 0xff);
|
||||
}
|
||||
|
||||
static ssize_t set_sys_shutdown(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Unlock the shutdown register */
|
||||
iowrite32(0x12345678, tmc->membase + TMC_SYS_SHUTDOWN_LOCK);
|
||||
iowrite32(0x1, tmc->membase + TMC_SYS_SHUTDOWN);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR(major, S_IRUGO, major_show, NULL);
|
||||
static DEVICE_ATTR(minor, S_IRUGO, minor_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_major, S_IRUGO, optic_cpld_major_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_devid, S_IRUGO, optic_cpld_devid_show, NULL);
|
||||
static DEVICE_ATTR(optic_cpld_minor, S_IRUGO, optic_cpld_minor_show, NULL);
|
||||
static DEVICE_ATTR(shutdown, S_IWUSR, NULL, set_sys_shutdown);
|
||||
|
||||
static struct attribute *tmc_attrs[] = {
|
||||
&dev_attr_major.attr,
|
||||
&dev_attr_minor.attr,
|
||||
&dev_attr_optic_cpld_major.attr,
|
||||
&dev_attr_optic_cpld_devid.attr,
|
||||
&dev_attr_optic_cpld_minor.attr,
|
||||
&dev_attr_shutdown.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group tmc_attr_group = {
|
||||
.attrs = tmc_attrs,
|
||||
};
|
||||
|
||||
#if defined TMC_DO_SCRATCH_TEST
|
||||
/* Do a quick scratch access test */
|
||||
static int tmc_do_test_scratch(struct tmc_fpga_data *tmc)
|
||||
{
|
||||
struct pci_dev *pdev = tmc->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
int offset = TMC_SCRATCH;
|
||||
u32 acc, val = 0xdeadbeaf;
|
||||
|
||||
/*
|
||||
* Check rw register access -> use the scratch reg.
|
||||
*/
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
acc = ioread32(tmc->membase + offset);
|
||||
if (acc != val) {
|
||||
dev_err(dev, "Tmc scratch(0x%x) failed: %08x.%08x!\n",
|
||||
offset, val, acc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (val = 0; val < 0xf0000000; val += 0x01010101) {
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
acc = ioread32(tmc->membase + offset);
|
||||
if (acc != val) {
|
||||
dev_err(dev, "Tmc scratch(0x%x) failed: %08x.%08x!\n",
|
||||
offset, val, acc);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a sig before leaving..
|
||||
*/
|
||||
val = 0xcafebabe;
|
||||
iowrite32(val, tmc->membase + offset);
|
||||
dev_dbg(dev, "Tmc scratch result: 0x%08x\n",
|
||||
ioread32(tmc->membase + offset));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TMC_DO_SCRATCH_TEST */
|
||||
|
||||
static int tmc_fpga_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int err;
|
||||
struct tmc_fpga_data *tmc;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
dev_dbg(dev, "Tmc FPGA Probe called\n");
|
||||
|
||||
tmc = devm_kzalloc(dev, sizeof(*tmc), GFP_KERNEL);
|
||||
if (!tmc)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to enable device %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pcim_iomap_regions(pdev, 1 << 0, "tmc-core");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to iomap regions %d\n", err);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
tmc->membase = pcim_iomap_table(pdev)[0];
|
||||
if (IS_ERR(tmc->membase)) {
|
||||
dev_err(dev, "pci_ioremap_bar() failed\n");
|
||||
err = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
tmc->pdev = pdev;
|
||||
pci_set_drvdata(pdev, tmc);
|
||||
|
||||
/* All Tmc uses MSI interrupts - enable bus mastering */
|
||||
pci_set_master(pdev);
|
||||
|
||||
#if defined TMC_DO_SCRATCH_TEST
|
||||
/* Check IO before proceeding */
|
||||
dev_dbg(dev, "Tmc FPGA starting scratch test\n");
|
||||
err = tmc_do_test_scratch(tmc);
|
||||
if (err)
|
||||
goto err_unmap;
|
||||
|
||||
dev_dbg(dev, "Tmc FPGA scratch test passed !!!\n");
|
||||
#endif /* TMC_DO_SCRATCH_TEST */
|
||||
|
||||
switch (id->device) {
|
||||
case PCI_DEVICE_ID_JNX_TMC_CHD:
|
||||
err = mfd_add_devices(dev, pdev->bus->number,
|
||||
&chassisd_tmc_mfd_devs[0],
|
||||
ARRAY_SIZE(chassisd_tmc_mfd_devs),
|
||||
&pdev->resource[0],
|
||||
0, NULL /* tmc->irq_domain */);
|
||||
break;
|
||||
case PCI_DEVICE_ID_JNX_TMC_PFE:
|
||||
err = mfd_add_devices(dev, pdev->bus->number,
|
||||
&pfe_tmc_mfd_devs[0],
|
||||
ARRAY_SIZE(pfe_tmc_mfd_devs),
|
||||
&pdev->resource[0],
|
||||
0, NULL /* tmc->irq_domain */);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid PCI Device ID id:%d\n",
|
||||
id->device);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Failed to add mfd devices %d\n", err);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
if (err) {
|
||||
sysfs_remove_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
dev_err(&pdev->dev, "Failed to create attr group\n");
|
||||
goto err_remove_mfd;
|
||||
}
|
||||
|
||||
tmc->major = ioread32(tmc->membase + TMC_REVISION);
|
||||
tmc->minor = ioread32(tmc->membase + TMC_MINOR);
|
||||
|
||||
tmc->optic_cpld_major = ioread32(tmc->membase + TMC_OPTIC_CPLD_MAJOR);
|
||||
tmc->optic_cpld_minor = ioread32(tmc->membase + TMC_OPTIC_CPLD_MINOR);
|
||||
|
||||
dev_info(dev, "Tmc FPGA Revision: 0x%02X_%06X, Minor: %02X\n",
|
||||
(tmc->major >> 24) & 0xff,
|
||||
tmc->major & 0xffffff,
|
||||
(tmc->minor) & 0xff);
|
||||
dev_info(dev, "Tmc FPGA optic cpld Major: 0x%01X, Minor: 0x%02X "
|
||||
"Devid: 0x%01X\n", (tmc->optic_cpld_major) & 0xf,
|
||||
(tmc->optic_cpld_minor) & 0xff,
|
||||
(tmc->optic_cpld_major >> 4) & 0xf);
|
||||
dev_info(dev, "Tmc FPGA mem:0x%lx\n",
|
||||
(unsigned long)tmc->membase);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_mfd:
|
||||
mfd_remove_devices(dev);
|
||||
err_unmap:
|
||||
pci_iounmap(pdev, tmc->membase);
|
||||
err_release:
|
||||
pci_release_regions(pdev);
|
||||
err_disable:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tmc_fpga_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct tmc_fpga_data *tmc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &tmc_attr_group);
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id tmc_fpga_id_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TMC_CHD) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TMC_PFE) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, tmc_fpga_id_tbl);
|
||||
|
||||
static struct pci_driver tmc_fpga_driver = {
|
||||
.name = "tmc-core",
|
||||
.id_table = tmc_fpga_id_tbl,
|
||||
.probe = tmc_fpga_probe,
|
||||
.remove = tmc_fpga_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(tmc_fpga_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Juniper Networks TMC FPGA MFD core driver");
|
||||
MODULE_AUTHOR("Ashish Bhensdadia <bashish@juniper.net>");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1 @@
|
||||
../../common/modules/jnx-tmc-core.c
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Juniper Tmc FPGA register definitions
|
||||
*
|
||||
* Copyright (C) 2018 Juniper Networks
|
||||
* Author: Ashish Bhensdadia <bashish@juniper.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __JNX_TMC_H__
|
||||
#define __JNX_TMC_H__
|
||||
|
||||
|
||||
#define TMC_REVISION 0x00064
|
||||
#define TMC_MINOR 0x00068
|
||||
#define TMC_SCRATCH 0x00098
|
||||
|
||||
#define TMC_OPTIC_CPLD_MAJOR 0x00104
|
||||
#define TMC_OPTIC_CPLD_MINOR 0x00108
|
||||
|
||||
/*
|
||||
* I2C Master Block
|
||||
*/
|
||||
#define TMC_I2C_AUTOMATION_I2C_CONTROL_START 0x07000
|
||||
#define TMC_I2C_AUTOMATION_I2C_CONTROL_END 0x07500
|
||||
|
||||
#define TMC_I2C_DPMEM_ENTRY_START 0x10000
|
||||
#define TMC_I2C_DPMEM_ENTRY_END 0x13FFC
|
||||
|
||||
#define TMC_LED_CONTROL_START 0x58
|
||||
#define TMC_LED_CONTROL_END 0x5B
|
||||
|
||||
/*
|
||||
* RE-FPGA block
|
||||
*/
|
||||
#define TMC_REFPGA_ACCESS_START 0x228
|
||||
#define TMC_REFPGA_ACCESS_END 0x233
|
||||
|
||||
#define TMC_I2C_MASTER_NR_MSTRS 16
|
||||
#define TMC_I2C_MSTR_MAX_GROUPS 66
|
||||
|
||||
|
||||
/*
|
||||
* TMC GPIO SLAVE Block
|
||||
*/
|
||||
#define TMC_GPIO_PTP_RESET_START 0x94
|
||||
#define TMC_GPIO_PTP_RESET_END 0x97
|
||||
|
||||
#define TMC_GPIO_PTP_CFG_START 0xa4
|
||||
#define TMC_GPIO_PTP_CFG_END 0xa7
|
||||
|
||||
#define TMC_GPIO_PTP_DATA_START 0xa8
|
||||
#define TMC_GPIO_PTP_DATA_END 0xab
|
||||
|
||||
#define TMC_GPIO_SLAVE0_START 0xf0
|
||||
#define TMC_GPIO_SLAVE0_END 0x16b
|
||||
|
||||
#define TMC_GPIO_SLAVE1_START 0x170
|
||||
#define TMC_GPIO_SLAVE1_END 0x1eb
|
||||
|
||||
#define TMC_GPIO_SLAVE2_START 0x1f0
|
||||
#define TMC_GPIO_SLAVE2_END 0x213
|
||||
|
||||
#define TMC_GPIO_SLAVE3_START 0x280
|
||||
#define TMC_GPIO_SLAVE3_END 0x2eb
|
||||
|
||||
#define TMC_GPIO_SFP_SLAVE0_START 0x308
|
||||
#define TMC_GPIO_SFP_SLAVE0_END 0x32b
|
||||
|
||||
#define TMC_GPIO_SFP_SLAVE1_START 0x32c
|
||||
#define TMC_GPIO_SFP_SLAVE1_END 0x34b
|
||||
|
||||
/*
|
||||
* TMC PSU Block
|
||||
*/
|
||||
#define TMC_PSU_START 0x240
|
||||
#define TMC_PSU_END 0x243
|
||||
|
||||
/*
|
||||
* TMC SHUTDOWN REG
|
||||
*/
|
||||
#define TMC_SYS_SHUTDOWN_LOCK 0x254
|
||||
#define TMC_SYS_SHUTDOWN 0x250
|
||||
|
||||
/*
|
||||
* TMC DS100 MUX Block
|
||||
*/
|
||||
#define TMC_GPIO_MUX_SLAVE_START 0x26c
|
||||
#define TMC_GPIO_MUX_SLAVE_END 0x26f
|
||||
|
||||
#endif /* __JNX_TMC_H__ */
|
@ -0,0 +1 @@
|
||||
../../common/modules/jnx-tmc.h
|
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Juniper PCI ID(s) - for devices on Juniper Boards
|
||||
*
|
||||
* Rajat Jain <rajatjain@juniper.net>
|
||||
* Copyright 2014 Juniper Networks
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __JNX_PCI_IDS_H__
|
||||
#define __JNX_PCI_IDS_H__
|
||||
|
||||
#define PCI_VENDOR_ID_JUNIPER 0x1304
|
||||
#define PCI_VENDOR_ID_ERICSSON 0x1519
|
||||
#define PCI_VENDOR_ID_ERICSSON_AS 0x1a25
|
||||
|
||||
/*
|
||||
* PTX SAM FPGA, device ID as present on various Juniper boards, such as
|
||||
* - Sangria FPC
|
||||
* - Hendricks FPC
|
||||
* - Sangria 24x10GE PIC
|
||||
* - Gladiator FPC
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_SAM 0x0004
|
||||
|
||||
/* Juniper Broadway ASIC family */
|
||||
#define PCI_DEVICE_ID_JNX_TF 0x003c
|
||||
#define PCI_DEVICE_ID_JNX_TL 0x003d
|
||||
#define PCI_DEVICE_ID_JNX_TQ 0x003e
|
||||
#define PCI_DEVICE_ID_JNX_OTN_FRAMER 0x0055
|
||||
#define PCI_DEVICE_ID_JNX_PE 0x005e
|
||||
#define PCI_DEVICE_ID_JNX_PF 0x005f /* Juniper Paradise ASIC */
|
||||
#define PCI_DEVICE_ID_JNX_ZF 0x008d /* Juniper ZF Fabric ASIC */
|
||||
#define PCI_DEVICE_ID_JNX_ZX 0x008e /* Juniper ZX ASIC */
|
||||
#define PCI_DEVICE_ID_JNX_ZT 0x0090 /* Juniper ZT ASIC */
|
||||
#define PCI_DEVICE_ID_JNX_BT 0x00B2 /* Juniper BT ASIC */
|
||||
|
||||
/* Juniper SAM FPGA - Omega SIB, Sochu SHAM, Gladiator SIB */
|
||||
#define PCI_DEVICE_ID_JNX_SAM_OMEGA 0x006a
|
||||
|
||||
/* Juniper SAM FPGA - present on GLD FPC board */
|
||||
#define PCI_DEVICE_ID_JNX_SAM_X 0x006b
|
||||
|
||||
/* Juniper PAM FPGA - present on PTX MLC board */
|
||||
#define PCI_DEVICE_ID_JNX_PAM 0x006c
|
||||
/* Juniper CBC FPGA - present on PTX1K RCB */
|
||||
#define PCI_DEVICE_ID_JNX_CBC 0x006e
|
||||
#define PCI_DEVICE_ID_JNX_CBC_P2 0x0079
|
||||
#define PCI_DEVICE_ID_JNX_OMG_CBC 0x0083
|
||||
|
||||
/* Juniper Summit FPGA */
|
||||
#define PCI_DEVICE_ID_JNX_SUMMIT 0x009B
|
||||
|
||||
/* Juniper DOON FPGA */
|
||||
#define PCI_DEVICE_ID_JNX_DOON_RCB_CBC 0x0098
|
||||
|
||||
/* Juniper CBC FPGA in PTX-5K MTRCB */
|
||||
#define PCI_DEVICE_ID_JNX_PTX5K_MTRCB_CBC 0x0071
|
||||
|
||||
/* Other Vendors' devices */
|
||||
#define PCI_DEVICE_ID_IDT_PES12NT3_TRANS_AB 0x8058
|
||||
#define PCI_DEVICE_ID_IDT_PES12NT3_TRANS_C 0x8059
|
||||
#define PCI_DEVICE_ID_IDT_PES12NT3_INT_NTB_C 0x805a
|
||||
#define PCI_DEVICE_ID_IDT_48H12G2 0x807a
|
||||
#define PCI_DEVICE_ID_IDT_PES24NT24G2 0x808e
|
||||
#define PCI_DEVICE_ID_IDT_PES16NT16G2 0x8090
|
||||
|
||||
#define PCI_DEVICE_ID_PLX_8614 0x8614
|
||||
#define PCI_DEVICE_ID_PLX_8618 0x8618
|
||||
#define PCI_DEVICE_ID_PLX_8713 0x8713
|
||||
#define PCI_DEVICE_ID_PLX_8725 0x8725
|
||||
#define PCI_DEVICE_ID_PLX_8749 0x8749
|
||||
#define PCI_DEVICE_ID_PLX_8796 0x8796
|
||||
#define PCI_DEVICE_ID_PLX_8608 0x8608
|
||||
|
||||
/*
|
||||
* Juniper CBD FPGA Device ID(s)
|
||||
*/
|
||||
#define JNX_CBD_FPGA_DID_09B3 0x004D
|
||||
#define JNX_CBD_FPGA_DID_0BA8 0x005A
|
||||
|
||||
/*
|
||||
* Juniper Brackla FPGA Device IDs
|
||||
* - UBAM, MBAM, PBAM, QBAM
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_UBAM 0x00A7
|
||||
#define PCI_DEVICE_ID_JNX_PBAM 0x00A8
|
||||
#define PCI_DEVICE_ID_JNX_MBAM 0x00A9
|
||||
#define PCI_DEVICE_ID_JNX_QBAM 0x00AA
|
||||
|
||||
/*
|
||||
* Juniper MPC11E Supercon and WAN FPGA IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_MPC11CON 0x00A1
|
||||
#define PCI_DEVICE_ID_JNX_MPC11WAN 0x00C4
|
||||
|
||||
/*
|
||||
* Juniper Attella TMC and Supercon FPGA IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_ARGUS 0x00B0
|
||||
#define PCI_DEVICE_ID_JNX_ATIC 0x00C0
|
||||
#define PCI_DEVICE_ID_JNX_ATMC_CHD 0x00C1
|
||||
#define PCI_DEVICE_ID_JNX_ATMC_PFE 0x00C2
|
||||
#define PCI_DEVICE_ID_JNX_AOHIO 0x00C3
|
||||
|
||||
/*
|
||||
* Juniper TMC FPGA Device IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_TMC_CHD 0x007B
|
||||
#define PCI_DEVICE_ID_JNX_TMC_PFE 0x007C
|
||||
|
||||
#define PCI_DEVICE_ID_XILINX_1588_FPGA 0x0505
|
||||
|
||||
/*
|
||||
* Juniper Scapa SIB/LC Supercon FPGA IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_SCAPA_SIB_CTRL 0x00BA
|
||||
#define PCI_DEVICE_ID_JNX_SDLC_CTRL 0x00BE
|
||||
#define PCI_DEVICE_ID_JNX_LLC_CTRL 0x00C8
|
||||
|
||||
/*
|
||||
* Deanston WANIO FPGA
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_DEANSTON_WAN 0x00C6
|
||||
|
||||
/*
|
||||
* Juniper Ardbeg Supercon FPGA IDs
|
||||
*/
|
||||
#define PCI_DEVICE_ID_JNX_ARDBEG_CTRL 0x00C5
|
||||
|
||||
/*
|
||||
* Ericsson CCM FPGA ID used in Bolan (ACX753)
|
||||
*/
|
||||
#define PCI_DEVICE_ID_ERIC_CCM_FPGA 0x0020
|
||||
|
||||
/*
|
||||
* Ericsson OAM FPGA ID used in Bolan (ACX753)
|
||||
*/
|
||||
#define PCI_DEVICE_ID_ERIC_OAM_FPGA 0x7021
|
||||
|
||||
#endif /* __JNX_PCI_IDS_H__ */
|
0
platform/broadcom/sonic-platform-modules-juniper/qfx5200/service/qfx5200-platform-init.service
Executable file → Normal file
0
platform/broadcom/sonic-platform-modules-juniper/qfx5200/service/qfx5200-platform-init.service
Executable file → Normal file
@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
from setuptools import setup
|
||||
os.listdir
|
||||
|
||||
setup(
|
||||
name='sonic_platform',
|
||||
version='1.0',
|
||||
description='Module to initialize Juniper QFX5200-32C-S platforms',
|
||||
|
||||
packages=['sonic_platform'],
|
||||
package_dir={'sonic_platform': 'qfx5200/sonic_platform'},
|
||||
)
|
||||
|
@ -1,260 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Name: chassis.py, version: 1.0
|
||||
#
|
||||
# Description: Module contains the definitions of SONiC platform APIs
|
||||
# which provide the chassis specific details
|
||||
#
|
||||
# Copyright (c) 2020, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Notice and Disclaimer: This code is licensed to you under the GNU General
|
||||
# Public License as published by the Free Software Foundation, version 3 or
|
||||
# any later version. This code is not an official Juniper product. You can
|
||||
# obtain a copy of the License at <https://www.gnu.org/licenses/>
|
||||
#
|
||||
# OSS License:
|
||||
#
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Third-Party Code: This code may depend on other components under separate
|
||||
# copyright notice and license terms. Your use of the source code for those
|
||||
# components is subject to the terms and conditions of the respective license
|
||||
# as noted in the Third-Party source code file.
|
||||
#
|
||||
|
||||
try:
|
||||
import commands
|
||||
import time
|
||||
from sonic_platform_base.chassis_base import ChassisBase
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Chassis(ChassisBase):
|
||||
"""
|
||||
JUNIPER QFX5200 Platform-specific Chassis class
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ChassisBase.__init__(self)
|
||||
|
||||
def get_qfx5200_parameter_value(self,parameter_name):
|
||||
try:
|
||||
with open("/var/run/eeprom", "r") as file:
|
||||
for item in file:
|
||||
content = item.split('=')
|
||||
if content[0] == parameter_name:
|
||||
return content[1:]
|
||||
return "False"
|
||||
except IOError:
|
||||
print "Error: File not found"
|
||||
return "False"
|
||||
|
||||
def get_product_name(self):
|
||||
product_name_list = self.get_qfx5200_parameter_value('Product Name')
|
||||
if product_name_list:
|
||||
product_name = ''.join(product_name_list)
|
||||
return product_name
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_part_number(self):
|
||||
part_number_list = self.get_qfx5200_parameter_value('Part Number')
|
||||
if part_number_list:
|
||||
part_number = ''.join(part_number_list)
|
||||
return part_number
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_serial_number(self):
|
||||
serial_number_list = self.get_qfx5200_parameter_value('Serial Number')
|
||||
if serial_number_list:
|
||||
serial_number = ''.join(serial_number_list)
|
||||
return serial_number
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_base_mac(self):
|
||||
mac_list = self.get_qfx5200_parameter_value('MAC Address')
|
||||
if mac_list:
|
||||
mac = ''.join(mac_list)
|
||||
return mac
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_mfg_date(self):
|
||||
mfgdate_list = self.get_qfx5200_parameter_value('Manufacture Date')
|
||||
if mfgdate_list:
|
||||
mfgdate = ''.join(mfgdate_list)
|
||||
return mfgdate
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_platform_name(self):
|
||||
platform_name_list = self.get_qfx5200_parameter_value('Platform Name')
|
||||
if platform_name_list:
|
||||
platform_name = ''.join(platform_name_list)
|
||||
return platform_name
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_MACnumber_name(self):
|
||||
MACnumber_name_list = self.get_qfx5200_parameter_value('Number of MAC Addresses')
|
||||
if MACnumber_name_list:
|
||||
MACnumber_name = ''.join(MACnumber_name_list)
|
||||
return MACnumber_name
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_vendor_name(self):
|
||||
vendor_name_list = self.get_qfx5200_parameter_value('Vendor Name')
|
||||
if vendor_name_list:
|
||||
vendor_name = ''.join(vendor_name_list)
|
||||
return vendor_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_mfg_name(self):
|
||||
mfg_name_list = self.get_qfx5200_parameter_value('Manufacture Name')
|
||||
if mfg_name_list:
|
||||
mfg_name = ''.join(mfg_name_list)
|
||||
return mfg_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorext_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Vendor Extension')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextIANA_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('IANA')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextASMREV_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Assembly Part Number Revision')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextASMPartNum_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Assembly Part Number')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextASMID_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Assembly ID')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextASMMajNum_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Assembly Major Revision')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextASMMinNum_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('Assembly Minor Revision')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_vendorextCLEI_name(self):
|
||||
vendorext_list = self.get_qfx5200_parameter_value('CLEI code')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_onieversion_name(self):
|
||||
onieversion_name_list = self.get_qfx5200_parameter_value('ONIE Version')
|
||||
if onieversion_name_list:
|
||||
onieversion_name = ''.join(onieversion_name_list)
|
||||
return onieversion_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_crc_name(self):
|
||||
crc_list = self.get_qfx5200_parameter_value('CRC')
|
||||
if crc_list:
|
||||
crc_name = ''.join(crc_list)
|
||||
return crc_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_fan_type(self):
|
||||
fantype_list = self.get_qfx5200_parameter_value('Fan Type')
|
||||
if fantype_list:
|
||||
fantype_name = ''.join(fantype_list)
|
||||
return fantype_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_reboot_cause(self):
|
||||
"""
|
||||
Retrieves the cause of the previous reboot
|
||||
"""
|
||||
status, last_reboot_reason = commands.getstatusoutput("busybox devmem 0xFED50004 8")
|
||||
if (status == 0):
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
else:
|
||||
time.sleep(3)
|
||||
status, last_reboot_reason = commands.getstatusoutput("busybox devmem 0xFED50004 8")
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
@ -1 +0,0 @@
|
||||
../../common/modules/juniper_i2c_cpld.c
|
@ -0,0 +1,892 @@
|
||||
/*
|
||||
* A hwmon driver for the juniper_i2c_cpld
|
||||
*
|
||||
* Tested and validated on Juniper QFX5210
|
||||
* Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* Copyright (C) 2013 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
|
||||
#define MAX_PORT_NUM 64
|
||||
#define I2C_RW_RETRY_COUNT 10
|
||||
#define I2C_RW_RETRY_INTERVAL 60 /* ms */
|
||||
|
||||
#define I2C_ADDR_CPLD1 0x60
|
||||
#define I2C_ADDR_CPLD2 0x62
|
||||
#define I2C_ADDR_CPLD3 0x64
|
||||
#define CPLD_ADDRS {I2C_ADDR_CPLD1, I2C_ADDR_CPLD2, I2C_ADDR_CPLD3}
|
||||
|
||||
|
||||
/*
|
||||
* Number of additional attribute pointers to allocate
|
||||
* with each call to krealloc
|
||||
*/
|
||||
#define ATTR_ALLOC_SIZE 1 /*For last attribute which is NUll.*/
|
||||
|
||||
#define NAME_SIZE 24
|
||||
#define MAX_RESP_LENGTH 48
|
||||
|
||||
typedef ssize_t (*show_func)( struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
typedef ssize_t (*store_func)(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
enum models {
|
||||
AS7712_32X,
|
||||
AS7716_32X,
|
||||
qfx5210_64X,
|
||||
AS7312_54X,
|
||||
PLAIN_CPLD, /*No attribute but add i2c addr to the list.*/
|
||||
NUM_MODEL
|
||||
};
|
||||
|
||||
enum sfp_func {
|
||||
HAS_SFP = 1<<0 ,
|
||||
HAS_QSFP = 1<<1 ,
|
||||
};
|
||||
|
||||
enum common_attrs {
|
||||
CMN_VERSION,
|
||||
CMN_ACCESS,
|
||||
CMN_PRESENT_ALL,
|
||||
NUM_COMMON_ATTR
|
||||
};
|
||||
|
||||
enum sfp_attrs {
|
||||
SFP_PRESENT,
|
||||
SFP_RESET,
|
||||
SFP_LP_MODE,
|
||||
NUM_SFP_ATTR
|
||||
};
|
||||
|
||||
struct cpld_sensor {
|
||||
struct cpld_sensor *next;
|
||||
char name[NAME_SIZE+1]; /* sysfs sensor name */
|
||||
struct device_attribute attribute;
|
||||
bool update; /* runtime sensor update needed */
|
||||
int data; /* Sensor data. Negative if there was a read error */
|
||||
|
||||
u8 reg; /* register */
|
||||
u8 mask; /* bit mask */
|
||||
bool invert; /* inverted value*/
|
||||
|
||||
};
|
||||
|
||||
#define to_cpld_sensor(_attr) \
|
||||
container_of(_attr, struct cpld_sensor, attribute)
|
||||
|
||||
struct cpld_data {
|
||||
struct device *dev;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
int num_attributes;
|
||||
struct attribute_group group;
|
||||
|
||||
enum models model;
|
||||
struct cpld_sensor *sensors;
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
int attr_index;
|
||||
u16 sfp_num;
|
||||
u8 sfp_types;
|
||||
struct model_attrs *cmn_attr;
|
||||
};
|
||||
|
||||
struct cpld_client_node {
|
||||
struct i2c_client *client;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
||||
struct base_attrs {
|
||||
const char *name;
|
||||
umode_t mode;
|
||||
show_func get;
|
||||
store_func set;
|
||||
};
|
||||
|
||||
struct attrs {
|
||||
int reg;
|
||||
bool invert;
|
||||
struct base_attrs *base;
|
||||
};
|
||||
|
||||
struct model_attrs {
|
||||
struct attrs **cmn;
|
||||
struct attrs **portly;
|
||||
};
|
||||
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf);
|
||||
static ssize_t show_presnet_all(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf);
|
||||
static ssize_t set_1bit(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t set_byte(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
|
||||
int juniper_i2c_cpld_read(u8 cpld_addr, u8 reg);
|
||||
int juniper_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value);
|
||||
|
||||
|
||||
struct base_attrs common_attrs[NUM_COMMON_ATTR] =
|
||||
{
|
||||
[CMN_VERSION] = {"version", S_IRUGO, show_bit, NULL},
|
||||
[CMN_ACCESS] = {"access", S_IWUSR, NULL, set_byte},
|
||||
[CMN_PRESENT_ALL] = {"module_present_all", S_IRUGO, show_presnet_all, NULL},
|
||||
};
|
||||
|
||||
struct attrs as7712_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs qfx5210_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {0x30, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs as7312_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
[CMN_ACCESS] = {0x00, false, &common_attrs[CMN_ACCESS]},
|
||||
[CMN_PRESENT_ALL] = {-1, false, &common_attrs[CMN_PRESENT_ALL]},
|
||||
};
|
||||
struct attrs plain_common[] = {
|
||||
[CMN_VERSION] = {0x01, false, &common_attrs[CMN_VERSION]},
|
||||
};
|
||||
|
||||
struct base_attrs portly_attrs[] =
|
||||
{
|
||||
[SFP_PRESENT] = {"module_present", S_IRUGO, show_bit, NULL},
|
||||
// Only root user will have the privilege to write to sysfs
|
||||
// [SFP_RESET] = {"module_reset", S_IRUGO|S_IWUGO, show_bit, set_1bit},
|
||||
[SFP_RESET] = {"module_reset", S_IRUGO|S_IWUSR, show_bit, set_1bit},
|
||||
};
|
||||
|
||||
struct attrs as7712_port[] = {
|
||||
{0x30, true, &portly_attrs[SFP_PRESENT]},
|
||||
{0x04, true, &portly_attrs[SFP_RESET]},
|
||||
};
|
||||
|
||||
struct attrs qfx5210_port[] = {
|
||||
{0x70, true, &portly_attrs[SFP_PRESENT]},
|
||||
{0x40, true, &portly_attrs[SFP_RESET]},
|
||||
};
|
||||
|
||||
struct attrs *as7712_cmn_list[] = {
|
||||
&as7712_common[CMN_VERSION],
|
||||
&as7712_common[CMN_ACCESS],
|
||||
&as7712_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *qfx5210_cmn_list[] = {
|
||||
&qfx5210_common[CMN_VERSION],
|
||||
&qfx5210_common[CMN_ACCESS],
|
||||
&qfx5210_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *as7312_cmn_list[] = {
|
||||
&as7312_common[CMN_VERSION],
|
||||
&as7312_common[CMN_ACCESS],
|
||||
&as7312_common[CMN_PRESENT_ALL],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *plain_cmn_list[] = {
|
||||
&plain_common[CMN_VERSION],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attrs *as7712_port_list[] = {
|
||||
&as7712_port[SFP_PRESENT],
|
||||
&as7712_port[SFP_RESET],
|
||||
NULL
|
||||
};
|
||||
struct attrs *qfx5210_port_list[] = {
|
||||
&qfx5210_port[SFP_PRESENT],
|
||||
&qfx5210_port[SFP_RESET],
|
||||
NULL
|
||||
};
|
||||
|
||||
struct model_attrs models_attr[NUM_MODEL] = {
|
||||
{.cmn = as7712_cmn_list, .portly=as7712_port_list},
|
||||
{.cmn = as7712_cmn_list, .portly=as7712_port_list}, /*7716's as 7712*/
|
||||
{.cmn = qfx5210_cmn_list, .portly=qfx5210_port_list},
|
||||
{.cmn = as7312_cmn_list, .portly=qfx5210_port_list},
|
||||
{.cmn = plain_cmn_list, .portly=NULL},
|
||||
};
|
||||
|
||||
static LIST_HEAD(cpld_client_list);
|
||||
static struct mutex list_lock;
|
||||
/* Addresses scanned for juniper_i2c_cpld
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||
|
||||
static int get_sfp_spec(int model, u16 *num, u8 *types)
|
||||
{
|
||||
switch (model) {
|
||||
case AS7712_32X:
|
||||
case AS7716_32X:
|
||||
*num = 32;
|
||||
*types = HAS_QSFP;
|
||||
break;
|
||||
case qfx5210_64X:
|
||||
*num = 64;
|
||||
*types = HAS_QSFP;
|
||||
break;
|
||||
case AS7312_54X:
|
||||
*num = 54;
|
||||
*types = HAS_QSFP|HAS_SFP;
|
||||
default:
|
||||
*types = 0;
|
||||
*num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_present_reg(int model, u8 port, u8 *cpld_addr, u8 *reg, u8 *num)
|
||||
{
|
||||
u8 cpld_address[] = CPLD_ADDRS;
|
||||
|
||||
switch (model) {
|
||||
case AS7312_54X:
|
||||
if (port < 48) {
|
||||
*cpld_addr = cpld_address[1 + port/24];
|
||||
*reg = 0x09 + (port%24)/8;
|
||||
*num = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
*reg = 0x18;
|
||||
*num = 4;
|
||||
*cpld_addr = ( port < 52)? cpld_address[1]: cpld_address[2];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*Assume the bits for ports are listed in-a-row.*/
|
||||
static int get_reg_bit(u8 reg_start, int port,
|
||||
u8 *reg ,u8 *mask)
|
||||
{
|
||||
*reg = reg_start + ((port)/8);
|
||||
*mask = 1 << ((port)%8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpld_write_internal(
|
||||
struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_write_byte_data(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int cpld_read_internal(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int status = 0, retry = I2C_RW_RETRY_COUNT;
|
||||
|
||||
while (retry) {
|
||||
status = i2c_smbus_read_byte_data(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
msleep(I2C_RW_RETRY_INTERVAL);
|
||||
retry--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*Turn a numberic array into string with " " between each element.
|
||||
* e.g., {0x11, 0x33, 0xff, 0xf1} => "11 33 ff f1"
|
||||
*/
|
||||
static ssize_t array_stringify(char *buf, u8 *input, size_t size) {
|
||||
|
||||
int i;
|
||||
char t[MAX_RESP_LENGTH+1];
|
||||
|
||||
buf[0] = '\0';
|
||||
for (i = 0; i < size; i++) {
|
||||
snprintf(t, MAX_RESP_LENGTH, "%x ", input[i]);
|
||||
strncat(buf, t, MAX_RESP_LENGTH);
|
||||
}
|
||||
|
||||
if (strlen(buf) > 0)
|
||||
buf[strlen(buf)-1] = '\0'; /*Remove tailing blank*/
|
||||
|
||||
return snprintf(buf, MAX_RESP_LENGTH, "%s\n", buf);
|
||||
}
|
||||
|
||||
static ssize_t show_presnet_all_distinct(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
u8 i, value, reg;
|
||||
u8 cpld_addr, num;
|
||||
u8 _value[8];
|
||||
u64 *values = (u64 *)_value;
|
||||
|
||||
values = 0;
|
||||
mutex_lock(&data->update_lock);
|
||||
while(i < data->sfp_num)
|
||||
{
|
||||
get_present_reg(data->model, i, &cpld_addr, ®, &num);
|
||||
if(cpld_addr == client->addr)
|
||||
value = cpld_read_internal(client, reg);
|
||||
else
|
||||
value = juniper_i2c_cpld_read(cpld_addr, reg);
|
||||
|
||||
if (unlikely(value < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
*values |= (value&((1<<(num))-1)) << i;
|
||||
i += num;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
*values = cpu_to_le64(*values);
|
||||
return array_stringify(buf, _value, i);
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return value;
|
||||
}
|
||||
|
||||
static ssize_t show_presnet_all(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
u8 i, values[MAX_RESP_LENGTH/8];
|
||||
|
||||
if (sensor->reg < 0) {
|
||||
return show_presnet_all_distinct(dev, devattr, buf);
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
for (i = 0; i < ((data->sfp_num+7)/8); i++) {
|
||||
values[i] = cpld_read_internal(client, sensor->reg + i);
|
||||
if (unlikely(values[i] < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return array_stringify(buf, values, i);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return values[i];
|
||||
}
|
||||
|
||||
static ssize_t show_bit(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int value;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
value = cpld_read_internal(client, sensor->reg);
|
||||
value = value & sensor->mask;
|
||||
if (sensor->invert)
|
||||
value = !value;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", value);
|
||||
}
|
||||
|
||||
static ssize_t set_1bit(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
long is_reset;
|
||||
int value, status;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
struct cpld_sensor *sensor = to_cpld_sensor(devattr);
|
||||
u8 cpld_bit, reg;
|
||||
|
||||
status = kstrtol(buf, 10, &is_reset);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
reg = sensor->reg;
|
||||
cpld_bit = sensor->mask;
|
||||
mutex_lock(&data->update_lock);
|
||||
value = cpld_read_internal(client, reg);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (sensor->invert)
|
||||
is_reset = !is_reset;
|
||||
|
||||
if (is_reset) {
|
||||
value |= cpld_bit;
|
||||
}
|
||||
else {
|
||||
value &= ~cpld_bit;
|
||||
}
|
||||
|
||||
status = cpld_write_internal(client, reg, value);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t set_byte(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return access(dev, da, buf, count);
|
||||
}
|
||||
|
||||
static ssize_t access(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int status;
|
||||
u32 addr, val;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cpld_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (sscanf(buf, "0x%x 0x%x", &addr, &val) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr > 0xFF || val > 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
status = cpld_write_internal(client, addr, val);
|
||||
if (unlikely(status < 0)) {
|
||||
goto exit;
|
||||
}
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void juniper_i2c_cpld_add_client(struct i2c_client *client)
|
||||
{
|
||||
struct cpld_client_node *node =
|
||||
kzalloc(sizeof(struct cpld_client_node), GFP_KERNEL);
|
||||
|
||||
if (!node) {
|
||||
dev_dbg(&client->dev, "Can't allocate cpld_client_node (0x%x)\n",
|
||||
client->addr);
|
||||
return;
|
||||
}
|
||||
node->client = client;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
list_add(&node->list, &cpld_client_list);
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static void juniper_i2c_cpld_remove_client(struct i2c_client *client)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client == client) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
list_del(list_node);
|
||||
kfree(cpld_node);
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
}
|
||||
|
||||
static int cpld_add_attribute(struct cpld_data *data, struct attribute *attr)
|
||||
{
|
||||
int new_max_attrs = ++data->num_attributes + ATTR_ALLOC_SIZE;
|
||||
void *new_attrs = krealloc(data->group.attrs,
|
||||
new_max_attrs * sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!new_attrs)
|
||||
return -ENOMEM;
|
||||
data->group.attrs = new_attrs;
|
||||
|
||||
|
||||
data->group.attrs[data->num_attributes-1] = attr;
|
||||
data->group.attrs[data->num_attributes] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpld_dev_attr_init(struct device_attribute *dev_attr,
|
||||
const char *name, umode_t mode,
|
||||
show_func show, store_func store)
|
||||
{
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
dev_attr->attr.name = name;
|
||||
dev_attr->attr.mode = mode;
|
||||
dev_attr->show = show;
|
||||
dev_attr->store = store;
|
||||
}
|
||||
|
||||
static struct cpld_sensor * add_sensor(struct cpld_data *data,
|
||||
const char *name,
|
||||
u8 reg, u8 mask, bool invert,
|
||||
bool update, umode_t mode,
|
||||
show_func get, store_func set)
|
||||
{
|
||||
struct cpld_sensor *sensor;
|
||||
struct device_attribute *a;
|
||||
|
||||
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor)
|
||||
return NULL;
|
||||
a = &sensor->attribute;
|
||||
|
||||
snprintf(sensor->name, sizeof(sensor->name), name);
|
||||
sensor->reg = reg;
|
||||
sensor->mask = mask;
|
||||
sensor->update = update;
|
||||
sensor->invert = invert;
|
||||
cpld_dev_attr_init(a, sensor->name,
|
||||
mode,
|
||||
get, set);
|
||||
|
||||
if (cpld_add_attribute(data, &a->attr))
|
||||
return NULL;
|
||||
|
||||
sensor->next = data->sensors;
|
||||
data->sensors = sensor;
|
||||
|
||||
return sensor;
|
||||
}
|
||||
|
||||
static int add_attributes_cmn(struct cpld_data *data, struct attrs **cmn)
|
||||
{
|
||||
u8 reg, i ;
|
||||
bool invert;
|
||||
struct attrs *a;
|
||||
struct base_attrs *b;
|
||||
|
||||
if (NULL == cmn)
|
||||
return -1;
|
||||
|
||||
for (i = 0; cmn[i]; i++)
|
||||
{
|
||||
a = cmn[i];
|
||||
|
||||
reg = a->reg;
|
||||
invert = a->invert;
|
||||
|
||||
b = a->base;
|
||||
if (NULL == b)
|
||||
break;
|
||||
|
||||
if (add_sensor(data, b->name,
|
||||
reg, 0xff, invert,
|
||||
true, b->mode,
|
||||
b->get, b->set) == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_attributes_portly(struct cpld_data *data, struct attrs **pa)
|
||||
{
|
||||
char name[NAME_SIZE+1];
|
||||
int i, j;
|
||||
u8 reg, mask, invert;
|
||||
struct attrs *a;
|
||||
struct base_attrs *b;
|
||||
|
||||
if (NULL == pa)
|
||||
return -1;
|
||||
|
||||
|
||||
for (i = 0; pa[i]; i++) {
|
||||
a = pa[i];
|
||||
|
||||
invert = a->invert;
|
||||
b = a->base;
|
||||
if (b == NULL)
|
||||
break;
|
||||
|
||||
for (j = 0; j < data->sfp_num; j++)
|
||||
{
|
||||
snprintf(name, NAME_SIZE, "%s_%d", b->name, j+1);
|
||||
get_reg_bit(a->reg, j, ®, &mask);
|
||||
|
||||
if (add_sensor(data, name, reg, mask, invert,
|
||||
true, b->mode, b->get, b->set) == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_attributes(struct i2c_client *client,
|
||||
struct cpld_data *data)
|
||||
{
|
||||
struct model_attrs *m = data->cmn_attr;
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Common attributes.*/
|
||||
add_attributes_cmn(data, m->cmn);
|
||||
|
||||
/* Port-wise attributes.*/
|
||||
add_attributes_portly(data, m->portly);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int juniper_i2c_cpld_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
int status;
|
||||
struct cpld_data *data = NULL;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_dbg(dev, "i2c_check_functionality failed (0x%x)\n", client->addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->model = dev_id->driver_data;
|
||||
data->cmn_attr = &models_attr[data->model];
|
||||
get_sfp_spec(data->model, &data->sfp_num, &data->sfp_types);
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->dev = dev;
|
||||
dev_info(dev, "chip found\n");
|
||||
|
||||
status = add_attributes(client, data);
|
||||
if (status)
|
||||
goto out_kfree;
|
||||
|
||||
/*
|
||||
* If there are no attributes, something is wrong.
|
||||
* Bail out instead of trying to register nothing.
|
||||
*/
|
||||
if (!data->num_attributes) {
|
||||
dev_err(dev, "No attributes found\n");
|
||||
status = -ENODEV;
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &data->group);
|
||||
if (status) {
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
status = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
juniper_i2c_cpld_add_client(client);
|
||||
dev_info(dev, "%s: cpld '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &data->group);
|
||||
out_kfree:
|
||||
kfree(data->group.attrs);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static int juniper_i2c_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, &data->group);
|
||||
kfree(data->group.attrs);
|
||||
juniper_i2c_cpld_remove_client(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int juniper_i2c_cpld_read(u8 cpld_addr, u8 reg)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EPERM;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_read_byte_data(cpld_node->client, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(juniper_i2c_cpld_read);
|
||||
|
||||
int juniper_i2c_cpld_write(unsigned short cpld_addr, u8 reg, u8 value)
|
||||
{
|
||||
struct list_head *list_node = NULL;
|
||||
struct cpld_client_node *cpld_node = NULL;
|
||||
int ret = -EIO;
|
||||
|
||||
mutex_lock(&list_lock);
|
||||
|
||||
list_for_each(list_node, &cpld_client_list)
|
||||
{
|
||||
cpld_node = list_entry(list_node, struct cpld_client_node, list);
|
||||
|
||||
if (cpld_node->client->addr == cpld_addr) {
|
||||
ret = i2c_smbus_write_byte_data(cpld_node->client, reg, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(juniper_i2c_cpld_write);
|
||||
|
||||
|
||||
static const struct i2c_device_id juniper_i2c_cpld_id[] = {
|
||||
{ "cpld_as7712", AS7712_32X},
|
||||
{ "cpld_as7716", AS7716_32X},
|
||||
{ "cpld_qfx5210", qfx5210_64X},
|
||||
{ "cpld_as7312", AS7312_54X},
|
||||
{ "cpld_plain", PLAIN_CPLD},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, juniper_i2c_cpld_id);
|
||||
|
||||
static struct i2c_driver juniper_i2c_cpld_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "juniper_i2c_cpld",
|
||||
},
|
||||
.probe = juniper_i2c_cpld_probe,
|
||||
.remove = juniper_i2c_cpld_remove,
|
||||
.id_table = juniper_i2c_cpld_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
|
||||
static int __init juniper_i2c_cpld_init(void)
|
||||
{
|
||||
mutex_init(&list_lock);
|
||||
return i2c_add_driver(&juniper_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
static void __exit juniper_i2c_cpld_exit(void)
|
||||
{
|
||||
i2c_del_driver(&juniper_i2c_cpld_driver);
|
||||
}
|
||||
|
||||
module_init(juniper_i2c_cpld_init);
|
||||
module_exit(juniper_i2c_cpld_exit);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("juniper_i2c_cpld driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1 +0,0 @@
|
||||
../../common/modules/ym2651y.c
|
@ -0,0 +1,622 @@
|
||||
/*
|
||||
* An hwmon driver for the 3Y Power YM-2651Y Power Module
|
||||
*
|
||||
* Tested and validated on Juniper QFX5210
|
||||
* Ciju Rajan K <crajank@juniper.net>
|
||||
*
|
||||
* Copyright (C) 2014 Accton Technology Corporation.
|
||||
* Brandon Chuang <brandon_chuang@accton.com.tw>
|
||||
*
|
||||
* Based on ad7414.c
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MAX_FAN_DUTY_CYCLE 100
|
||||
|
||||
/* Addresses scanned
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { 0x58, 0x5b, I2C_CLIENT_END };
|
||||
|
||||
enum chips {
|
||||
YM2651,
|
||||
YM2401,
|
||||
YM2851,
|
||||
};
|
||||
|
||||
/* Each client has this additional data
|
||||
*/
|
||||
struct ym2651y_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* !=0 if registers are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
u8 capability; /* Register value */
|
||||
u16 status_word; /* Register value */
|
||||
u8 fan_fault; /* Register value */
|
||||
u8 over_temp; /* Register value */
|
||||
u16 v_out; /* Register value */
|
||||
u16 i_out; /* Register value */
|
||||
u16 p_out; /* Register value */
|
||||
u16 temp; /* Register value */
|
||||
u16 fan_speed; /* Register value */
|
||||
u16 fan_duty_cycle[2]; /* Register value */
|
||||
u8 fan_dir[4]; /* Register value */
|
||||
u8 pmbus_revision; /* Register value */
|
||||
u8 mfr_id[10]; /* Register value */
|
||||
u8 mfr_model[10]; /* Register value */
|
||||
u8 mfr_revsion[3]; /* Register value */
|
||||
u16 mfr_vin_min; /* Register value */
|
||||
u16 mfr_vin_max; /* Register value */
|
||||
u16 mfr_iin_max; /* Register value */
|
||||
u16 mfr_iout_max; /* Register value */
|
||||
u16 mfr_pin_max; /* Register value */
|
||||
u16 mfr_pout_max; /* Register value */
|
||||
u16 mfr_vout_min; /* Register value */
|
||||
u16 mfr_vout_max; /* Register value */
|
||||
};
|
||||
|
||||
static ssize_t show_byte(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_word(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_linear(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_over_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static ssize_t show_ascii(struct device *dev, struct device_attribute *da,
|
||||
char *buf);
|
||||
static struct ym2651y_data *ym2651y_update_device(struct device *dev);
|
||||
static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count);
|
||||
static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value);
|
||||
|
||||
enum ym2651y_sysfs_attributes {
|
||||
PSU_POWER_ON = 0,
|
||||
PSU_TEMP_FAULT,
|
||||
PSU_POWER_GOOD,
|
||||
PSU_FAN1_FAULT,
|
||||
PSU_FAN_DIRECTION,
|
||||
PSU_OVER_TEMP,
|
||||
PSU_V_OUT,
|
||||
PSU_I_OUT,
|
||||
PSU_P_OUT,
|
||||
PSU_P_OUT_UV, /*In Unit of microVolt, instead of mini.*/
|
||||
PSU_TEMP1_INPUT,
|
||||
PSU_FAN1_SPEED,
|
||||
PSU_FAN1_DUTY_CYCLE,
|
||||
PSU_PMBUS_REVISION,
|
||||
PSU_MFR_ID,
|
||||
PSU_MFR_MODEL,
|
||||
PSU_MFR_REVISION,
|
||||
PSU_MFR_VIN_MIN,
|
||||
PSU_MFR_VIN_MAX,
|
||||
PSU_MFR_VOUT_MIN,
|
||||
PSU_MFR_VOUT_MAX,
|
||||
PSU_MFR_IIN_MAX,
|
||||
PSU_MFR_IOUT_MAX,
|
||||
PSU_MFR_PIN_MAX,
|
||||
PSU_MFR_POUT_MAX
|
||||
};
|
||||
|
||||
/* sysfs attributes for hwmon
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(psu_power_on, S_IRUGO, show_word, NULL, PSU_POWER_ON);
|
||||
static SENSOR_DEVICE_ATTR(psu_temp_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT);
|
||||
static SENSOR_DEVICE_ATTR(psu_power_good, S_IRUGO, show_word, NULL, PSU_POWER_GOOD);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_fault, S_IRUGO, show_fan_fault, NULL, PSU_FAN1_FAULT);
|
||||
static SENSOR_DEVICE_ATTR(psu_over_temp, S_IRUGO, show_over_temp, NULL, PSU_OVER_TEMP);
|
||||
static SENSOR_DEVICE_ATTR(psu_v_out, S_IRUGO, show_linear, NULL, PSU_V_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_i_out, S_IRUGO, show_linear, NULL, PSU_I_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_p_out, S_IRUGO, show_linear, NULL, PSU_P_OUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_speed_rpm, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan1_duty_cycle_percentage, S_IWUSR | S_IRUGO, show_linear, set_fan_duty_cycle, PSU_FAN1_DUTY_CYCLE);
|
||||
static SENSOR_DEVICE_ATTR(psu_fan_dir, S_IRUGO, show_ascii, NULL, PSU_FAN_DIRECTION);
|
||||
static SENSOR_DEVICE_ATTR(psu_pmbus_revision, S_IRUGO, show_byte, NULL, PSU_PMBUS_REVISION);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_id, S_IRUGO, show_ascii, NULL, PSU_MFR_ID);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_model, S_IRUGO, show_ascii, NULL, PSU_MFR_MODEL);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_revision, S_IRUGO, show_ascii, NULL, PSU_MFR_REVISION);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vin_min, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MIN);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vin_max, S_IRUGO, show_linear, NULL, PSU_MFR_VIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vout_min, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MIN);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_vout_max, S_IRUGO, show_linear, NULL, PSU_MFR_VOUT_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_iin_max, S_IRUGO, show_linear, NULL, PSU_MFR_IIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_iout_max, S_IRUGO, show_linear, NULL, PSU_MFR_IOUT_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_pin_max, S_IRUGO, show_linear, NULL, PSU_MFR_PIN_MAX);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_pout_max, S_IRUGO, show_linear, NULL, PSU_MFR_POUT_MAX);
|
||||
|
||||
/*Duplicate nodes for lm-sensors.*/
|
||||
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_linear, NULL, PSU_V_OUT);
|
||||
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, show_linear, NULL, PSU_I_OUT);
|
||||
static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, show_linear, NULL, PSU_P_OUT_UV);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_linear, NULL, PSU_TEMP1_INPUT);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_linear, NULL, PSU_FAN1_SPEED);
|
||||
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_word, NULL, PSU_TEMP_FAULT);
|
||||
|
||||
static struct attribute *ym2651y_attributes[] = {
|
||||
&sensor_dev_attr_psu_power_on.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_temp_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_power_good.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_over_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_v_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_i_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_p_out.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_speed_rpm.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan1_duty_cycle_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_fan_dir.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_pmbus_revision.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_id.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_model.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_revision.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vin_min.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_pout_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_iin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_pin_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vout_min.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_vout_max.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_iout_max.dev_attr.attr,
|
||||
/*Duplicate nodes for lm-sensors.*/
|
||||
&sensor_dev_attr_curr2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_fault.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t show_byte(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
return (attr->index == PSU_PMBUS_REVISION) ? sprintf(buf, "%d\n", data->pmbus_revision) :
|
||||
sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t show_word(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
u16 status = 0;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_POWER_ON: /* psu_power_on, low byte bit 6 of status_word, 0=>ON, 1=>OFF */
|
||||
status = (data->status_word & 0x40) ? 0 : 1;
|
||||
break;
|
||||
case PSU_TEMP_FAULT: /* psu_temp_fault, low byte bit 2 of status_word, 0=>Normal, 1=>temp fault */
|
||||
status = (data->status_word & 0x4) >> 2;
|
||||
break;
|
||||
case PSU_POWER_GOOD: /* psu_power_good, high byte bit 3 of status_word, 0=>OK, 1=>FAIL */
|
||||
status = (data->status_word & 0x800) ? 0 : 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", status);
|
||||
}
|
||||
|
||||
static int two_complement_to_int(u16 data, u8 valid_bit, int mask)
|
||||
{
|
||||
u16 valid_data = data & mask;
|
||||
bool is_negative = valid_data >> (valid_bit - 1);
|
||||
|
||||
return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data;
|
||||
}
|
||||
|
||||
static ssize_t set_fan_duty_cycle(struct device *dev, struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1;
|
||||
long speed;
|
||||
int error;
|
||||
|
||||
error = kstrtol(buf, 10, &speed);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_duty_cycle[nr] = speed;
|
||||
ym2651y_write_word(client, 0x3B + nr, data->fan_duty_cycle[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_linear(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
u16 value = 0;
|
||||
int exponent, mantissa;
|
||||
int multiplier = 1000;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_V_OUT:
|
||||
value = data->v_out;
|
||||
break;
|
||||
case PSU_I_OUT:
|
||||
value = data->i_out;
|
||||
break;
|
||||
case PSU_P_OUT_UV:
|
||||
multiplier = 1000000; /*For lm-sensors, unit is micro-Volt.*/
|
||||
/*Passing through*/
|
||||
case PSU_P_OUT:
|
||||
value = data->p_out;
|
||||
break;
|
||||
case PSU_TEMP1_INPUT:
|
||||
value = data->temp;
|
||||
break;
|
||||
case PSU_FAN1_SPEED:
|
||||
value = data->fan_speed;
|
||||
multiplier = 1;
|
||||
break;
|
||||
case PSU_FAN1_DUTY_CYCLE:
|
||||
value = data->fan_duty_cycle[0];
|
||||
multiplier = 1;
|
||||
break;
|
||||
case PSU_MFR_VIN_MIN:
|
||||
value = data->mfr_vin_min;
|
||||
break;
|
||||
case PSU_MFR_VIN_MAX:
|
||||
value = data->mfr_vin_max;
|
||||
break;
|
||||
case PSU_MFR_VOUT_MIN:
|
||||
value = data->mfr_vout_min;
|
||||
break;
|
||||
case PSU_MFR_VOUT_MAX:
|
||||
value = data->mfr_vout_max;
|
||||
break;
|
||||
case PSU_MFR_PIN_MAX:
|
||||
value = data->mfr_pin_max;
|
||||
break;
|
||||
case PSU_MFR_POUT_MAX:
|
||||
value = data->mfr_pout_max;
|
||||
break;
|
||||
case PSU_MFR_IOUT_MAX:
|
||||
value = data->mfr_iout_max;
|
||||
break;
|
||||
case PSU_MFR_IIN_MAX:
|
||||
value = data->mfr_iin_max;
|
||||
break;
|
||||
}
|
||||
|
||||
exponent = two_complement_to_int(value >> 11, 5, 0x1f);
|
||||
mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff);
|
||||
return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) :
|
||||
sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent));
|
||||
}
|
||||
|
||||
static ssize_t show_fan_fault(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6;
|
||||
|
||||
return sprintf(buf, "%d\n", data->fan_fault >> shift);
|
||||
}
|
||||
|
||||
static ssize_t show_over_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", data->over_temp >> 7);
|
||||
}
|
||||
|
||||
static ssize_t show_ascii(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ym2651y_data *data = ym2651y_update_device(dev);
|
||||
u8 *ptr = NULL;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_FAN_DIRECTION: /* psu_fan_dir */
|
||||
ptr = data->fan_dir;
|
||||
break;
|
||||
case PSU_MFR_ID: /* psu_mfr_id */
|
||||
ptr = data->mfr_id;
|
||||
break;
|
||||
case PSU_MFR_MODEL: /* psu_mfr_model */
|
||||
ptr = data->mfr_model;
|
||||
break;
|
||||
case PSU_MFR_REVISION: /* psu_mfr_revision */
|
||||
ptr = data->mfr_revsion;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", ptr);
|
||||
}
|
||||
|
||||
static const struct attribute_group ym2651y_group = {
|
||||
.attrs = ym2651y_attributes,
|
||||
};
|
||||
|
||||
static int ym2651y_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct ym2651y_data *data;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct ym2651y_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
status = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
dev_info(&client->dev, "chip found\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &ym2651y_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: psu '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &ym2651y_group);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ym2651y_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &ym2651y_group);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ym2651y_id[] = {
|
||||
{ "ym2651", YM2651 },
|
||||
{ "ym2401", YM2401 },
|
||||
{ "ym2851", YM2851 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ym2651y_id);
|
||||
|
||||
static struct i2c_driver ym2651y_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ym2651",
|
||||
},
|
||||
.probe = ym2651y_probe,
|
||||
.remove = ym2651y_remove,
|
||||
.id_table = ym2651y_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int ym2651y_read_byte(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int ym2651y_read_word(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
|
||||
static int ym2651y_write_word(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, value);
|
||||
}
|
||||
|
||||
static int ym2651y_read_block(struct i2c_client *client, u8 command, u8 *data,
|
||||
int data_len)
|
||||
{
|
||||
int result = i2c_smbus_read_i2c_block_data(client, command, data_len, data);
|
||||
|
||||
if (unlikely(result < 0))
|
||||
goto abort;
|
||||
if (unlikely(result != data_len)) {
|
||||
result = -EIO;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
abort:
|
||||
return result;
|
||||
}
|
||||
|
||||
struct reg_data_byte {
|
||||
u8 reg;
|
||||
u8 *value;
|
||||
};
|
||||
|
||||
struct reg_data_word {
|
||||
u8 reg;
|
||||
u16 *value;
|
||||
};
|
||||
|
||||
static struct ym2651y_data *ym2651y_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ym2651y_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|
||||
|| !data->valid) {
|
||||
int i, status;
|
||||
u8 command;
|
||||
u8 fan_dir[5] = {0};
|
||||
struct reg_data_byte regs_byte[] = { {0x19, &data->capability},
|
||||
{0x7d, &data->over_temp},
|
||||
{0x81, &data->fan_fault},
|
||||
{0x98, &data->pmbus_revision}
|
||||
};
|
||||
struct reg_data_word regs_word[] = { {0x79, &data->status_word},
|
||||
{0x8b, &data->v_out},
|
||||
{0x8c, &data->i_out},
|
||||
{0x96, &data->p_out},
|
||||
{0x8d, &data->temp},
|
||||
{0x3b, &(data->fan_duty_cycle[0])},
|
||||
{0x3c, &(data->fan_duty_cycle[1])},
|
||||
{0x90, &data->fan_speed},
|
||||
{0xa0, &data->mfr_vin_min},
|
||||
{0xa1, &data->mfr_vin_max},
|
||||
{0xa2, &data->mfr_iin_max},
|
||||
{0xa3, &data->mfr_pin_max},
|
||||
{0xa4, &data->mfr_vout_min},
|
||||
{0xa5, &data->mfr_vout_max},
|
||||
{0xa6, &data->mfr_iout_max},
|
||||
{0xa7, &data->mfr_pout_max}
|
||||
};
|
||||
|
||||
dev_dbg(&client->dev, "Starting ym2651 update\n");
|
||||
|
||||
/* Read byte data */
|
||||
for (i = 0; i < ARRAY_SIZE(regs_byte); i++) {
|
||||
status = ym2651y_read_byte(client, regs_byte[i].reg);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_byte[i].reg, status);
|
||||
*(regs_byte[i].value) = 0;
|
||||
}
|
||||
else {
|
||||
*(regs_byte[i].value) = status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read word data */
|
||||
for (i = 0; i < ARRAY_SIZE(regs_word); i++) {
|
||||
status = ym2651y_read_word(client, regs_word[i].reg);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_word[i].reg, status);
|
||||
*(regs_word[i].value) = 0;
|
||||
}
|
||||
else {
|
||||
*(regs_word[i].value) = status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read fan_direction */
|
||||
command = 0xC3;
|
||||
status = ym2651y_read_block(client, command, fan_dir, ARRAY_SIZE(fan_dir)-1);
|
||||
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
}
|
||||
|
||||
strncpy(data->fan_dir, fan_dir+1, ARRAY_SIZE(data->fan_dir)-1);
|
||||
data->fan_dir[ARRAY_SIZE(data->fan_dir)-1] = '\0';
|
||||
|
||||
/* Read mfr_id */
|
||||
command = 0x99;
|
||||
status = ym2651y_read_block(client, command, data->mfr_id,
|
||||
ARRAY_SIZE(data->mfr_id)-1);
|
||||
data->mfr_id[ARRAY_SIZE(data->mfr_id)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
/* Read mfr_model */
|
||||
command = 0x9a;
|
||||
status = ym2651y_read_block(client, command, data->mfr_model,
|
||||
ARRAY_SIZE(data->mfr_model)-1);
|
||||
data->mfr_model[ARRAY_SIZE(data->mfr_model)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
/* Read mfr_revsion */
|
||||
command = 0x9b;
|
||||
status = ym2651y_read_block(client, command, data->mfr_revsion,
|
||||
ARRAY_SIZE(data->mfr_revsion)-1);
|
||||
data->mfr_revsion[ARRAY_SIZE(data->mfr_revsion)-1] = '\0';
|
||||
|
||||
if (status < 0)
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command, status);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
module_i2c_driver(ym2651y_driver);
|
||||
|
||||
MODULE_AUTHOR("Brandon Chuang <brandon_chuang@accton.com.tw>");
|
||||
MODULE_DESCRIPTION("3Y Power YM-2651Y driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
0
platform/broadcom/sonic-platform-modules-juniper/qfx5210/service/qfx5210-platform-init.service
Executable file → Normal file
0
platform/broadcom/sonic-platform-modules-juniper/qfx5210/service/qfx5210-platform-init.service
Executable file → Normal file
@ -1 +0,0 @@
|
||||
import platform
|
@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Name: platform.py, version: 1.0
|
||||
#
|
||||
# Description: Module contains the definitions of SONiC platform APIs
|
||||
# which provide the platform specific details
|
||||
#
|
||||
# Copyright (c) 2019, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Notice and Disclaimer: This code is licensed to you under the GNU General
|
||||
# Public License as published by the Free Software Foundation, version 3 or
|
||||
# any later version. This code is not an official Juniper product. You can
|
||||
# obtain a copy of the License at <https://www.gnu.org/licenses/>
|
||||
#
|
||||
# OSS License:
|
||||
#
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Third-Party Code: This code may depend on other components under separate
|
||||
# copyright notice and license terms. Your use of the source code for those
|
||||
# components is subject to the terms and conditions of the respective license
|
||||
# as noted in the Third-Party source code file.
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from sonic_platform_base.platform_base import PlatformBase
|
||||
except ImportError as e:
|
||||
raise ImportError("%s - required module not found" % e)
|
||||
|
||||
platformDict = {'platform':'QFX5210-64C'}
|
||||
|
||||
class Platform(PlatformBase):
|
||||
def __init__(self):
|
||||
self.platform = self.getPlatform()
|
||||
|
||||
def getPlatformDict(self):
|
||||
global platformDict
|
||||
if platformDict:
|
||||
return platformDict
|
||||
|
||||
def readPlatformName(self):
|
||||
return self.getPlatformDict().get('platform')
|
||||
|
||||
def getPlatform(self):
|
||||
platformCls = self.readPlatformName()
|
||||
return platformCls
|
||||
|
||||
def get_chassis(self):
|
||||
from chassis import Chassis
|
||||
chassis = Chassis()
|
||||
return chassis
|
||||
|
@ -35,20 +35,12 @@
|
||||
try:
|
||||
import os
|
||||
import commands
|
||||
import sys, getopt
|
||||
import subprocess
|
||||
import click
|
||||
import imp
|
||||
import logging
|
||||
import logging.config
|
||||
import logging.handlers
|
||||
import types
|
||||
import time
|
||||
import traceback
|
||||
import glob
|
||||
import collections
|
||||
import StringIO
|
||||
from tabulate import tabulate
|
||||
except ImportError as e:
|
||||
raise ImportError('%s - required module not found' % str(e))
|
||||
|
||||
@ -58,17 +50,16 @@ FUNCTION_NAME = '/var/log/juniper_qfx5210_monitor'
|
||||
verbose = False
|
||||
DEBUG = False
|
||||
|
||||
global log_file
|
||||
global log_level
|
||||
log_file = '%s.log' % FUNCTION_NAME
|
||||
log_level = logging.DEBUG
|
||||
|
||||
|
||||
global isPlatformAFI
|
||||
global is80PerFlag
|
||||
global is60PerFlag
|
||||
global isFireThresholdReached
|
||||
global isFireThresholdPrint
|
||||
global PrevASICValue
|
||||
global FireThresholdSecsRemaining
|
||||
isPlatformAFI = False
|
||||
is80PerFlag = True
|
||||
is60PerFlag = True
|
||||
isFireThresholdReached = False
|
||||
isFireThresholdPrint = True
|
||||
PrevASICValue = 0
|
||||
FireThresholdSecsRemaining = 120
|
||||
|
||||
temp_policy_AFI = {
|
||||
0: [[70, 0, 48000], [70, 48000, 53000], [80, 53000, 0], [80, 53000, 58000], [100, 58000, 0], ['Yellow Alarm', 64000, 70000], ['Red Alarm', 70000, 75000], ['Fire Shut Alarm', 75000, 0]],
|
||||
@ -201,7 +192,7 @@ class QFX5210_ThermalUtil(object):
|
||||
|
||||
try:
|
||||
val_file.close()
|
||||
except:
|
||||
except IOError as e:
|
||||
logging.debug('get_sensor_node_val: unable to close file. device_path:%s', device_path)
|
||||
return None
|
||||
|
||||
@ -227,8 +218,8 @@ class QFX5210_ThermalUtil(object):
|
||||
return None
|
||||
|
||||
try:
|
||||
val_file.close()
|
||||
except:
|
||||
val_file.close()
|
||||
except IOError as e:
|
||||
logging.debug('get_coretemp_node_val: unable to close file. device_path:%s', device_path)
|
||||
return None
|
||||
|
||||
@ -326,7 +317,7 @@ class QFX5210_ThermalUtil(object):
|
||||
isFireThresholdReached == False
|
||||
time.sleep(20)
|
||||
cmd = "poweroff"
|
||||
returned_value = os.system(cmd)
|
||||
os.system(cmd)
|
||||
|
||||
for x in range(self.SENSOR_CORETEMP_NUM_ON_MAIN_BOARD):
|
||||
if x < self.SENSOR_NUM_ON_MAIN_BOARD:
|
||||
@ -340,7 +331,7 @@ class QFX5210_ThermalUtil(object):
|
||||
proc = subprocess.Popen("bcmcmd \"show temp\" | grep \"maximum peak temperature\" | awk '{ print $5 }' > /var/log/asic_value 2>&1 & ",shell=True)
|
||||
time.sleep(2)
|
||||
cmd = "kill -9 %s"%(proc.pid)
|
||||
status, cmd_out = commands.getstatusoutput(cmd)
|
||||
commands.getstatusoutput(cmd)
|
||||
|
||||
if os.stat("/var/log/asic_value").st_size == 0:
|
||||
value = PrevASICValue
|
||||
@ -531,13 +522,6 @@ class device_monitor(object):
|
||||
|
||||
def __init__(self, log_file, log_level):
|
||||
global DEBUG
|
||||
global isPlatformAFI
|
||||
global isFireThresholdReached
|
||||
global is80PerFlag
|
||||
global is60PerFlag
|
||||
global isFireThresholdPrint
|
||||
global PrevASICValue
|
||||
global FireThresholdSecsRemaining
|
||||
MASTER_LED_PATH = '/sys/class/leds/master/brightness'
|
||||
SYSTEM_LED_PATH = '/sys/class/leds/system/brightness'
|
||||
FANTYPE_PATH = '/sys/bus/i2c/devices/17-0068/fan1_direction'
|
||||
@ -561,27 +545,26 @@ class device_monitor(object):
|
||||
console.setFormatter(formatter)
|
||||
logging.getLogger('').addHandler(console)
|
||||
|
||||
import sonic_platform
|
||||
platform = sonic_platform.platform.Platform()
|
||||
chassis = platform.get_chassis()
|
||||
fan_type = chassis.get_fan_type(FANTYPE_PATH)
|
||||
try:
|
||||
fan_type_file = open(FANTYPE_PATH)
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
fan_type = -1
|
||||
else:
|
||||
fan_type = fan_type_file.read()
|
||||
fan_type_file.close()
|
||||
|
||||
|
||||
# the return value of get_fan_type is AFO = 0, AFI = 1 and for error condition it is -1
|
||||
# In the error condition also, we are making default platform as AFO, to continue with Energy Monitoring
|
||||
if (int(fan_type) == -1 or int(fan_type) == 0):
|
||||
logging.debug('FANTYPE_PATH. fan_type %d', int(fan_type))
|
||||
if (int(fan_type) == -1):
|
||||
logging.error('device_monitor: unable to open sys file for fan handling, defaulting it to AFO')
|
||||
isPlatformAFI = False
|
||||
else:
|
||||
isPlatformAFI = True
|
||||
|
||||
isFireThresholdReached = False
|
||||
is80PerFlag = True
|
||||
is60PerFlag = True
|
||||
isFireThresholdPrint = True
|
||||
FireThresholdSecsRemaining = 120
|
||||
PrevASICValue = 0
|
||||
|
||||
master_led_value = 1
|
||||
try:
|
||||
masterLED_file = open(MASTER_LED_PATH, 'r+')
|
||||
@ -605,8 +588,6 @@ class device_monitor(object):
|
||||
thermal.getSensorTemp()
|
||||
|
||||
def main():
|
||||
log_file = '%s.log' % FUNCTION_NAME
|
||||
log_level = logging.DEBUG
|
||||
|
||||
#Introducing sleep of 150 seconds to wait for all the docker containers to start before starting the EM policy.
|
||||
time.sleep(150)
|
||||
|
@ -39,11 +39,7 @@ import commands
|
||||
import sys, getopt
|
||||
import binascii
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import random
|
||||
import optparse
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
|
||||
@ -139,144 +135,15 @@ def main():
|
||||
print 'Error: Execution of "%s" failed', DisableWatchDogCmd
|
||||
return False
|
||||
|
||||
|
||||
CPUeepromFileCmd = 'cat /sys/devices/pci0000:00/0000:00:1f.3/i2c-0/0-0056/eeprom > /etc/init.d/eeprom_qfx5210_ascii'
|
||||
# Write the contents of CPU EEPROM to file
|
||||
time.sleep(1)
|
||||
# Invoking the script which retrieves the data from Board EEPROM and storing in file
|
||||
EEPROMDataCmd = 'python /usr/share/sonic/device/x86_64-juniper_qfx5210-r0/plugins/qfx5210_eeprom_data.py'
|
||||
try:
|
||||
os.system(CPUeepromFileCmd)
|
||||
os.system(EEPROMDataCmd)
|
||||
except OSError:
|
||||
print 'Error: Execution of "%s" failed', CPUeepromFileCmd
|
||||
print 'Error: Execution of "%s" failed', EEPROMDataCmd
|
||||
return False
|
||||
|
||||
eeprom_ascii = '/etc/init.d/eeprom_qfx5210_ascii'
|
||||
# Read file contents in Hex format
|
||||
with open(eeprom_ascii, 'rb') as Hexformat:
|
||||
content = Hexformat.read()
|
||||
Hexformatoutput = binascii.hexlify(content)
|
||||
|
||||
eeprom_hex = '/etc/init.d/eeprom_qfx5210_hex'
|
||||
#Write contents of CPU EEPROM to new file in hexa format
|
||||
with open(eeprom_hex, 'wb+') as Hexfile:
|
||||
Hexfile.write(Hexformatoutput)
|
||||
|
||||
# Read from EEPROM Hex file and extract the different fields like Product name,
|
||||
# Part Number, Serial Number MAC Address, Mfg Date ... etc and store in /var/run/eeprom file
|
||||
with open(eeprom_hex, 'rb') as eeprom_hexfile:
|
||||
# moving the file pointer to required position where product name is stored in EEPROM file and reading the required bytes from this position
|
||||
|
||||
product_position = eeprom_hexfile.seek(26, 0)
|
||||
product_read = eeprom_hexfile.read(36)
|
||||
product_name = binascii.unhexlify(product_read)
|
||||
|
||||
# creating the "/var/run/eeprom" file and storing all the values of different fields in this file.
|
||||
eeprom_file = open ("/var/run/eeprom", "a+")
|
||||
eeprom_file.write("Product Name=%s\r\n" % str(product_name))
|
||||
|
||||
# like wise we are moving the file pointer to respective position where other fields are stored and extract these fields and store in /var/run/eeprom file
|
||||
partnumber_position = eeprom_hexfile.seek(66, 0)
|
||||
partnumber_read = eeprom_hexfile.read(20)
|
||||
partnumber_name = binascii.unhexlify(partnumber_read)
|
||||
eeprom_file.write("Part Number=%s\r\n" % str(partnumber_name))
|
||||
|
||||
serialnumber_position = eeprom_hexfile.seek(90, 0)
|
||||
serialnumber_read = eeprom_hexfile.read(24)
|
||||
serialnumber_name = binascii.unhexlify(serialnumber_read)
|
||||
eeprom_file.write("Serial Number=%s\r\n" % str(serialnumber_name))
|
||||
|
||||
macaddress_position = eeprom_hexfile.seek(118, 0)
|
||||
macaddress_read = eeprom_hexfile.read(12)
|
||||
macaddress_name=""
|
||||
for i in range(0,12,2):
|
||||
macaddress_name += macaddress_read[i:i+2] + ":"
|
||||
macaddress_name=macaddress_name[:-1]
|
||||
eeprom_file.write("MAC Address=%s\r\n" % str(macaddress_name))
|
||||
|
||||
mfgdate_position = eeprom_hexfile.seek(132, 0)
|
||||
mfgdate_read = eeprom_hexfile.read(40)
|
||||
mfgdate_name = binascii.unhexlify(mfgdate_read)
|
||||
eeprom_file.write("Manufacture Date=%s\r\n" % str(mfgdate_name))
|
||||
|
||||
devversion_position = eeprom_hexfile.seek(176, 0)
|
||||
devversion_read = eeprom_hexfile.read(2)
|
||||
eeprom_file.write("Device Version=%s\r\n" % str(devversion_read))
|
||||
|
||||
platform_position = eeprom_hexfile.seek(182, 0)
|
||||
platform_read = eeprom_hexfile.read(68)
|
||||
platform_name = binascii.unhexlify(platform_read)
|
||||
eeprom_file.write("Platform Name=%s\r\n" % str(platform_name))
|
||||
|
||||
MACnumber_position = eeprom_hexfile.seek(254, 0)
|
||||
MACnumber_read = eeprom_hexfile.read(4)
|
||||
MACnumber = int(MACnumber_read, 16)
|
||||
eeprom_file.write("Number of MAC Addresses=%s\r\n" % str(MACnumber))
|
||||
|
||||
vendorName_position = eeprom_hexfile.seek(262, 0)
|
||||
vendorName_read = eeprom_hexfile.read(40)
|
||||
vendorName = binascii.unhexlify(vendorName_read)
|
||||
eeprom_file.write("Vendor Name=%s\r\n" % str(vendorName))
|
||||
|
||||
mfgname_position = eeprom_hexfile.seek(306, 0)
|
||||
mfgname_read = eeprom_hexfile.read(40)
|
||||
mfgname = binascii.unhexlify(mfgname_read)
|
||||
eeprom_file.write("Manufacture Name=%s\r\n" % str(mfgname))
|
||||
|
||||
vendorext_position = eeprom_hexfile.seek(350, 0)
|
||||
vendorext_read = eeprom_hexfile.read(124)
|
||||
vendorext=""
|
||||
vendorext += "0x" + vendorext_read[0:2]
|
||||
for i in range(2,124,2):
|
||||
vendorext += " 0x" + vendorext_read[i:i+2]
|
||||
eeprom_file.write("Vendor Extension=%s\r\n" % str(vendorext))
|
||||
|
||||
IANA_position = eeprom_hexfile.seek(350, 0)
|
||||
IANA_read = eeprom_hexfile.read(8)
|
||||
IANAName = binascii.unhexlify(IANA_read)
|
||||
eeprom_file.write("IANA=%s\r\n" % str(IANAName))
|
||||
|
||||
ASMpartrev_position = eeprom_hexfile.seek(358, 0)
|
||||
ASMpartrev_read = eeprom_hexfile.read(4)
|
||||
ASMpartrev = binascii.unhexlify(ASMpartrev_read)
|
||||
eeprom_file.write("Assembly Part Number Rev=%s\r\n" % str(ASMpartrev))
|
||||
|
||||
ASMpartnum_position = eeprom_hexfile.seek(374, 0)
|
||||
ASMpartnum_read = eeprom_hexfile.read(20)
|
||||
ASMpartnum_read = binascii.unhexlify(ASMpartnum_read)
|
||||
eeprom_file.write("Assembly Part Number=%s\r\n" % str(ASMpartnum_read))
|
||||
|
||||
ASMID_position = eeprom_hexfile.seek(402, 0)
|
||||
ASMID_read = eeprom_hexfile.read(4)
|
||||
ASMID_read_upper = ASMID_read.upper()
|
||||
eeprom_file.write("Assembly ID=0x%s\r\n" % str(ASMID_read_upper))
|
||||
|
||||
ASMHWMajRev_position = eeprom_hexfile.seek(410, 0)
|
||||
ASMHWMajRev_read = eeprom_hexfile.read(2)
|
||||
eeprom_file.write("Assembly Major Revision=0x%s\r\n" % str(ASMHWMajRev_read))
|
||||
|
||||
ASMHWMinRev_position = eeprom_hexfile.seek(416, 0)
|
||||
ASMHWMinRev_read = eeprom_hexfile.read(2)
|
||||
eeprom_file.write("Assembly Minor Revision=0x%s\r\n" % str(ASMHWMinRev_read))
|
||||
|
||||
Deviation_position = eeprom_hexfile.seek(422, 0)
|
||||
Deviation_read = eeprom_hexfile.read(28)
|
||||
Deviation_read_upper = Deviation_read.upper()
|
||||
eeprom_file.write("Deviation=0x%s\r\n" % str(Deviation_read_upper))
|
||||
|
||||
CLEI_position = eeprom_hexfile.seek(450, 0)
|
||||
CLEI_read = eeprom_hexfile.read(20)
|
||||
CLEI_name = binascii.unhexlify(CLEI_read)
|
||||
eeprom_file.write("CLEI code=%s\r\n" % str(CLEI_name))
|
||||
|
||||
ONIEversion_position = eeprom_hexfile.seek(478, 0)
|
||||
ONIEversion_read = eeprom_hexfile.read(22)
|
||||
ONIEversion = binascii.unhexlify(ONIEversion_read)
|
||||
eeprom_file.write("ONIE Version=%s\r\n" % str(ONIEversion))
|
||||
|
||||
CRC_position = eeprom_hexfile.seek(504, 0)
|
||||
CRC = eeprom_hexfile.read(8)
|
||||
eeprom_file.write("CRC=%s\r\n" % str(CRC))
|
||||
|
||||
eeprom_file.close()
|
||||
|
||||
return True
|
||||
|
||||
def show_help():
|
||||
|
@ -8,9 +8,9 @@ os.listdir
|
||||
setup(
|
||||
name='sonic_platform',
|
||||
version='1.0',
|
||||
description='Module to initialize Juniper QFX5210-64X platforms',
|
||||
description='Module to initialize Juniper platforms',
|
||||
|
||||
packages=['sonic_platform'],
|
||||
package_dir={'sonic_platform': 'qfx5210/sonic_platform'},
|
||||
package_dir={'sonic_platform': 'sonic_platform'},
|
||||
)
|
||||
|
@ -5,7 +5,7 @@
|
||||
# Description: Module contains the definitions of SONiC platform APIs
|
||||
# which provide the chassis specific details
|
||||
#
|
||||
# Copyright (c) 2019, Juniper Networks, Inc.
|
||||
# Copyright (c) 2020, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Notice and Disclaimer: This code is licensed to you under the GNU General
|
||||
@ -39,26 +39,24 @@ try:
|
||||
import commands
|
||||
import sys
|
||||
import time
|
||||
import syslog
|
||||
from sonic_platform_base.chassis_base import ChassisBase
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
SYSLOG_IDENTIFIER = "Juniper-Chassis"
|
||||
|
||||
def log_info(msg):
|
||||
syslog.openlog(SYSLOG_IDENTIFIER)
|
||||
syslog.syslog(syslog.LOG_INFO, msg)
|
||||
syslog.closelog()
|
||||
|
||||
class Chassis(ChassisBase):
|
||||
"""
|
||||
JUNIPER QFX5210 Platform-specific Chassis class
|
||||
"""
|
||||
|
||||
# Find the last reboot reason out of following
|
||||
# CPLD_WATCHDOG_RESET 0x08
|
||||
# POWER_ON_RESET 0x20
|
||||
# CPU_WATCHDOG_RESET 0x40
|
||||
# SOFTWARE_RESET 0x80
|
||||
|
||||
def __init__(self):
|
||||
ChassisBase.__init__(self)
|
||||
|
||||
def get_qfx5210_parameter_value(self,parameter_name):
|
||||
def get_parameter_value(self,parameter_name):
|
||||
try:
|
||||
with open("/var/run/eeprom", "r") as file:
|
||||
for item in file:
|
||||
@ -71,7 +69,7 @@ class Chassis(ChassisBase):
|
||||
return "False"
|
||||
|
||||
def get_product_name(self):
|
||||
product_name_list = self.get_qfx5210_parameter_value('Product Name')
|
||||
product_name_list = self.get_parameter_value('Product Name')
|
||||
if product_name_list:
|
||||
product_name = ''.join(product_name_list)
|
||||
return product_name
|
||||
@ -80,7 +78,7 @@ class Chassis(ChassisBase):
|
||||
|
||||
|
||||
def get_part_number(self):
|
||||
part_number_list = self.get_qfx5210_parameter_value('Part Number')
|
||||
part_number_list = self.get_parameter_value('Part Number')
|
||||
if part_number_list:
|
||||
part_number = ''.join(part_number_list)
|
||||
return part_number
|
||||
@ -89,7 +87,7 @@ class Chassis(ChassisBase):
|
||||
|
||||
|
||||
def get_serial_number(self):
|
||||
serial_number_list = self.get_qfx5210_parameter_value('Serial Number')
|
||||
serial_number_list = self.get_parameter_value('Serial Number')
|
||||
if serial_number_list:
|
||||
serial_number = ''.join(serial_number_list)
|
||||
return serial_number
|
||||
@ -98,7 +96,7 @@ class Chassis(ChassisBase):
|
||||
|
||||
|
||||
def get_base_mac(self):
|
||||
mac_list = self.get_qfx5210_parameter_value('MAC')
|
||||
mac_list = self.get_parameter_value('MAC')
|
||||
if mac_list:
|
||||
mac = ''.join(mac_list)
|
||||
return mac
|
||||
@ -107,7 +105,7 @@ class Chassis(ChassisBase):
|
||||
|
||||
|
||||
def get_mfg_date(self):
|
||||
mfgdate_list = self.get_qfx5210_parameter_value('Manufacture Date')
|
||||
mfgdate_list = self.get_parameter_value('Manufacture Date')
|
||||
if mfgdate_list:
|
||||
mfgdate = ''.join(mfgdate_list)
|
||||
return mfgdate
|
||||
@ -115,7 +113,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_deviceversion_name(self):
|
||||
device_version_list = self.get_qfx5210_parameter_value('Device Version')
|
||||
device_version_list = self.get_parameter_value('Device Version')
|
||||
if device_version_list:
|
||||
deviceversion_name = ''.join(device_version_list)
|
||||
return deviceversion_name
|
||||
@ -123,7 +121,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_platform_name(self):
|
||||
platform_name_list = self.get_qfx5210_parameter_value('Platform Name')
|
||||
platform_name_list = self.get_parameter_value('Platform Name')
|
||||
if platform_name_list:
|
||||
platform_name = ''.join(platform_name_list)
|
||||
return platform_name
|
||||
@ -131,7 +129,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_MACnumber_name(self):
|
||||
MACnumber_name_list = self.get_qfx5210_parameter_value('Number of MAC Addresses')
|
||||
MACnumber_name_list = self.get_parameter_value('Number of MAC Addresses')
|
||||
if MACnumber_name_list:
|
||||
MACnumber_name = ''.join(MACnumber_name_list)
|
||||
return MACnumber_name
|
||||
@ -139,7 +137,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendor_name(self):
|
||||
vendor_name_list = self.get_qfx5210_parameter_value('Vendor Name')
|
||||
vendor_name_list = self.get_parameter_value('Vendor Name')
|
||||
if vendor_name_list:
|
||||
vendor_name = ''.join(vendor_name_list)
|
||||
return vendor_name
|
||||
@ -147,7 +145,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_mfg_name(self):
|
||||
mfg_name_list = self.get_qfx5210_parameter_value('Manufacture Name')
|
||||
mfg_name_list = self.get_parameter_value('Manufacture Name')
|
||||
if mfg_name_list:
|
||||
mfg_name = ''.join(mfg_name_list)
|
||||
return mfg_name
|
||||
@ -155,7 +153,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorext_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Vendor Extension')
|
||||
vendorext_list = self.get_parameter_value('Vendor Extension')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -163,7 +161,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextIANA_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('IANA')
|
||||
vendorext_list = self.get_parameter_value('IANA')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -171,7 +169,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextASMREV_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Assembly Part Number Rev')
|
||||
vendorext_list = self.get_parameter_value('Assembly Part Number Rev')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -179,7 +177,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextASMPartNum_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Assembly Part Number')
|
||||
vendorext_list = self.get_parameter_value('Assembly Part Number')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -187,7 +185,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextASMID_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Assembly ID')
|
||||
vendorext_list = self.get_parameter_value('Assembly ID')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -195,7 +193,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextASMMajNum_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Assembly Major Revision')
|
||||
vendorext_list = self.get_parameter_value('Assembly Major Revision')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -203,7 +201,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextASMMinNum_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('Assembly Minor Revision')
|
||||
vendorext_list = self.get_parameter_value('Assembly Minor Revision')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -211,7 +209,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_vendorextCLEI_name(self):
|
||||
vendorext_list = self.get_qfx5210_parameter_value('CLEI code')
|
||||
vendorext_list = self.get_parameter_value('CLEI code')
|
||||
if vendorext_list:
|
||||
vendorext = ''.join(vendorext_list)
|
||||
return vendorext
|
||||
@ -219,7 +217,7 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_onieversion_name(self):
|
||||
onieversion_name_list = self.get_qfx5210_parameter_value('ONIE Version')
|
||||
onieversion_name_list = self.get_parameter_value('ONIE Version')
|
||||
if onieversion_name_list:
|
||||
onieversion_name = ''.join(onieversion_name_list)
|
||||
return onieversion_name
|
||||
@ -227,51 +225,75 @@ class Chassis(ChassisBase):
|
||||
return False
|
||||
|
||||
def get_crc_name(self):
|
||||
crc_list = self.get_qfx5210_parameter_value('CRC')
|
||||
crc_list = self.get_parameter_value('CRC')
|
||||
if crc_list:
|
||||
crc_name = ''.join(crc_list)
|
||||
return crc_name
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_fan_type(self, fantype_path):
|
||||
try:
|
||||
fan_type_file = open(fantype_path)
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return "-1"
|
||||
else:
|
||||
fan_type = fan_type_file.read()
|
||||
fan_type_file.close()
|
||||
return str(fan_type)
|
||||
|
||||
|
||||
def get_reboot_cause(self):
|
||||
"""
|
||||
Retrieves the cause of the previous reboot
|
||||
"""
|
||||
status, last_reboot_reason = commands.getstatusoutput("i2cget -y 0 0x65 0x24")
|
||||
if (status == 0):
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
platform_name = self.get_platform_name()
|
||||
platform_name = platform_name.replace("\r","")
|
||||
platform_name = platform_name.replace("\n","")
|
||||
log_info("Juniper Platform name: {} and {}".format(self.get_platform_name(), platform_name))
|
||||
if str(platform_name) == "x86_64-juniper_networks_qfx5210-r0":
|
||||
log_info("Juniper Platform QFX5210 ")
|
||||
status, last_reboot_reason = commands.getstatusoutput("i2cget -f -y 0 0x65 0x24")
|
||||
if (status == 0):
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
time.sleep(3)
|
||||
status, last_reboot_reason = commands.getstatusoutput("i2cget -f -y 0 0x65 0x24")
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
|
||||
elif str(platform_name) == "x86_64-juniper_networks_qfx5200-r0" :
|
||||
log_info("Juniper Platform QFX5200 ")
|
||||
status, last_reboot_reason = commands.getstatusoutput("busybox devmem 0xFED50004 8")
|
||||
if (status == 0):
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
else:
|
||||
time.sleep(3)
|
||||
status, last_reboot_reason = commands.getstatusoutput("busybox devmem 0xFED50004 8")
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
else:
|
||||
time.sleep(3)
|
||||
status, last_reboot_reason = commands.getstatusoutput("i2cget -y 0 0x65 0x24")
|
||||
if last_reboot_reason == "0x80":
|
||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||
elif last_reboot_reason == "0x40" or last_reboot_reason == "0x08":
|
||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||
elif last_reboot_reason == "0x20":
|
||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||
elif last_reboot_reason == "0x10":
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Swizzle Reset")
|
||||
else:
|
||||
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Unknown reason")
|
||||
log_info("Juniper QFX5200 and QFX5210 platforms are supported")
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Name: platform.py, version: 1.0
|
||||
#
|
||||
# Description: Module contains the definitions of SONiC platform APIs
|
||||
# Description: Module contains the definition of SONiC platform API
|
||||
# which provide the platform specific details
|
||||
#
|
||||
# Copyright (c) 2020, Juniper Networks, Inc.
|
||||
@ -35,31 +35,18 @@
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from sonic_platform_base.platform_base import PlatformBase
|
||||
from sonic_platform.chassis import Chassis
|
||||
except ImportError as e:
|
||||
raise ImportError("%s - required module not found" % e)
|
||||
|
||||
platformDict = {'platform':'QFX5200-32C'}
|
||||
|
||||
class Platform(PlatformBase):
|
||||
"""
|
||||
Juniper Platform-specific class
|
||||
"""
|
||||
def __init__(self):
|
||||
self.platform = self.getPlatform()
|
||||
|
||||
def getPlatformDict(self):
|
||||
global platformDict
|
||||
if platformDict:
|
||||
return platformDict
|
||||
|
||||
def readPlatformName(self):
|
||||
return self.getPlatformDict().get('platform')
|
||||
|
||||
def getPlatform(self):
|
||||
platformCls = self.readPlatformName()
|
||||
return platformCls
|
||||
|
||||
def get_chassis(self):
|
||||
from chassis import Chassis
|
||||
chassis = Chassis()
|
||||
return chassis
|
||||
|
||||
PlatformBase.__init__(self)
|
||||
self._chassis = Chassis()
|
Loading…
Reference in New Issue
Block a user