2020-01-15 20:42:09 -06:00
|
|
|
|
/*
|
|
|
|
|
* switchboard_fpga.c - driver for seastone2 Switch board FPGA/CPLD.
|
|
|
|
|
*
|
|
|
|
|
* Author: Pradchaya Phucharoen
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2019 Celestica Corp.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* /
|
|
|
|
|
* \--sys
|
|
|
|
|
* \--devices
|
|
|
|
|
* \--platform
|
|
|
|
|
* \--switchboard
|
|
|
|
|
* |--FPGA
|
|
|
|
|
* |--CPLD1
|
|
|
|
|
* |--CPLD2
|
|
|
|
|
* \--SFF
|
|
|
|
|
* |--QSFP[1..32]
|
|
|
|
|
* \--SFP1
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef TEST_MODE
|
2020-09-09 12:32:56 -05:00
|
|
|
|
#define MOD_VERSION "2.2.0"
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#else
|
|
|
|
|
#define MOD_VERSION "TEST"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/stddef.h>
|
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
|
#include <linux/init.h>
|
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
|
#include <linux/acpi.h>
|
|
|
|
|
#include <linux/io.h>
|
|
|
|
|
#include <linux/dmi.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
|
#include <linux/kobject.h>
|
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
#include <linux/types.h>
|
|
|
|
|
#include <uapi/linux/stat.h>
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
|
#include <linux/jiffies.h>
|
|
|
|
|
|
|
|
|
|
static int majorNumber;
|
|
|
|
|
|
|
|
|
|
#define CLASS_NAME "seastone2_fpga"
|
|
|
|
|
#define DRIVER_NAME "switchboard"
|
|
|
|
|
#define FPGA_PCI_NAME "Seastone2_fpga_pci"
|
|
|
|
|
#define DEVICE_NAME "fwupgrade"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int smbus_access(struct i2c_adapter *adapter, u16 addr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned short flags, char rw, u8 cmd,
|
|
|
|
|
int size, union i2c_smbus_data *data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned short flags, char rw, u8 cmd,
|
|
|
|
|
int size, union i2c_smbus_data *data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
static int fpgafw_init(void);
|
|
|
|
|
static void fpgafw_exit(void);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
========================================
|
|
|
|
|
FPGA PCIe BAR 0 Registers
|
|
|
|
|
========================================
|
|
|
|
|
Misc Control 0x00000000 – 0x000000FF.
|
|
|
|
|
I2C_CH1 0x00000100 - 0x00000110
|
|
|
|
|
I2C_CH2 0x00000200 - 0x00000210.
|
|
|
|
|
I2C_CH3 0x00000300 - 0x00000310.
|
|
|
|
|
I2C_CH4 0x00000400 - 0x00000410.
|
|
|
|
|
I2C_CH5 0x00000500 - 0x00000510.
|
|
|
|
|
I2C_CH6 0x00000600 - 0x00000610.
|
|
|
|
|
I2C_CH7 0x00000700 - 0x00000710.
|
|
|
|
|
I2C_CH8 0x00000800 - 0x00000810.
|
|
|
|
|
I2C_CH9 0x00000900 - 0x00000910.
|
|
|
|
|
I2C_CH10 0x00000A00 - 0x00000A10.
|
|
|
|
|
SPI Master 0x00001200 - 0x00001300.
|
|
|
|
|
PORT XCVR 0x00004000 - 0x00004FFF.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* MISC */
|
|
|
|
|
#define FPGA_VERSION 0x0000
|
|
|
|
|
#define FPGA_VERSION_MJ_MSK 0xff00
|
|
|
|
|
#define FPGA_VERSION_MN_MSK 0x00ff
|
|
|
|
|
#define FPGA_SCRATCH 0x0004
|
|
|
|
|
#define FPGA_BROAD_TYPE 0x0008
|
|
|
|
|
#define FPGA_BROAD_REV_MSK 0x0038
|
|
|
|
|
#define FPGA_BROAD_ID_MSK 0x0007
|
|
|
|
|
#define FPGA_PLL_STATUS 0x0014
|
|
|
|
|
#define BMC_I2C_SCRATCH 0x0020
|
|
|
|
|
#define FPGA_SLAVE_CPLD_REST 0x0030
|
|
|
|
|
#define FPGA_PERIPH_RESET_CTRL 0x0034
|
|
|
|
|
#define FPGA_INT_STATUS 0x0040
|
|
|
|
|
#define FPGA_INT_SRC_STATUS 0x0044
|
|
|
|
|
#define FPGA_INT_FLAG 0x0048
|
|
|
|
|
#define FPGA_INT_MASK 0x004c
|
|
|
|
|
#define FPGA_MISC_CTRL 0x0050
|
|
|
|
|
#define FPGA_MISC_STATUS 0x0054
|
|
|
|
|
#define FPGA_AVS_VID_STATUS 0x0068
|
|
|
|
|
#define FPGA_FEATURE_CARD_GPIO 0x0070
|
|
|
|
|
#define FPGA_PORT_XCVR_READY 0x000c
|
|
|
|
|
|
|
|
|
|
/* I2C_MASTER BASE ADDR */
|
|
|
|
|
#define I2C_MASTER_FREQ_1 0x0100
|
|
|
|
|
#define I2C_MASTER_CTRL_1 0x0104
|
|
|
|
|
#define I2C_MASTER_STATUS_1 0x0108
|
|
|
|
|
#define I2C_MASTER_DATA_1 0x010c
|
|
|
|
|
#define I2C_MASTER_PORT_ID_1 0x0110
|
|
|
|
|
#define I2C_MASTER_CH_1 1
|
|
|
|
|
#define I2C_MASTER_CH_2 2
|
|
|
|
|
#define I2C_MASTER_CH_3 3
|
|
|
|
|
#define I2C_MASTER_CH_4 4
|
|
|
|
|
#define I2C_MASTER_CH_5 5
|
|
|
|
|
#define I2C_MASTER_CH_6 6
|
|
|
|
|
#define I2C_MASTER_CH_7 7
|
|
|
|
|
#define I2C_MASTER_CH_8 8
|
|
|
|
|
#define I2C_MASTER_CH_9 9
|
|
|
|
|
#define I2C_MASTER_CH_10 10
|
|
|
|
|
#define I2C_MASTER_CH_TOTAL I2C_MASTER_CH_10
|
|
|
|
|
|
|
|
|
|
/* SPI_MASTER */
|
|
|
|
|
#define SPI_MASTER_WR_EN 0x1200 /* one bit */
|
|
|
|
|
#define SPI_MASTER_WR_DATA 0x1204 /* 32 bits */
|
|
|
|
|
#define SPI_MASTER_CHK_ID 0x1208 /* one bit */
|
|
|
|
|
#define SPI_MASTER_VERIFY 0x120c /* one bit */
|
|
|
|
|
#define SPI_MASTER_STATUS 0x1210 /* 15 bits */
|
|
|
|
|
#define SPI_MASTER_MODULE_RST 0x1214 /* one bit */
|
|
|
|
|
|
|
|
|
|
/* FPGA FRONT PANEL PORT MGMT */
|
|
|
|
|
#define SFF_PORT_CTRL_BASE 0x4000
|
|
|
|
|
#define SFF_PORT_STATUS_BASE 0x4004
|
|
|
|
|
#define SFF_PORT_INT_STATUS_BASE 0x4008
|
|
|
|
|
#define SFF_PORT_INT_MASK_BASE 0x400c
|
|
|
|
|
|
|
|
|
|
#define PORT_XCVR_REGISTER_SIZE 0x1000
|
|
|
|
|
|
|
|
|
|
/* PORT CTRL REGISTER
|
|
|
|
|
[31:7] RSVD
|
|
|
|
|
[6] LPMOD 6
|
|
|
|
|
[5] RSVD
|
|
|
|
|
[4] RST 4
|
|
|
|
|
[3:1] RSVD
|
|
|
|
|
[0] TXDIS 0
|
|
|
|
|
*/
|
|
|
|
|
#define CTRL_LPMOD 6
|
|
|
|
|
#define CTRL_RST 4
|
|
|
|
|
#define CTRL_TXDIS 0
|
|
|
|
|
|
|
|
|
|
/* PORT STATUS REGISTER
|
|
|
|
|
[31:6] RSVD
|
|
|
|
|
[5] IRQ 5
|
|
|
|
|
[4] PRESENT 4
|
|
|
|
|
[3] RSVD
|
|
|
|
|
[2] TXFAULT 2
|
|
|
|
|
[1] RXLOS 1
|
|
|
|
|
[0] MODABS 0
|
|
|
|
|
*/
|
|
|
|
|
#define STAT_IRQ 5
|
|
|
|
|
#define STAT_PRESENT 4
|
|
|
|
|
#define STAT_TXFAULT 2
|
|
|
|
|
#define STAT_RXLOS 1
|
|
|
|
|
#define STAT_MODABS 0
|
|
|
|
|
|
|
|
|
|
/* PORT INTRPT REGISTER
|
|
|
|
|
[31:6] RSVD
|
|
|
|
|
[5] INT_N 5
|
|
|
|
|
[4] PRESENT 4
|
|
|
|
|
[3] RSVD
|
|
|
|
|
[2] RSVD
|
|
|
|
|
[1] RXLOS 1
|
|
|
|
|
[0] MODABS 0
|
|
|
|
|
*/
|
|
|
|
|
#define INTR_INT_N 5
|
|
|
|
|
#define INTR_PRESENT 4
|
|
|
|
|
#define INTR_RXLOS 1
|
|
|
|
|
#define INTR_MODABS 0
|
|
|
|
|
|
|
|
|
|
/* PORT INT MASK REGISTER
|
|
|
|
|
[31:6] RSVD
|
|
|
|
|
[5] INT_N 5
|
|
|
|
|
[4] PRESENT 4
|
|
|
|
|
[3] RSVD
|
|
|
|
|
[2] RSVD
|
|
|
|
|
[1] RXLOS_INT 1
|
|
|
|
|
[0] MODABS 0
|
|
|
|
|
*/
|
|
|
|
|
#define MASK_INT_N 5
|
|
|
|
|
#define MASK_PRESENT 4
|
|
|
|
|
#define MASK_RXLOS 1
|
|
|
|
|
#define MASK_MODABS 0
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
I2C_SR_BIT_RXAK = 0,
|
|
|
|
|
I2C_SR_BIT_MIF,
|
|
|
|
|
I2C_SR_BIT_SRW,
|
|
|
|
|
I2C_SR_BIT_BCSTM,
|
|
|
|
|
I2C_SR_BIT_MAL,
|
|
|
|
|
I2C_SR_BIT_MBB,
|
|
|
|
|
I2C_SR_BIT_MAAS,
|
|
|
|
|
I2C_SR_BIT_MCF
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
I2C_CR_BIT_BCST = 0,
|
|
|
|
|
I2C_CR_BIT_RSTA = 2,
|
|
|
|
|
I2C_CR_BIT_TXAK,
|
|
|
|
|
I2C_CR_BIT_MTX,
|
|
|
|
|
I2C_CR_BIT_MSTA,
|
|
|
|
|
I2C_CR_BIT_MIEN,
|
|
|
|
|
I2C_CR_BIT_MEN,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* The function is i2c algorithm implement to allow master access to
|
|
|
|
|
* correct endpoint devices trough the PCA9548 switch devices.
|
|
|
|
|
*
|
|
|
|
|
* FPGA I2C Master [mutex resource]
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* ---------------------------
|
|
|
|
|
* | PCA9548(s) |
|
|
|
|
|
* ---1--2--3--4--5--6--7--8--
|
|
|
|
|
* | | | | | | | |
|
|
|
|
|
* EEPROM ... EEPROM
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define VIRTUAL_I2C_QSFP_PORT 32
|
|
|
|
|
#define VIRTUAL_I2C_SFP_PORT 1
|
|
|
|
|
|
|
|
|
|
#define SFF_PORT_TOTAL VIRTUAL_I2C_QSFP_PORT + VIRTUAL_I2C_SFP_PORT
|
|
|
|
|
|
|
|
|
|
#define VIRTUAL_I2C_BUS_OFFSET 2
|
|
|
|
|
#define CPLD1_SLAVE_ADDR 0x30
|
|
|
|
|
#define CPLD2_SLAVE_ADDR 0x31
|
|
|
|
|
|
|
|
|
|
static struct class* fpgafwclass = NULL; ///< The device-driver class struct pointer
|
|
|
|
|
static struct device* fpgafwdev = NULL; ///< The device-driver device struct pointer
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static struct platform_device *seastone2_dev;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_TEST 0x1af4
|
|
|
|
|
|
|
|
|
|
#ifndef PCI_VENDOR_ID_XILINX
|
|
|
|
|
#define PCI_VENDOR_ID_XILINX 0x10EE
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define FPGA_PCIE_DEVICE_ID 0x7021
|
|
|
|
|
#define TEST_PCIE_DEVICE_ID 0x1110
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_KERN
|
|
|
|
|
#define info(fmt,args...) printk(KERN_INFO "line %3d : "fmt,__LINE__,##args)
|
|
|
|
|
#define check(REG) printk(KERN_INFO "line %3d : %-8s = %2.2X",__LINE__,#REG,ioread8(REG));
|
|
|
|
|
#else
|
|
|
|
|
#define info(fmt,args...)
|
|
|
|
|
#define check(REG)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define GET_REG_BIT(REG,BIT) ((ioread8(REG) >> BIT) & 0x01)
|
|
|
|
|
#define SET_REG_BIT_H(REG,BIT) iowrite8(ioread8(REG) | (0x01 << BIT),REG)
|
|
|
|
|
#define SET_REG_BIT_L(REG,BIT) iowrite8(ioread8(REG) & ~(0x01 << BIT),REG)
|
|
|
|
|
|
|
|
|
|
static struct mutex fpga_i2c_master_locks[I2C_MASTER_CH_TOTAL];
|
|
|
|
|
/* Store lasted switch address and channel */
|
|
|
|
|
static uint16_t fpga_i2c_lasted_access_port[I2C_MASTER_CH_TOTAL];
|
|
|
|
|
|
|
|
|
|
enum PORT_TYPE {
|
|
|
|
|
NONE,
|
|
|
|
|
QSFP,
|
|
|
|
|
SFP
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
struct i2c_switch {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
unsigned char master_bus; // I2C bus number
|
|
|
|
|
unsigned char switch_addr; // PCA9548 device address, 0xFF if directly connect to a bus.
|
|
|
|
|
unsigned char channel; // PCA9548 channel number. If the switch_addr is 0xFF, this value is ignored.
|
|
|
|
|
enum PORT_TYPE port_type; // QSFP/SFP tranceiver port type.
|
|
|
|
|
char calling_name[20]; // Calling name.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct i2c_dev_data {
|
|
|
|
|
int portid;
|
|
|
|
|
struct i2c_switch pca9548;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* PREDEFINED I2C SWITCH DEVICE TOPOLOGY */
|
|
|
|
|
static struct i2c_switch fpga_i2c_bus_dev[] = {
|
|
|
|
|
/* BUS2 QSFP Exported as virtual bus */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
{I2C_MASTER_CH_2, 0x72, 0, QSFP, "QSFP1"}, {I2C_MASTER_CH_2, 0x72, 1, QSFP, "QSFP2"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x72, 2, QSFP, "QSFP3"}, {I2C_MASTER_CH_2, 0x72, 3, QSFP, "QSFP4"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x72, 4, QSFP, "QSFP5"}, {I2C_MASTER_CH_2, 0x72, 5, QSFP, "QSFP6"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x72, 6, QSFP, "QSFP7"}, {I2C_MASTER_CH_2, 0x72, 7, QSFP, "QSFP8"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x73, 0, QSFP, "QSFP9"}, {I2C_MASTER_CH_2, 0x73, 1, QSFP, "QSFP10"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x73, 2, QSFP, "QSFP11"}, {I2C_MASTER_CH_2, 0x73, 3, QSFP, "QSFP12"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x73, 4, QSFP, "QSFP13"}, {I2C_MASTER_CH_2, 0x73, 5, QSFP, "QSFP14"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x73, 6, QSFP, "QSFP15"}, {I2C_MASTER_CH_2, 0x73, 7, QSFP, "QSFP16"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x74, 0, QSFP, "QSFP17"}, {I2C_MASTER_CH_2, 0x74, 1, QSFP, "QSFP18"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x74, 2, QSFP, "QSFP19"}, {I2C_MASTER_CH_2, 0x74, 3, QSFP, "QSFP20"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x74, 4, QSFP, "QSFP21"}, {I2C_MASTER_CH_2, 0x74, 5, QSFP, "QSFP22"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x74, 6, QSFP, "QSFP23"}, {I2C_MASTER_CH_2, 0x74, 7, QSFP, "QSFP24"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x75, 0, QSFP, "QSFP25"}, {I2C_MASTER_CH_2, 0x75, 1, QSFP, "QSFP26"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x75, 2, QSFP, "QSFP27"}, {I2C_MASTER_CH_2, 0x75, 3, QSFP, "QSFP28"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x75, 4, QSFP, "QSFP29"}, {I2C_MASTER_CH_2, 0x75, 5, QSFP, "QSFP30"},
|
|
|
|
|
{I2C_MASTER_CH_2, 0x75, 6, QSFP, "QSFP31"}, {I2C_MASTER_CH_2, 0x75, 7, QSFP, "QSFP32"},
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/* BUS1 SFP+ Exported as virtual bus */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
{I2C_MASTER_CH_1, 0x72, 0, SFP, "SFP1"},
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/* BUS3 Switchboard CPLD */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
{I2C_MASTER_CH_3, 0xFF, 0, NONE, "I2C_3"},
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define VIRTUAL_I2C_PORT_LENGTH ARRAY_SIZE(fpga_i2c_bus_dev)
|
|
|
|
|
#define VIRTUAL_I2C_CPLD_INDEX SFF_PORT_TOTAL
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
struct fpga_device {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/* data mmio region */
|
|
|
|
|
void __iomem *data_base_addr;
|
|
|
|
|
resource_size_t data_mmio_start;
|
|
|
|
|
resource_size_t data_mmio_len;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct fpga_device fpga_dev = {
|
|
|
|
|
.data_base_addr = 0,
|
|
|
|
|
.data_mmio_start = 0,
|
|
|
|
|
.data_mmio_len = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct seastone2_fpga_data {
|
|
|
|
|
struct device *sff_devices[SFF_PORT_TOTAL];
|
|
|
|
|
struct i2c_client *sff_i2c_clients[SFF_PORT_TOTAL];
|
|
|
|
|
struct i2c_adapter *i2c_adapter[VIRTUAL_I2C_PORT_LENGTH];
|
|
|
|
|
struct mutex fpga_lock; // For FPGA internal lock
|
|
|
|
|
void __iomem * fpga_read_addr;
|
|
|
|
|
uint8_t cpld1_read_addr;
|
|
|
|
|
uint8_t cpld2_read_addr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct sff_device_data {
|
|
|
|
|
int portid;
|
|
|
|
|
enum PORT_TYPE port_type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct seastone2_fpga_data *fpga_data;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Kernel object for other module drivers.
|
|
|
|
|
* Other module can use these kobject as a parent.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static struct kobject *fpga = NULL;
|
|
|
|
|
static struct kobject *cpld1 = NULL;
|
|
|
|
|
static struct kobject *cpld2 = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Device node in sysfs tree.
|
|
|
|
|
*/
|
|
|
|
|
static struct device *sff_dev = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
2020-09-09 12:32:56 -05:00
|
|
|
|
* Show the value of the register set by 'set_fpga_reg_address'
|
|
|
|
|
* If the address is not set by 'set_fpga_reg_address' first,
|
|
|
|
|
* The version register is selected by default.
|
|
|
|
|
* @param buf register value in hextring
|
|
|
|
|
* @return number of bytes read, or an error code
|
2020-01-15 20:42:09 -06:00
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t get_fpga_reg_value(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// read data from the address
|
|
|
|
|
uint32_t data;
|
|
|
|
|
data = ioread32(fpga_data->fpga_read_addr);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return sprintf(buf, "0x%8.8x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/**
|
2020-09-09 12:32:56 -05:00
|
|
|
|
* Store the register address
|
|
|
|
|
* @param buf address wanted to be read value of
|
|
|
|
|
* @return number of bytes stored, or an error code
|
2020-01-15 20:42:09 -06:00
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t set_fpga_reg_address(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
uint32_t addr;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou32(buf, 0, &addr);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
fpga_data->fpga_read_addr = fpga_dev.data_base_addr + addr;
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/**
|
2020-09-09 12:32:56 -05:00
|
|
|
|
* Show value of fpga scratch register
|
|
|
|
|
* @param buf register value in hexstring
|
|
|
|
|
* @return number of bytes read, or an error code
|
2020-01-15 20:42:09 -06:00
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t get_fpga_scratch(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint32_t data;
|
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + FPGA_SCRATCH);
|
|
|
|
|
data &= 0xffffffff;
|
|
|
|
|
return sprintf(buf, "0x%8.8x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/**
|
2020-09-09 12:32:56 -05:00
|
|
|
|
* Store value of fpga scratch register
|
|
|
|
|
* @param buf scratch register value passing from user space
|
|
|
|
|
* @return number of bytes stored, or an error code
|
2020-01-15 20:42:09 -06:00
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t set_fpga_scratch(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
uint32_t data;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
status = kstrtou32(buf, 0, &data);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
iowrite32(data, fpga_dev.data_base_addr + FPGA_SCRATCH);
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
/**
|
2020-09-09 12:32:56 -05:00
|
|
|
|
* Store a value in a specific register address
|
|
|
|
|
* @param buf the value and address in format '0xhhhh 0xhhhhhhhh'
|
|
|
|
|
* @return number of bytes sent by user space, or an error code
|
2020-01-15 20:42:09 -06:00
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t set_fpga_reg_value(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
//register is 4 bytes
|
|
|
|
|
uint32_t addr;
|
|
|
|
|
uint32_t value;
|
|
|
|
|
uint32_t mode = 8;
|
|
|
|
|
char *tok;
|
|
|
|
|
char clone[count];
|
|
|
|
|
char *pclone = clone;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
strcpy(clone, buf);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou32(tok, 0, &addr);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou32(tok, 0, &value);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mode = 32;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
|
|
|
|
status = kstrtou32(tok, 0, &mode);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (mode == 32) {
|
|
|
|
|
iowrite32(value, fpga_dev.data_base_addr + addr);
|
|
|
|
|
} else if (mode == 8) {
|
|
|
|
|
iowrite8(value, fpga_dev.data_base_addr + addr);
|
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Show FPGA port XCVR ready status
|
|
|
|
|
*/
|
|
|
|
|
static ssize_t ready_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
unsigned int REGISTER = FPGA_PORT_XCVR_READY;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> 0) & 1U);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FPGA attributes */
|
|
|
|
|
static DEVICE_ATTR( getreg, 0600, get_fpga_reg_value, set_fpga_reg_address);
|
|
|
|
|
static DEVICE_ATTR( scratch, 0600, get_fpga_scratch, set_fpga_scratch);
|
|
|
|
|
static DEVICE_ATTR( setreg, 0200, NULL , set_fpga_reg_value);
|
|
|
|
|
static DEVICE_ATTR_RO(ready);
|
|
|
|
|
|
|
|
|
|
static struct attribute *fpga_attrs[] = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&dev_attr_getreg.attr,
|
|
|
|
|
&dev_attr_scratch.attr,
|
|
|
|
|
&dev_attr_setreg.attr,
|
|
|
|
|
&dev_attr_ready.attr,
|
|
|
|
|
NULL,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct attribute_group fpga_attr_grp = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
.attrs = fpga_attrs,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* SW CPLDs attributes */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld1_getreg_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
|
|
|
|
uint8_t data;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00,
|
|
|
|
|
I2C_SMBUS_READ, fpga_data->cpld1_read_addr,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
static ssize_t cpld1_getreg_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
uint8_t addr;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
status = kstrtou8(buf, 0, &addr);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
fpga_data->cpld1_read_addr = addr;
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld1_scratch_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t data;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
static ssize_t cpld1_scratch_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t data;
|
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
status = kstrtou8(buf, 0, &data);
|
|
|
|
|
if (status != 0) {
|
|
|
|
|
return status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld1_setreg_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t addr, value;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
char *tok;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
char clone[count];
|
2020-01-15 20:42:09 -06:00
|
|
|
|
char *pclone = clone;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
strcpy(clone, buf);
|
|
|
|
|
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou8(tok, 0, &addr);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou8(tok, 0, &value);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
struct device_attribute dev_attr_cpld1_getreg = __ATTR(getreg, 0600, cpld1_getreg_show, cpld1_getreg_store);
|
|
|
|
|
struct device_attribute dev_attr_cpld1_scratch = __ATTR(scratch, 0600, cpld1_scratch_show, cpld1_scratch_store);
|
|
|
|
|
struct device_attribute dev_attr_cpld1_setreg = __ATTR(setreg, 0200, NULL, cpld1_setreg_store);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
static struct attribute *cpld1_attrs[] = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&dev_attr_cpld1_getreg.attr,
|
|
|
|
|
&dev_attr_cpld1_scratch.attr,
|
|
|
|
|
&dev_attr_cpld1_setreg.attr,
|
|
|
|
|
NULL,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct attribute_group cpld1_attr_grp = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
.attrs = cpld1_attrs,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld2_getreg_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
|
|
|
|
uint8_t data;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, fpga_data->cpld2_read_addr,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
static ssize_t cpld2_getreg_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t addr;
|
|
|
|
|
ssize_t status;
|
|
|
|
|
|
|
|
|
|
status = kstrtou8(buf, 0, &addr);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
fpga_data->cpld2_read_addr = addr;
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld2_scratch_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t data;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x01,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
static ssize_t cpld2_scratch_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// CPLD register is one byte
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t data;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou8(buf, 0, &data);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x01,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&data);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t cpld2_setreg_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint8_t addr, value;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
char *tok;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
char clone[count];
|
2020-01-15 20:42:09 -06:00
|
|
|
|
char *pclone = clone;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ssize_t status;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
strcpy(clone, buf);
|
|
|
|
|
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou8(tok, 0, &addr);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
tok = strsep((char**)&pclone, " ");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (tok == NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou8(tok, 0, &value);
|
|
|
|
|
if (status != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, addr,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&value);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
|
|
|
|
struct device_attribute dev_attr_cpld2_getreg = __ATTR(getreg, 0600, cpld2_getreg_show, cpld2_getreg_store);
|
|
|
|
|
struct device_attribute dev_attr_cpld2_scratch = __ATTR(scratch, 0600, cpld2_scratch_show, cpld2_scratch_store);
|
|
|
|
|
struct device_attribute dev_attr_cpld2_setreg = __ATTR(setreg, 0200, NULL, cpld2_setreg_store);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
static struct attribute *cpld2_attrs[] = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&dev_attr_cpld2_getreg.attr,
|
|
|
|
|
&dev_attr_cpld2_scratch.attr,
|
|
|
|
|
&dev_attr_cpld2_setreg.attr,
|
|
|
|
|
NULL,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct attribute_group cpld2_attr_grp = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
.attrs = cpld2_attrs,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* QSFP/SFP+ attributes */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_modirq_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> STAT_IRQ) & 1U);
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RO(qsfp_modirq);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_modprs_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> STAT_PRESENT) & 1U);
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RO(qsfp_modprs);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t sfp_txfault_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> STAT_TXFAULT) & 1U);
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RO(sfp_txfault);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t sfp_rxlos_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> STAT_RXLOS) & 1U);
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RO(sfp_rxlos);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t sfp_modabs_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_STATUS_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> STAT_MODABS) & 1U);
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RO(sfp_modabs);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_lpmode_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> CTRL_LPMOD) & 1U);
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_lpmode_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint32_t value;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
if (status == 0) {
|
|
|
|
|
// check if value is 0 clear
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
if (!value)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
data = data & ~( (u32)0x1 << CTRL_LPMOD);
|
|
|
|
|
else
|
|
|
|
|
data = data | ((u32)0x1 << CTRL_LPMOD);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite32(data, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(qsfp_lpmode);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_reset_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> CTRL_RST) & 1U);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_reset_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
uint32_t value;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
if (status == 0) {
|
|
|
|
|
// check if value is 0 clear
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
if (!value)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
data = data & ~( (u32)0x1 << CTRL_RST);
|
|
|
|
|
else
|
|
|
|
|
data = data | ((u32)0x1 << CTRL_RST);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite32(data, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(qsfp_reset);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t qsfp_isr_flags_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
u8 data;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Unify the return pattern to 2-bit
|
|
|
|
|
* [1] : module interrupt
|
|
|
|
|
* [0] : presence
|
|
|
|
|
*/
|
|
|
|
|
data = data & valid_bits;
|
|
|
|
|
data = data >> 4;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t qsfp_isr_flags_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
|
|
|
|
u32 value;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
value = value << 4;
|
|
|
|
|
value = value & valid_bits;
|
|
|
|
|
iowrite32(value, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(qsfp_isr_flags);
|
|
|
|
|
|
|
|
|
|
static ssize_t qsfp_isr_mask_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Unify the return pattern to 2-bit
|
|
|
|
|
* [1] : module interrupt
|
|
|
|
|
* [0] : presence
|
|
|
|
|
*/
|
|
|
|
|
data = data & valid_bits;
|
|
|
|
|
data = data >> 4;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t qsfp_isr_mask_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
|
|
|
|
u32 value;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
value = value << 4;
|
|
|
|
|
value = value & valid_bits;
|
|
|
|
|
iowrite32(value, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(qsfp_isr_mask);
|
|
|
|
|
|
|
|
|
|
static ssize_t sfp_txdisable_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return sprintf(buf, "%d\n", (data >> CTRL_TXDIS) & 1U);
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t sfp_txdisable_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
|
|
|
|
long value;
|
|
|
|
|
u32 data;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned int REGISTER = SFF_PORT_CTRL_BASE + (portid - 1) * 0x10;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
status = kstrtol(buf, 0, &value);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
// check if value is 0 clear
|
2020-09-09 12:32:56 -05:00
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
if (!value)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
data = data & ~( (u32)0x1 << CTRL_TXDIS);
|
|
|
|
|
else
|
|
|
|
|
data = data | ((u32)0x1 << CTRL_TXDIS);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite32(data, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(sfp_txdisable);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t sfp_isr_flags_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
u8 data;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
data = (u8) ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
|
|
|
|
|
data = data & valid_bits;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t sfp_isr_flags_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
|
|
|
|
u32 value;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_STATUS_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_INT_N) | BIT(INTR_PRESENT);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
value = value & valid_bits;
|
|
|
|
|
iowrite32(value, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(sfp_isr_flags);
|
|
|
|
|
|
|
|
|
|
static ssize_t sfp_isr_mask_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
|
{
|
|
|
|
|
u32 data;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
data = ioread32(fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
|
|
|
|
|
data = data & valid_bits;
|
|
|
|
|
|
|
|
|
|
return sprintf(buf, "0x%2.2x\n", data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t sfp_isr_mask_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
|
|
|
|
{
|
|
|
|
|
ssize_t status;
|
|
|
|
|
u32 value;
|
|
|
|
|
u8 valid_bits;
|
|
|
|
|
struct sff_device_data *dev_data = dev_get_drvdata(dev);
|
|
|
|
|
unsigned int portid = dev_data->portid;
|
|
|
|
|
unsigned int REGISTER = SFF_PORT_INT_MASK_BASE + (portid - 1) * 0x10;
|
|
|
|
|
valid_bits = BIT(INTR_RXLOS) | BIT(INTR_MODABS);
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
status = kstrtou32(buf, 0, &value);
|
|
|
|
|
if (status == 0) {
|
|
|
|
|
value = value & valid_bits;
|
|
|
|
|
iowrite32(value, fpga_dev.data_base_addr + REGISTER);
|
|
|
|
|
status = count;
|
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(sfp_isr_mask);
|
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
static struct attribute *sff_attrs[] = {
|
|
|
|
|
&dev_attr_qsfp_modirq.attr,
|
|
|
|
|
&dev_attr_qsfp_modprs.attr,
|
|
|
|
|
&dev_attr_qsfp_lpmode.attr,
|
|
|
|
|
&dev_attr_qsfp_reset.attr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&dev_attr_qsfp_isr_flags.attr,
|
|
|
|
|
&dev_attr_qsfp_isr_mask.attr,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
&dev_attr_sfp_txfault.attr,
|
|
|
|
|
&dev_attr_sfp_rxlos.attr,
|
|
|
|
|
&dev_attr_sfp_modabs.attr,
|
|
|
|
|
&dev_attr_sfp_txdisable.attr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&dev_attr_sfp_isr_flags.attr,
|
|
|
|
|
&dev_attr_sfp_isr_mask.attr,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct attribute_group sff_attr_grp = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
.attrs = sff_attrs,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct attribute_group *sff_attr_grps[] = {
|
|
|
|
|
&sff_attr_grp,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t port_led_mode_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// value can be "nomal", "test"
|
2020-09-09 12:32:56 -05:00
|
|
|
|
__u8 led_mode_1, led_mode_2;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x09,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_2);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return sprintf(buf, "%s %s\n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
led_mode_1 ? "test" : "normal",
|
|
|
|
|
led_mode_2 ? "test" : "normal");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t port_led_mode_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
__u8 led_mode_1;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (sysfs_streq(buf, "test")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_mode_1 = 0x01;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else if (sysfs_streq(buf, "normal")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_mode_1 = 0x00;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1);
|
|
|
|
|
status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_WRITE, 0x09,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_mode_1);
|
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(port_led_mode);
|
|
|
|
|
|
|
|
|
|
// Only work when port_led_mode set to 1
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t port_led_color_show(struct device *dev,
|
|
|
|
|
struct device_attribute *attr, char *buf)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
// value can be "off", "green", "amber", "both"
|
2020-09-09 12:32:56 -05:00
|
|
|
|
__u8 led_color1, led_color2;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color1);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00, I2C_SMBUS_READ, 0x0A,
|
|
|
|
|
I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&led_color2);
|
|
|
|
|
if (err < 0)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
|
|
|
|
return sprintf(buf, "%s %s\n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
led_color1 == 0x03 ? "off" : led_color1 == 0x02 ? "green" : led_color1 == 0x01 ? "amber" : "both",
|
|
|
|
|
led_color2 == 0x03 ? "off" : led_color2 == 0x02 ? "green" : led_color2 == 0x01 ? "amber" : "both");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static ssize_t port_led_color_store(struct device *dev,
|
|
|
|
|
struct device_attribute *attr,
|
|
|
|
|
const char *buf, size_t count)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
__u8 led_color;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (sysfs_streq(buf, "off")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_color = 0x03;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else if (sysfs_streq(buf, "green")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_color = 0x02;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else if (sysfs_streq(buf, "amber")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_color = 0x01;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else if (sysfs_streq(buf, "both")) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
led_color = 0x00;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
status = -EINVAL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD1_SLAVE_ADDR, 0x00,
|
|
|
|
|
I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA,
|
|
|
|
|
(union i2c_smbus_data*)&led_color);
|
|
|
|
|
status = fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX],
|
|
|
|
|
CPLD2_SLAVE_ADDR, 0x00,
|
|
|
|
|
I2C_SMBUS_WRITE, 0x0A, I2C_SMBUS_BYTE_DATA,
|
|
|
|
|
(union i2c_smbus_data*)&led_color);
|
|
|
|
|
return count;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
DEVICE_ATTR_RW(port_led_color);
|
|
|
|
|
|
|
|
|
|
static struct attribute *sff_led_test[] = {
|
|
|
|
|
&dev_attr_port_led_mode.attr,
|
|
|
|
|
&dev_attr_port_led_color.attr,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct attribute_group sff_led_test_grp = {
|
2020-09-09 12:32:56 -05:00
|
|
|
|
.attrs = sff_led_test,
|
2020-01-15 20:42:09 -06:00
|
|
|
|
};
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static struct device * seastone2_sff_init(int portid) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
struct sff_device_data *new_data;
|
|
|
|
|
struct device *new_device;
|
|
|
|
|
|
|
|
|
|
new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
|
|
|
|
|
if (!new_data) {
|
|
|
|
|
printk(KERN_ALERT "Cannot alloc sff device data @port%d", portid);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
/* The QSFP port ID start from 1 */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
new_data->portid = portid + 1;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
new_data->port_type = fpga_i2c_bus_dev[portid].port_type;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
new_device = device_create_with_groups(fpgafwclass, sff_dev,
|
|
|
|
|
MKDEV(0, 0), new_data, sff_attr_grps, "%s",
|
|
|
|
|
fpga_i2c_bus_dev[portid].calling_name);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
if (IS_ERR(new_device)) {
|
|
|
|
|
printk(KERN_ALERT "Cannot create sff device @port%d", portid);
|
|
|
|
|
kfree(new_data);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return new_device;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static int i2c_wait_ack(struct i2c_adapter *a, unsigned long timeout, int writing) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int error = 0;
|
|
|
|
|
int Status;
|
|
|
|
|
|
|
|
|
|
struct i2c_dev_data *new_data = i2c_get_adapdata(a);
|
|
|
|
|
void __iomem *pci_bar = fpga_dev.data_base_addr;
|
|
|
|
|
|
|
|
|
|
unsigned int REG_FDR0;
|
|
|
|
|
unsigned int REG_CR0;
|
|
|
|
|
unsigned int REG_SR0;
|
|
|
|
|
unsigned int REG_DR0;
|
|
|
|
|
unsigned int REG_ID0;
|
|
|
|
|
|
|
|
|
|
unsigned int master_bus = new_data->pca9548.master_bus;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
error = -EINVAL;
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
check(pci_bar + REG_SR0);
|
|
|
|
|
check(pci_bar + REG_CR0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
timeout = jiffies + msecs_to_jiffies(timeout);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
while (1) {
|
|
|
|
|
Status = ioread8(pci_bar + REG_SR0);
|
|
|
|
|
if (jiffies > timeout) {
|
|
|
|
|
info("Status %2.2X", Status);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info("Error Timeout");
|
|
|
|
|
error = -ETIMEDOUT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (Status & (1 << I2C_SR_BIT_MIF)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (writing == 0 && (Status & (1 << I2C_SR_BIT_MCF))) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
Status = ioread8(pci_bar + REG_SR0);
|
|
|
|
|
iowrite8(0, pci_bar + REG_SR0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (error < 0) {
|
|
|
|
|
info("Status %2.2X", Status);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!(Status & (1 << I2C_SR_BIT_MCF))) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info("Error Unfinish");
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (Status & (1 << I2C_SR_BIT_MAL)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info("Error MAL");
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (Status & (1 << I2C_SR_BIT_RXAK)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info( "SL No Acknowlege");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (writing) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info("Error No Acknowlege");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return -ENXIO;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
info( "SL Acknowlege");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int smbus_access(struct i2c_adapter *adapter, u16 addr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned short flags, char rw, u8 cmd,
|
|
|
|
|
int size, union i2c_smbus_data *data)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
2020-09-09 12:32:56 -05:00
|
|
|
|
int error = 0;
|
|
|
|
|
int cnt = 0;
|
|
|
|
|
int bid = 0;
|
|
|
|
|
struct i2c_dev_data *dev_data;
|
|
|
|
|
void __iomem *pci_bar;
|
|
|
|
|
unsigned int portid, master_bus;
|
|
|
|
|
|
|
|
|
|
unsigned int REG_FDR0;
|
|
|
|
|
unsigned int REG_CR0;
|
|
|
|
|
unsigned int REG_SR0;
|
|
|
|
|
unsigned int REG_DR0;
|
|
|
|
|
unsigned int REG_ID0;
|
|
|
|
|
|
|
|
|
|
REG_FDR0 = 0;
|
|
|
|
|
REG_CR0 = 0;
|
|
|
|
|
REG_SR0 = 0;
|
|
|
|
|
REG_DR0 = 0;
|
|
|
|
|
REG_ID0 = 0;
|
|
|
|
|
|
|
|
|
|
/* Write the command register */
|
|
|
|
|
dev_data = i2c_get_adapdata(adapter);
|
|
|
|
|
portid = dev_data->portid;
|
|
|
|
|
pci_bar = fpga_dev.data_base_addr;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_KERN
|
2020-09-09 12:32:56 -05:00
|
|
|
|
printk(KERN_INFO "portid %2d|@ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-15s|CMD %2.2X "
|
|
|
|
|
, portid, addr, flags, rw, rw == 1 ? "READ " : "WRITE"
|
|
|
|
|
, size, size == 0 ? "QUICK" :
|
|
|
|
|
size == 1 ? "BYTE" :
|
|
|
|
|
size == 2 ? "BYTE_DATA" :
|
|
|
|
|
size == 3 ? "WORD_DATA" :
|
|
|
|
|
size == 4 ? "PROC_CALL" :
|
|
|
|
|
size == 5 ? "BLOCK_DATA" :
|
|
|
|
|
size == 8 ? "I2C_BLOCK_DATA" : "ERROR"
|
|
|
|
|
, cmd);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#endif
|
2020-09-09 12:32:56 -05:00
|
|
|
|
/* Map the size to what the chip understands */
|
|
|
|
|
switch (size) {
|
|
|
|
|
case I2C_SMBUS_QUICK:
|
|
|
|
|
case I2C_SMBUS_BYTE:
|
|
|
|
|
case I2C_SMBUS_BYTE_DATA:
|
|
|
|
|
case I2C_SMBUS_WORD_DATA:
|
|
|
|
|
case I2C_SMBUS_BLOCK_DATA:
|
|
|
|
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printk(KERN_INFO "Unsupported transaction %d\n", size);
|
|
|
|
|
error = -EOPNOTSUPP;
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
master_bus = dev_data->pca9548.master_bus;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (master_bus < I2C_MASTER_CH_1 || master_bus > I2C_MASTER_CH_TOTAL) {
|
|
|
|
|
error = -ENXIO;
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
REG_FDR0 = I2C_MASTER_FREQ_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_CR0 = I2C_MASTER_CTRL_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_SR0 = I2C_MASTER_STATUS_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_DR0 = I2C_MASTER_DATA_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
REG_ID0 = I2C_MASTER_PORT_ID_1 + (master_bus - 1) * 0x0100;
|
|
|
|
|
|
|
|
|
|
iowrite8(portid, pci_bar + REG_ID0);
|
|
|
|
|
|
|
|
|
|
////[S][ADDR/R]
|
|
|
|
|
// Clear status register
|
|
|
|
|
iowrite8(0, pci_bar + REG_SR0);
|
|
|
|
|
iowrite8(1 << I2C_CR_BIT_MIEN |
|
|
|
|
|
1 << I2C_CR_BIT_MTX |
|
|
|
|
|
1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0);
|
|
|
|
|
SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN);
|
|
|
|
|
|
|
|
|
|
if (rw == I2C_SMBUS_READ &&
|
|
|
|
|
(size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)) {
|
|
|
|
|
// sent device address with Read mode
|
|
|
|
|
iowrite8(addr << 1 | 0x01, pci_bar + REG_DR0);
|
|
|
|
|
} else {
|
|
|
|
|
// sent device address with Write mode
|
|
|
|
|
iowrite8(addr << 1 | 0x00, pci_bar + REG_DR0);
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
info( "MS Start");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
//// Wait {A}
|
|
|
|
|
error = i2c_wait_ack(adapter, 12, 1);
|
|
|
|
|
if (error < 0) {
|
|
|
|
|
info( "get error %d", error);
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
//// [CMD]{A}
|
|
|
|
|
if (size == I2C_SMBUS_BYTE_DATA ||
|
2020-01-15 20:42:09 -06:00
|
|
|
|
size == I2C_SMBUS_WORD_DATA ||
|
|
|
|
|
size == I2C_SMBUS_BLOCK_DATA ||
|
|
|
|
|
size == I2C_SMBUS_I2C_BLOCK_DATA ||
|
2020-09-09 12:32:56 -05:00
|
|
|
|
(size == I2C_SMBUS_BYTE && rw == I2C_SMBUS_WRITE)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// sent command code to data register
|
|
|
|
|
iowrite8(cmd, pci_bar + REG_DR0);
|
|
|
|
|
info( "MS Send CMD 0x%2.2X", cmd);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// Wait {A}
|
|
|
|
|
error = i2c_wait_ack(adapter, 12, 1);
|
|
|
|
|
if (error < 0) {
|
|
|
|
|
info( "get error %d", error);
|
|
|
|
|
goto Done;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
switch (size) {
|
|
|
|
|
case I2C_SMBUS_BYTE_DATA:
|
|
|
|
|
cnt = 1; break;
|
|
|
|
|
case I2C_SMBUS_WORD_DATA:
|
|
|
|
|
cnt = 2; break;
|
|
|
|
|
case I2C_SMBUS_BLOCK_DATA:
|
|
|
|
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
|
|
|
/* In block data modes keep number of byte in block[0] */
|
|
|
|
|
cnt = data->block[0];
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cnt = 0; break;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// [CNT] used only block data write
|
|
|
|
|
if (size == I2C_SMBUS_BLOCK_DATA && rw == I2C_SMBUS_WRITE) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite8(cnt, pci_bar + REG_DR0);
|
|
|
|
|
info( "MS Send CNT 0x%2.2X", cnt);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// Wait {A}
|
|
|
|
|
error = i2c_wait_ack(adapter, 12, 1);
|
|
|
|
|
if (error < 0) {
|
|
|
|
|
info( "get error %d", error);
|
|
|
|
|
goto Done;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// [DATA]{A}
|
|
|
|
|
if ( rw == I2C_SMBUS_WRITE && (
|
|
|
|
|
size == I2C_SMBUS_BYTE ||
|
2020-01-15 20:42:09 -06:00
|
|
|
|
size == I2C_SMBUS_BYTE_DATA ||
|
|
|
|
|
size == I2C_SMBUS_WORD_DATA ||
|
|
|
|
|
size == I2C_SMBUS_BLOCK_DATA ||
|
|
|
|
|
size == I2C_SMBUS_I2C_BLOCK_DATA
|
2020-09-09 12:32:56 -05:00
|
|
|
|
)) {
|
|
|
|
|
bid = 0;
|
|
|
|
|
info( "MS prepare to sent [%d bytes]", cnt);
|
|
|
|
|
if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA) {
|
|
|
|
|
bid = 1; // block[0] is cnt;
|
|
|
|
|
cnt += 1; // offset from block[0]
|
|
|
|
|
}
|
|
|
|
|
for (; bid < cnt; bid++) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite8(data->block[bid], pci_bar + REG_DR0);
|
|
|
|
|
info( " Data > %2.2X", data->block[bid]);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
// Wait {A}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
error = i2c_wait_ack(adapter, 12, 1);
|
|
|
|
|
if (error < 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// REPEATE START
|
|
|
|
|
if ( rw == I2C_SMBUS_READ && (
|
|
|
|
|
size == I2C_SMBUS_BYTE_DATA ||
|
|
|
|
|
size == I2C_SMBUS_WORD_DATA ||
|
|
|
|
|
size == I2C_SMBUS_BLOCK_DATA ||
|
|
|
|
|
size == I2C_SMBUS_I2C_BLOCK_DATA
|
|
|
|
|
)) {
|
|
|
|
|
info( "MS Repeated Start");
|
|
|
|
|
|
|
|
|
|
SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MEN);
|
|
|
|
|
iowrite8(1 << I2C_CR_BIT_MIEN |
|
|
|
|
|
1 << I2C_CR_BIT_MTX |
|
|
|
|
|
1 << I2C_CR_BIT_MSTA |
|
|
|
|
|
1 << I2C_CR_BIT_RSTA , pci_bar + REG_CR0);
|
|
|
|
|
SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_MEN);
|
|
|
|
|
|
|
|
|
|
// sent Address with Read mode
|
|
|
|
|
iowrite8( addr << 1 | 0x1 , pci_bar + REG_DR0);
|
|
|
|
|
|
|
|
|
|
// Wait {A}
|
|
|
|
|
error = i2c_wait_ack(adapter, 12, 1);
|
|
|
|
|
if (error < 0) {
|
|
|
|
|
goto Done;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( rw == I2C_SMBUS_READ && (
|
2020-01-15 20:42:09 -06:00
|
|
|
|
size == I2C_SMBUS_BYTE ||
|
|
|
|
|
size == I2C_SMBUS_BYTE_DATA ||
|
|
|
|
|
size == I2C_SMBUS_WORD_DATA ||
|
|
|
|
|
size == I2C_SMBUS_BLOCK_DATA ||
|
|
|
|
|
size == I2C_SMBUS_I2C_BLOCK_DATA
|
2020-09-09 12:32:56 -05:00
|
|
|
|
)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
switch (size) {
|
|
|
|
|
case I2C_SMBUS_BYTE:
|
|
|
|
|
case I2C_SMBUS_BYTE_DATA:
|
|
|
|
|
cnt = 1; break;
|
|
|
|
|
case I2C_SMBUS_WORD_DATA:
|
|
|
|
|
cnt = 2; break;
|
|
|
|
|
case I2C_SMBUS_BLOCK_DATA:
|
|
|
|
|
// will be changed after recived first data
|
|
|
|
|
cnt = 3; break;
|
|
|
|
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
|
|
|
cnt = data->block[0]; break;
|
|
|
|
|
default:
|
|
|
|
|
cnt = 0; break;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
bid = 0;
|
|
|
|
|
info( "MS Receive");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
//set to Receive mode
|
|
|
|
|
iowrite8(1 << I2C_CR_BIT_MEN |
|
|
|
|
|
1 << I2C_CR_BIT_MIEN |
|
|
|
|
|
1 << I2C_CR_BIT_MSTA , pci_bar + REG_CR0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (bid = -1; bid < cnt; bid++) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// Wait for byte transfer
|
|
|
|
|
error = i2c_wait_ack(adapter, 12, 0);
|
|
|
|
|
if (error < 0) {
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (bid == cnt - 2) {
|
|
|
|
|
info( "SET NAK");
|
|
|
|
|
SET_REG_BIT_H(pci_bar + REG_CR0, I2C_CR_BIT_TXAK);
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (bid < 0) {
|
|
|
|
|
ioread8(pci_bar + REG_DR0);
|
|
|
|
|
info( "READ Dummy Byte" );
|
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (bid == cnt - 1) {
|
|
|
|
|
info ( "SET STOP in read loop");
|
|
|
|
|
SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA);
|
|
|
|
|
}
|
|
|
|
|
if (size == I2C_SMBUS_I2C_BLOCK_DATA) {
|
|
|
|
|
// block[0] is read length
|
|
|
|
|
data->block[bid + 1] = ioread8(pci_bar + REG_DR0);
|
|
|
|
|
} else {
|
|
|
|
|
data->block[bid] = ioread8(pci_bar + REG_DR0);
|
|
|
|
|
}
|
|
|
|
|
info( "DATA IN [%d] %2.2X", bid, data->block[bid]);
|
|
|
|
|
|
|
|
|
|
if (size == I2C_SMBUS_BLOCK_DATA && bid == 0) {
|
|
|
|
|
cnt = data->block[0] + 1;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
// [P]
|
|
|
|
|
SET_REG_BIT_L(pci_bar + REG_CR0, I2C_CR_BIT_MSTA);
|
|
|
|
|
info( "MS STOP");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
Done:
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite8(1 << I2C_CR_BIT_MEN, pci_bar + REG_CR0);
|
|
|
|
|
check(pci_bar + REG_CR0);
|
|
|
|
|
check(pci_bar + REG_SR0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#ifdef DEBUG_KERN
|
2020-09-09 12:32:56 -05:00
|
|
|
|
printk(KERN_INFO "END --- Error code %d", error);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
return error;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wrapper of smbus_access access with PCA9548 I2C switch management.
|
|
|
|
|
* This function set PCA9548 switches to the proper slave channel.
|
|
|
|
|
* Only one channel among switches chip is selected during communication time.
|
|
|
|
|
*
|
|
|
|
|
* Note: If the bus does not have any PCA9548 on it, the switch_addr must be
|
|
|
|
|
* set to 0xFF, it will use normal smbus_access function.
|
|
|
|
|
*/
|
|
|
|
|
static int fpga_i2c_access(struct i2c_adapter *adapter, u16 addr,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
unsigned short flags, char rw, u8 cmd,
|
|
|
|
|
int size, union i2c_smbus_data *data)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
int error = 0;
|
|
|
|
|
struct i2c_dev_data *dev_data;
|
|
|
|
|
unsigned char master_bus;
|
|
|
|
|
unsigned char switch_addr;
|
|
|
|
|
unsigned char channel;
|
|
|
|
|
uint16_t prev_port = 0;
|
|
|
|
|
unsigned char prev_switch;
|
|
|
|
|
unsigned char prev_ch;
|
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
|
|
dev_data = i2c_get_adapdata(adapter);
|
|
|
|
|
master_bus = dev_data->pca9548.master_bus;
|
|
|
|
|
switch_addr = dev_data->pca9548.switch_addr;
|
|
|
|
|
channel = dev_data->pca9548.channel;
|
|
|
|
|
|
|
|
|
|
// Acquire the master resource.
|
|
|
|
|
mutex_lock(&fpga_i2c_master_locks[master_bus - 1]);
|
|
|
|
|
prev_port = fpga_i2c_lasted_access_port[master_bus - 1];
|
|
|
|
|
prev_switch = (unsigned char)(prev_port >> 8) & 0xFF;
|
|
|
|
|
prev_ch = (unsigned char)(prev_port & 0xFF);
|
|
|
|
|
|
|
|
|
|
if (switch_addr != 0xFF) {
|
|
|
|
|
|
|
|
|
|
// Check lasted access switch address on a master
|
|
|
|
|
if ( prev_switch != switch_addr && prev_switch != 0 ) {
|
|
|
|
|
// reset prev_port PCA9548 chip
|
|
|
|
|
retry = 3;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
while (retry--) {
|
|
|
|
|
error = smbus_access(adapter, (u16)(prev_switch), flags,
|
|
|
|
|
I2C_SMBUS_WRITE, 0x00,
|
|
|
|
|
I2C_SMBUS_BYTE, NULL);
|
|
|
|
|
if (error >= 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
break;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
|
|
|
|
dev_dbg(&adapter->dev,
|
|
|
|
|
"Failed to deselect ch %d of 0x%x, CODE %d\n",
|
|
|
|
|
prev_ch, prev_switch, error);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (retry < 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
goto release_unlock;
|
|
|
|
|
}
|
|
|
|
|
// set PCA9548 to current channel
|
|
|
|
|
retry = 3;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
while (retry--) {
|
|
|
|
|
error = smbus_access(adapter, switch_addr, flags,
|
|
|
|
|
I2C_SMBUS_WRITE, 1 << channel,
|
|
|
|
|
I2C_SMBUS_BYTE, NULL);
|
|
|
|
|
if (error >= 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
break;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
|
|
|
|
dev_dbg(&adapter->dev,
|
|
|
|
|
"Failed to deselect ch %d of 0x%x, CODE %d\n",
|
|
|
|
|
prev_ch, prev_switch, error);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (retry < 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
goto release_unlock;
|
|
|
|
|
}
|
|
|
|
|
// update lasted port
|
|
|
|
|
fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// check if channel is also changes
|
|
|
|
|
if ( prev_ch != channel || prev_switch == 0 ) {
|
|
|
|
|
// set new PCA9548 at switch_addr to current
|
|
|
|
|
retry = 3;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
while (retry--) {
|
|
|
|
|
error = smbus_access(adapter, switch_addr, flags,
|
|
|
|
|
I2C_SMBUS_WRITE, 1 << channel,
|
|
|
|
|
I2C_SMBUS_BYTE, NULL);
|
|
|
|
|
if (error >= 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
break;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
|
|
|
|
dev_dbg(&adapter->dev,
|
|
|
|
|
"Failed to deselect ch %d of 0x%x, CODE %d\n",
|
|
|
|
|
prev_ch, prev_switch, error);
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (retry < 0) {
|
|
|
|
|
goto release_unlock;
|
|
|
|
|
}
|
|
|
|
|
// update lasted port
|
|
|
|
|
fpga_i2c_lasted_access_port[master_bus - 1] = switch_addr << 8 | channel;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do SMBus communication
|
|
|
|
|
error = smbus_access(adapter, addr, flags, rw, cmd, size, data);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (error < 0) {
|
|
|
|
|
dev_dbg( &adapter->dev,
|
|
|
|
|
"smbus_xfer failed (%d) @ 0x%2.2X|f 0x%4.4X|(%d)%-5s| (%d)%-10s|CMD %2.2X "
|
|
|
|
|
, error, addr, flags, rw, rw == 1 ? "READ " : "WRITE"
|
|
|
|
|
, size, size == 0 ? "QUICK" :
|
|
|
|
|
size == 1 ? "BYTE" :
|
|
|
|
|
size == 2 ? "BYTE_DATA" :
|
|
|
|
|
size == 3 ? "WORD_DATA" :
|
|
|
|
|
size == 4 ? "PROC_CALL" :
|
|
|
|
|
size == 5 ? "BLOCK_DATA" :
|
|
|
|
|
size == 8 ? "I2C_BLOCK_DATA" : "ERROR"
|
|
|
|
|
, cmd);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
release_unlock:
|
2020-01-15 20:42:09 -06:00
|
|
|
|
mutex_unlock(&fpga_i2c_master_locks[master_bus - 1]);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
dev_dbg(&adapter->dev, "switch ch %d of 0x%x -> ch %d of 0x%x\n",
|
|
|
|
|
prev_ch, prev_switch, channel, switch_addr);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A callback function show available smbus functions.
|
|
|
|
|
*/
|
|
|
|
|
static u32 fpga_i2c_func(struct i2c_adapter *a)
|
|
|
|
|
{
|
|
|
|
|
return I2C_FUNC_SMBUS_QUICK |
|
2020-09-09 12:32:56 -05:00
|
|
|
|
I2C_FUNC_SMBUS_BYTE |
|
|
|
|
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
|
|
|
|
I2C_FUNC_SMBUS_WORD_DATA |
|
|
|
|
|
I2C_FUNC_SMBUS_BLOCK_DATA |
|
|
|
|
|
I2C_FUNC_SMBUS_I2C_BLOCK;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct i2c_algorithm seastone2_i2c_algorithm = {
|
|
|
|
|
.smbus_xfer = fpga_i2c_access,
|
|
|
|
|
.functionality = fpga_i2c_func,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create virtual I2C bus adapter for switch devices
|
|
|
|
|
* @param pdev platform device pointer
|
|
|
|
|
* @param portid virtual i2c port id for switch device mapping
|
|
|
|
|
* @param bus_number_offset bus offset for virtual i2c adapter in system
|
|
|
|
|
* @return i2c adapter.
|
|
|
|
|
*
|
|
|
|
|
* When bus_number_offset is -1, created adapter with dynamic bus number.
|
|
|
|
|
* Otherwise create adapter at i2c bus = bus_number_offset + portid.
|
|
|
|
|
*/
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static struct i2c_adapter * seastone2_i2c_init(struct platform_device *pdev,
|
|
|
|
|
int portid, int bus_number_offset)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
struct i2c_adapter *new_adapter;
|
|
|
|
|
struct i2c_dev_data *new_data;
|
|
|
|
|
void __iomem *i2c_freq_base_reg;
|
|
|
|
|
|
|
|
|
|
new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!new_adapter) {
|
|
|
|
|
printk(KERN_ALERT "Cannot alloc i2c adapter for %s",
|
|
|
|
|
fpga_i2c_bus_dev[portid].calling_name);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_adapter->owner = THIS_MODULE;
|
|
|
|
|
new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
|
|
|
|
new_adapter->algo = &seastone2_i2c_algorithm;
|
|
|
|
|
/* If the bus offset is -1, use dynamic bus number */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (bus_number_offset == -1) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
new_adapter->nr = -1;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
new_adapter->nr = bus_number_offset + portid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!new_data) {
|
|
|
|
|
printk(KERN_ALERT "Cannot alloc i2c data for %s",
|
|
|
|
|
fpga_i2c_bus_dev[portid].calling_name);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
kzfree(new_adapter);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_data->portid = portid;
|
|
|
|
|
new_data->pca9548.master_bus = fpga_i2c_bus_dev[portid].master_bus;
|
|
|
|
|
new_data->pca9548.switch_addr = fpga_i2c_bus_dev[portid].switch_addr;
|
|
|
|
|
new_data->pca9548.channel = fpga_i2c_bus_dev[portid].channel;
|
2020-09-09 12:32:56 -05:00
|
|
|
|
strcpy(new_data->pca9548.calling_name, fpga_i2c_bus_dev[portid].calling_name);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
snprintf(new_adapter->name, sizeof(new_adapter->name),
|
2020-09-09 12:32:56 -05:00
|
|
|
|
"SMBus I2C Adapter PortID: %s", new_data->pca9548.calling_name);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
i2c_freq_base_reg = fpga_dev.data_base_addr + I2C_MASTER_FREQ_1;
|
|
|
|
|
iowrite8(0x07, i2c_freq_base_reg + (new_data->pca9548.master_bus - 1) * 0x100); // 0x07 400kHz
|
|
|
|
|
i2c_set_adapdata(new_adapter, new_data);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
error = i2c_add_numbered_adapter(new_adapter);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (error < 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
printk(KERN_ALERT "Cannot add i2c adapter %s", new_data->pca9548.calling_name);
|
|
|
|
|
kzfree(new_adapter);
|
|
|
|
|
kzfree(new_data);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new_adapter;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Board info for QSFP/SFP+ eeprom.
|
|
|
|
|
* Note: Using sff8436 as I2C eeprom driver.
|
|
|
|
|
*/
|
|
|
|
|
static struct i2c_board_info sff8436_eeprom_info[] = {
|
|
|
|
|
{ I2C_BOARD_INFO("optoe1", 0x50) },
|
|
|
|
|
{ I2C_BOARD_INFO("optoe2", 0x50) },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int seastone2_drv_probe(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
int portid_count;
|
|
|
|
|
uint8_t cpld1_version, cpld2_version;
|
|
|
|
|
uint16_t prev_i2c_switch = 0;
|
|
|
|
|
struct sff_device_data *sff_data;
|
|
|
|
|
|
|
|
|
|
/* The device class need to be instantiated before this function called */
|
|
|
|
|
BUG_ON(fpgafwclass == NULL);
|
|
|
|
|
|
|
|
|
|
fpga_data = devm_kzalloc(&pdev->dev, sizeof(struct seastone2_fpga_data),
|
2020-09-09 12:32:56 -05:00
|
|
|
|
GFP_KERNEL);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
if (!fpga_data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
// Set default read address to VERSION
|
2020-09-09 12:32:56 -05:00
|
|
|
|
fpga_data->fpga_read_addr = fpga_dev.data_base_addr + FPGA_VERSION;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpga_data->cpld1_read_addr = 0x00;
|
|
|
|
|
fpga_data->cpld2_read_addr = 0x00;
|
|
|
|
|
|
|
|
|
|
mutex_init(&fpga_data->fpga_lock);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (ret = I2C_MASTER_CH_1 ; ret <= I2C_MASTER_CH_TOTAL; ret++) {
|
|
|
|
|
mutex_init(&fpga_i2c_master_locks[ret - 1]);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fpga = kobject_create_and_add("FPGA", &pdev->dev.kobj);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!fpga) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = sysfs_create_group(fpga, &fpga_attr_grp);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
printk(KERN_ERR "Cannot create FPGA sysfs attributes\n");
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cpld1 = kobject_create_and_add("CPLD1", &pdev->dev.kobj);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!cpld1) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
ret = sysfs_create_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
printk(KERN_ERR "Cannot create CPLD1 sysfs attributes\n");
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cpld2 = kobject_create_and_add("CPLD2", &pdev->dev.kobj);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (!cpld2) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
ret = sysfs_create_group(cpld2, &cpld2_attr_grp);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
printk(KERN_ERR "Cannot create CPLD2 sysfs attributes\n");
|
|
|
|
|
kobject_put(cpld2);
|
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
sff_dev = device_create(fpgafwclass, NULL, MKDEV(0, 0), NULL, "sff_device");
|
|
|
|
|
if (IS_ERR(sff_dev)) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
printk(KERN_ERR "Failed to create sff device\n");
|
|
|
|
|
sysfs_remove_group(cpld2, &cpld2_attr_grp);
|
|
|
|
|
kobject_put(cpld2);
|
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return PTR_ERR(sff_dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = sysfs_create_group(&sff_dev->kobj, &sff_led_test_grp);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
printk(KERN_ERR "Cannot create SFF attributes\n");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
device_destroy(fpgafwclass, MKDEV(0, 0));
|
2020-01-15 20:42:09 -06:00
|
|
|
|
sysfs_remove_group(cpld2, &cpld2_attr_grp);
|
|
|
|
|
kobject_put(cpld2);
|
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
ret = sysfs_create_link(&pdev->dev.kobj, &sff_dev->kobj, "SFF");
|
|
|
|
|
if (ret != 0) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
device_destroy(fpgafwclass, MKDEV(0, 0));
|
2020-01-15 20:42:09 -06:00
|
|
|
|
sysfs_remove_group(cpld2, &cpld2_attr_grp);
|
|
|
|
|
kobject_put(cpld2);
|
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kzfree(fpga_data);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpga_data->i2c_adapter[portid_count] = seastone2_i2c_init(pdev, portid_count, VIRTUAL_I2C_BUS_OFFSET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Init SFF devices */
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
struct i2c_adapter *i2c_adap = fpga_data->i2c_adapter[portid_count];
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (i2c_adap) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpga_data->sff_devices[portid_count] = seastone2_sff_init(portid_count);
|
|
|
|
|
sff_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]);
|
|
|
|
|
BUG_ON(sff_data == NULL);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if ( sff_data->port_type == QSFP ) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[0]);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
} else {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpga_data->sff_i2c_clients[portid_count] = i2c_new_device(i2c_adap, &sff8436_eeprom_info[1]);
|
|
|
|
|
}
|
|
|
|
|
sff_data = NULL;
|
|
|
|
|
sysfs_create_link(&fpga_data->sff_devices[portid_count]->kobj,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
&fpga_data->sff_i2c_clients[portid_count]->dev.kobj,
|
|
|
|
|
"i2c");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printk(KERN_INFO "Virtual I2C buses created\n");
|
|
|
|
|
|
|
|
|
|
#ifdef TEST_MODE
|
|
|
|
|
return 0;
|
|
|
|
|
#endif
|
2020-09-09 12:32:56 -05:00
|
|
|
|
fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD1_SLAVE_ADDR, 0x00,
|
|
|
|
|
I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld1_version);
|
|
|
|
|
fpga_i2c_access(fpga_data->i2c_adapter[VIRTUAL_I2C_CPLD_INDEX], CPLD2_SLAVE_ADDR, 0x00,
|
|
|
|
|
I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, (union i2c_smbus_data*)&cpld2_version);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
printk(KERN_INFO "CPLD1 VERSON: %2.2x\n", cpld1_version);
|
|
|
|
|
printk(KERN_INFO "CPLD2 VERSON: %2.2x\n", cpld2_version);
|
|
|
|
|
|
|
|
|
|
/* Init I2C buses that has PCA9548 switch device. */
|
|
|
|
|
for (portid_count = 0; portid_count < VIRTUAL_I2C_PORT_LENGTH; portid_count++) {
|
|
|
|
|
|
|
|
|
|
struct i2c_dev_data *dev_data;
|
|
|
|
|
unsigned char master_bus;
|
|
|
|
|
unsigned char switch_addr;
|
|
|
|
|
|
|
|
|
|
dev_data = i2c_get_adapdata(fpga_data->i2c_adapter[portid_count]);
|
|
|
|
|
master_bus = dev_data->pca9548.master_bus;
|
|
|
|
|
switch_addr = dev_data->pca9548.switch_addr;
|
|
|
|
|
|
|
|
|
|
if (switch_addr != 0xFF) {
|
|
|
|
|
|
|
|
|
|
if (prev_i2c_switch != ( (master_bus << 8) | switch_addr) ) {
|
|
|
|
|
// Found the bus with PCA9548, trying to clear all switch in it.
|
|
|
|
|
smbus_access(fpga_data->i2c_adapter[portid_count], switch_addr, 0x00,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
I2C_SMBUS_WRITE, 0x00, I2C_SMBUS_BYTE, NULL);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
prev_i2c_switch = ( master_bus << 8 ) | switch_addr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int seastone2_drv_remove(struct platform_device *pdev)
|
|
|
|
|
{
|
|
|
|
|
int portid_count;
|
|
|
|
|
struct sff_device_data *rem_data;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) {
|
|
|
|
|
sysfs_remove_link(&fpga_data->sff_devices[portid_count]->kobj, "i2c");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
i2c_unregister_device(fpga_data->sff_i2c_clients[portid_count]);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (portid_count = 0 ; portid_count < VIRTUAL_I2C_PORT_LENGTH ; portid_count++) {
|
|
|
|
|
if (fpga_data->i2c_adapter[portid_count] != NULL) {
|
|
|
|
|
info(KERN_INFO "<%x>", fpga_data->i2c_adapter[portid_count]);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
i2c_del_adapter(fpga_data->i2c_adapter[portid_count]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
for (portid_count = 0; portid_count < SFF_PORT_TOTAL; portid_count++) {
|
|
|
|
|
if (fpga_data->sff_devices[portid_count] != NULL) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
rem_data = dev_get_drvdata(fpga_data->sff_devices[portid_count]);
|
|
|
|
|
device_unregister(fpga_data->sff_devices[portid_count]);
|
|
|
|
|
put_device(fpga_data->sff_devices[portid_count]);
|
|
|
|
|
kfree(rem_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sysfs_remove_group(fpga, &fpga_attr_grp);
|
|
|
|
|
sysfs_remove_group(cpld1, &cpld1_attr_grp);
|
|
|
|
|
sysfs_remove_group(cpld2, &cpld2_attr_grp);
|
|
|
|
|
sysfs_remove_group(&sff_dev->kobj, &sff_led_test_grp);
|
|
|
|
|
kobject_put(fpga);
|
|
|
|
|
kobject_put(cpld1);
|
|
|
|
|
kobject_put(cpld2);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
device_destroy(fpgafwclass, MKDEV(0, 0));
|
2020-01-15 20:42:09 -06:00
|
|
|
|
devm_kfree(&pdev->dev, fpga_data);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct platform_driver seastone2_drv = {
|
|
|
|
|
.probe = seastone2_drv_probe,
|
|
|
|
|
.remove = __exit_p(seastone2_drv_remove),
|
|
|
|
|
.driver = {
|
|
|
|
|
.name = DRIVER_NAME,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef TEST_MODE
|
2020-09-09 12:32:56 -05:00
|
|
|
|
#define FPGA_PCI_BAR_NUM 2
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#else
|
2020-09-09 12:32:56 -05:00
|
|
|
|
#define FPGA_PCI_BAR_NUM 0
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static const struct pci_device_id fpga_id_table[] = {
|
|
|
|
|
{ PCI_VDEVICE(XILINX, FPGA_PCIE_DEVICE_ID) },
|
|
|
|
|
{ PCI_VDEVICE(TEST, TEST_PCIE_DEVICE_ID) },
|
|
|
|
|
{0, }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, fpga_id_table);
|
|
|
|
|
|
|
|
|
|
static int fpga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
|
uint32_t fpga_version;
|
|
|
|
|
|
|
|
|
|
if ((err = pci_enable_device(pdev))) {
|
|
|
|
|
dev_err(dev, "pci_enable_device probe error %d for device %s\n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
err, pci_name(pdev));
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((err = pci_request_regions(pdev, FPGA_PCI_NAME)) < 0) {
|
|
|
|
|
dev_err(dev, "pci_request_regions error %d\n", err);
|
|
|
|
|
goto pci_disable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* bar0: data mmio region */
|
|
|
|
|
fpga_dev.data_mmio_start = pci_resource_start(pdev, FPGA_PCI_BAR_NUM);
|
|
|
|
|
fpga_dev.data_mmio_len = pci_resource_len(pdev, FPGA_PCI_BAR_NUM);
|
|
|
|
|
fpga_dev.data_base_addr = pci_iomap(pdev, FPGA_PCI_BAR_NUM, 0);
|
|
|
|
|
if (!fpga_dev.data_base_addr) {
|
|
|
|
|
dev_err(dev, "cannot iomap region of size %lu\n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
(unsigned long)fpga_dev.data_mmio_len);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
goto pci_release;
|
|
|
|
|
}
|
|
|
|
|
dev_info(dev, "data_mmio iomap base = 0x%lx \n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
(unsigned long)fpga_dev.data_base_addr);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
dev_info(dev, "data_mmio_start = 0x%lx data_mmio_len = %lu\n",
|
2020-09-09 12:32:56 -05:00
|
|
|
|
(unsigned long)fpga_dev.data_mmio_start,
|
|
|
|
|
(unsigned long)fpga_dev.data_mmio_len);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
printk(KERN_INFO "FPGA PCIe driver probe OK.\n");
|
2020-09-09 12:32:56 -05:00
|
|
|
|
printk(KERN_INFO "FPGA ioremap registers of size %lu\n", (unsigned long)fpga_dev.data_mmio_len);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
printk(KERN_INFO "FPGA Virtual BAR %d at %8.8lx - %8.8lx\n", FPGA_PCI_BAR_NUM,
|
2020-09-09 12:32:56 -05:00
|
|
|
|
(unsigned long)fpga_dev.data_base_addr,
|
|
|
|
|
(unsigned long)(fpga_dev.data_base_addr + fpga_dev.data_mmio_len));
|
2020-01-15 20:42:09 -06:00
|
|
|
|
printk(KERN_INFO "");
|
|
|
|
|
fpga_version = ioread32(fpga_dev.data_base_addr);
|
|
|
|
|
printk(KERN_INFO "FPGA VERSION : %8.8x\n", fpga_version);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if ((err = fpgafw_init()) < 0) {
|
|
|
|
|
goto pci_unmap;
|
|
|
|
|
}
|
|
|
|
|
seastone2_dev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
platform_driver_register(&seastone2_drv);
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
pci_unmap:
|
|
|
|
|
pci_iounmap(pdev, fpga_dev.data_base_addr);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
pci_release:
|
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
|
pci_disable:
|
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void fpga_pci_remove(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
|
platform_driver_unregister(&seastone2_drv);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
platform_device_unregister(seastone2_dev);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
fpgafw_exit();
|
|
|
|
|
pci_iounmap(pdev, fpga_dev.data_base_addr);
|
|
|
|
|
pci_release_regions(pdev);
|
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
|
printk(KERN_INFO "FPGA PCIe driver remove OK.\n");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct pci_driver pci_dev_ops = {
|
|
|
|
|
.name = FPGA_PCI_NAME,
|
|
|
|
|
.probe = fpga_pci_probe,
|
|
|
|
|
.remove = fpga_pci_remove,
|
|
|
|
|
.id_table = fpga_id_table,
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
enum {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
READREG,
|
|
|
|
|
WRITEREG
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct fpga_reg_data {
|
|
|
|
|
uint32_t addr;
|
|
|
|
|
uint32_t value;
|
|
|
|
|
};
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static long fpgafw_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
|
unsigned long arg) {
|
2020-01-15 20:42:09 -06:00
|
|
|
|
int ret = 0;
|
|
|
|
|
struct fpga_reg_data data;
|
|
|
|
|
mutex_lock(&fpga_data->fpga_lock);
|
|
|
|
|
|
|
|
|
|
#ifdef TEST_MODE
|
|
|
|
|
static uint32_t status_reg;
|
|
|
|
|
#endif
|
|
|
|
|
// Switch function to read and write.
|
2020-09-09 12:32:56 -05:00
|
|
|
|
switch (cmd) {
|
|
|
|
|
case READREG:
|
|
|
|
|
if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) {
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
}
|
|
|
|
|
data.value = ioread32(fpga_dev.data_base_addr + data.addr);
|
|
|
|
|
if (copy_to_user((void __user*)arg , &data, sizeof(data)) != 0) {
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#ifdef TEST_MODE
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (data.addr == 0x1210) {
|
|
|
|
|
switch (status_reg) {
|
|
|
|
|
case 0x0000 : status_reg = 0x8000;
|
|
|
|
|
break;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
case 0x8080 : status_reg = 0x80C0;
|
|
|
|
|
break;
|
|
|
|
|
case 0x80C0 : status_reg = 0x80F0;
|
|
|
|
|
break;
|
|
|
|
|
case 0x80F0 : status_reg = 0x80F8;
|
|
|
|
|
break;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
}
|
2020-09-09 12:32:56 -05:00
|
|
|
|
iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210);
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
break;
|
|
|
|
|
case WRITEREG:
|
|
|
|
|
if (copy_from_user(&data, (void __user*)arg, sizeof(data)) != 0) {
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
}
|
|
|
|
|
iowrite32(data.value, fpga_dev.data_base_addr + data.addr);
|
2020-01-15 20:42:09 -06:00
|
|
|
|
|
|
|
|
|
#ifdef TEST_MODE
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (data.addr == 0x1204) {
|
|
|
|
|
status_reg = 0x8080;
|
|
|
|
|
iowrite32(status_reg, fpga_dev.data_base_addr + 0x1210);
|
|
|
|
|
}
|
2020-01-15 20:42:09 -06:00
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return -EINVAL;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
mutex_unlock(&fpga_data->fpga_lock);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const struct file_operations fpgafw_fops = {
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.unlocked_ioctl = fpgafw_unlocked_ioctl,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static int fpgafw_init(void) {
|
|
|
|
|
printk(KERN_INFO "Initializing the switchboard driver\n");
|
|
|
|
|
// Try to dynamically allocate a major number for the device -- more difficult but worth it
|
|
|
|
|
majorNumber = register_chrdev(0, DEVICE_NAME, &fpgafw_fops);
|
|
|
|
|
if (majorNumber < 0) {
|
|
|
|
|
printk(KERN_ALERT "Failed to register a major number\n");
|
|
|
|
|
return majorNumber;
|
|
|
|
|
}
|
|
|
|
|
printk(KERN_INFO "Device registered correctly with major number %d\n", majorNumber);
|
|
|
|
|
|
|
|
|
|
// Register the device class
|
|
|
|
|
fpgafwclass = class_create(THIS_MODULE, CLASS_NAME);
|
|
|
|
|
if (IS_ERR(fpgafwclass)) { // Check for error and clean up if there is
|
|
|
|
|
unregister_chrdev(majorNumber, DEVICE_NAME);
|
|
|
|
|
printk(KERN_ALERT "Failed to register device class\n");
|
|
|
|
|
return PTR_ERR(fpgafwclass);
|
|
|
|
|
}
|
|
|
|
|
printk(KERN_INFO "Device class registered correctly\n");
|
|
|
|
|
|
|
|
|
|
// Register the device driver
|
|
|
|
|
fpgafwdev = device_create(fpgafwclass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
|
|
|
|
|
if (IS_ERR(fpgafwdev)) { // Clean up if there is an error
|
|
|
|
|
class_destroy(fpgafwclass); // Repeated code but the alternative is goto statements
|
|
|
|
|
unregister_chrdev(majorNumber, DEVICE_NAME);
|
|
|
|
|
printk(KERN_ALERT "Failed to create the FW upgrade device node\n");
|
|
|
|
|
return PTR_ERR(fpgafwdev);
|
|
|
|
|
}
|
|
|
|
|
printk(KERN_INFO "FPGA fw upgrade device node created correctly\n"); // Made it! device was initialized
|
|
|
|
|
return 0;
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
static void fpgafw_exit(void) {
|
|
|
|
|
device_destroy(fpgafwclass, MKDEV(majorNumber, 0)); // remove the device
|
|
|
|
|
class_destroy(fpgafwclass); // remove the device class
|
|
|
|
|
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
|
|
|
|
|
printk(KERN_INFO "Goodbye!\n");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int seastone2_init(void)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
rc = pci_register_driver(&pci_dev_ops);
|
2020-09-09 12:32:56 -05:00
|
|
|
|
if (rc)
|
2020-01-15 20:42:09 -06:00
|
|
|
|
return rc;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void seastone2_exit(void)
|
|
|
|
|
{
|
|
|
|
|
pci_unregister_driver(&pci_dev_ops);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_init(seastone2_init);
|
|
|
|
|
module_exit(seastone2_exit);
|
|
|
|
|
|
2020-09-09 12:32:56 -05:00
|
|
|
|
MODULE_AUTHOR("Pradchaya P. <pphuhcar@celestica.com>");
|
|
|
|
|
MODULE_DESCRIPTION("Celestica Seastone2 switchboard driver");
|
2020-01-15 20:42:09 -06:00
|
|
|
|
MODULE_VERSION(MOD_VERSION);
|
|
|
|
|
MODULE_LICENSE("GPL");
|