/* * SFP driver for juniper qfx5210_64x sfp * * Tested and validated on Juniper QFX5210 * Ciju Rajan K * * Copyright (C) Brandon Chuang * * Based on ad7414.c * Copyright 2006 Stefan Roese , DENX Software Engineering * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "qfx5210_64x_sfp" /* Platform dependent */ #define DEBUG_MODE 0 #if (DEBUG_MODE == 1) #define DEBUG_PRINT(fmt, args...) \ printk (KERN_INFO "%s:%s[%d]: " fmt "\r\n", __FILE__, __FUNCTION__, __LINE__, ##args) #else #define DEBUG_PRINT(fmt, args...) #endif #define NUM_OF_SFP_PORT 24 #define EEPROM_NAME "sfp_eeprom" #define EEPROM_SIZE 256 /* 256 byte eeprom */ #define BIT_INDEX(i) (1ULL << (i)) #define USE_I2C_BLOCK_READ 1 /* Platform dependent */ #define I2C_RW_RETRY_COUNT 10 #define I2C_RW_RETRY_INTERVAL 60 /* ms */ #define SFP_EEPROM_A0_I2C_ADDR (0xA0 >> 1) #define SFF8024_PHYSICAL_DEVICE_ID_ADDR 0x0 #define SFF8024_DEVICE_ID_SFP 0x3 #define SFF8024_DEVICE_ID_QSFP 0xC #define SFF8024_DEVICE_ID_QSFP_PLUS 0xD #define SFF8024_DEVICE_ID_QSFP28 0x11 #define SFF8436_RX_LOS_ADDR 3 #define SFF8436_TX_FAULT_ADDR 4 #define SFF8436_TX_DISABLE_ADDR 86 #define MULTIPAGE_SUPPORT 1 #if (MULTIPAGE_SUPPORT == 1) /* fundamental unit of addressing for SFF_8472/SFF_8436 */ #define SFF_8436_PAGE_SIZE 128 /* * The current 8436 (QSFP) spec provides for only 4 supported * pages (pages 0-3). * This driver is prepared to support more, but needs a register in the * EEPROM to indicate how many pages are supported before it is safe * to implement more pages in the driver. */ #define SFF_8436_SPECED_PAGES 4 #define SFF_8436_EEPROM_SIZE ((1 + SFF_8436_SPECED_PAGES) * SFF_8436_PAGE_SIZE) #define SFF_8436_EEPROM_UNPAGED_SIZE (2 * SFF_8436_PAGE_SIZE) /* * The current 8472 (SFP) spec provides for only 3 supported * pages (pages 0-2). * This driver is prepared to support more, but needs a register in the * EEPROM to indicate how many pages are supported before it is safe * to implement more pages in the driver. */ #define SFF_8472_SPECED_PAGES 3 #define SFF_8472_EEPROM_SIZE ((3 + SFF_8472_SPECED_PAGES) * SFF_8436_PAGE_SIZE) #define SFF_8472_EEPROM_UNPAGED_SIZE (4 * SFF_8436_PAGE_SIZE) /* a few constants to find our way around the EEPROM */ #define SFF_8436_PAGE_SELECT_REG 0x7F #define SFF_8436_PAGEABLE_REG 0x02 #define SFF_8436_NOT_PAGEABLE (1<<2) #define SFF_8472_PAGEABLE_REG 0x40 #define SFF_8472_PAGEABLE (1<<4) /* * This parameter is to help this driver avoid blocking other drivers out * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C * clock, one 256 byte read takes about 1/43 second which is excessive; * but the 1/170 second it takes at 400 kHz may be quite reasonable; and * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. * * This value is forced to be a power of two so that writes align on pages. */ static unsigned io_limit = SFF_8436_PAGE_SIZE; /* * specs often allow 5 msec for a page write, sometimes 20 msec; * it's important to recover from write timeouts. */ static unsigned write_timeout = 25; typedef enum qsfp_opcode { QSFP_READ_OP = 0, QSFP_WRITE_OP = 1 } qsfp_opcode_e; #endif static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf); static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf); static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf); static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count);; static ssize_t sfp_eeprom_read(struct i2c_client *, u8, u8 *,int); static ssize_t sfp_eeprom_write(struct i2c_client *, u8 , const char *,int); extern int juniper_i2c_cpld_read (u8 cpld_addr, u8 reg); enum sfp_sysfs_attributes { PRESENT, PRESENT_ALL, PORT_NUMBER, PORT_TYPE, DDM_IMPLEMENTED, TX_FAULT, TX_FAULT1, TX_FAULT2, TX_FAULT3, TX_FAULT4, TX_DISABLE, TX_DISABLE1, TX_DISABLE2, TX_DISABLE3, TX_DISABLE4, RX_LOS, RX_LOS1, RX_LOS2, RX_LOS3, RX_LOS4, RX_LOS_ALL }; /* SFP/QSFP common attributes for sysfs */ static SENSOR_DEVICE_ATTR(sfp_port_number, S_IRUGO, show_port_number, NULL, PORT_NUMBER); static SENSOR_DEVICE_ATTR(sfp_is_present, S_IRUGO, show_present, NULL, PRESENT); static SENSOR_DEVICE_ATTR(sfp_is_present_all, S_IRUGO, show_present, NULL, PRESENT_ALL); static SENSOR_DEVICE_ATTR(sfp_tx_disable, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE); static SENSOR_DEVICE_ATTR(sfp_tx_fault, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT); /* QSFP attributes for sysfs */ static SENSOR_DEVICE_ATTR(sfp_rx_los, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS); static SENSOR_DEVICE_ATTR(sfp_rx_los1, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS1); static SENSOR_DEVICE_ATTR(sfp_rx_los2, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS2); static SENSOR_DEVICE_ATTR(sfp_rx_los3, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS3); static SENSOR_DEVICE_ATTR(sfp_rx_los4, S_IRUGO, qsfp_show_tx_rx_status, NULL, RX_LOS4); static SENSOR_DEVICE_ATTR(sfp_tx_disable1, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE1); static SENSOR_DEVICE_ATTR(sfp_tx_disable2, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE2); static SENSOR_DEVICE_ATTR(sfp_tx_disable3, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE3); static SENSOR_DEVICE_ATTR(sfp_tx_disable4, S_IWUSR | S_IRUGO, qsfp_show_tx_rx_status, qsfp_set_tx_disable, TX_DISABLE4); static SENSOR_DEVICE_ATTR(sfp_tx_fault1, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT1); static SENSOR_DEVICE_ATTR(sfp_tx_fault2, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT2); static SENSOR_DEVICE_ATTR(sfp_tx_fault3, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT3); static SENSOR_DEVICE_ATTR(sfp_tx_fault4, S_IRUGO, qsfp_show_tx_rx_status, NULL, TX_FAULT4); static struct attribute *qsfp_attributes[] = { &sensor_dev_attr_sfp_port_number.dev_attr.attr, &sensor_dev_attr_sfp_is_present.dev_attr.attr, &sensor_dev_attr_sfp_is_present_all.dev_attr.attr, &sensor_dev_attr_sfp_rx_los.dev_attr.attr, &sensor_dev_attr_sfp_rx_los1.dev_attr.attr, &sensor_dev_attr_sfp_rx_los2.dev_attr.attr, &sensor_dev_attr_sfp_rx_los3.dev_attr.attr, &sensor_dev_attr_sfp_rx_los4.dev_attr.attr, &sensor_dev_attr_sfp_tx_disable.dev_attr.attr, &sensor_dev_attr_sfp_tx_disable1.dev_attr.attr, &sensor_dev_attr_sfp_tx_disable2.dev_attr.attr, &sensor_dev_attr_sfp_tx_disable3.dev_attr.attr, &sensor_dev_attr_sfp_tx_disable4.dev_attr.attr, &sensor_dev_attr_sfp_tx_fault.dev_attr.attr, &sensor_dev_attr_sfp_tx_fault1.dev_attr.attr, &sensor_dev_attr_sfp_tx_fault2.dev_attr.attr, &sensor_dev_attr_sfp_tx_fault3.dev_attr.attr, &sensor_dev_attr_sfp_tx_fault4.dev_attr.attr, NULL }; /* Platform dependent +++ */ #define CPLD_PORT_TO_FRONT_PORT(port) (port+1) enum port_numbers { qfx5210_64x_port1, qfx5210_64x_port2, qfx5210_64x_port3, qfx5210_64x_port4, qfx5210_64x_port5, qfx5210_64x_port6, qfx5210_64x_port7, qfx5210_64x_port8, qfx5210_64x_port9, qfx5210_64x_port10, qfx5210_64x_port11, qfx5210_64x_port12, qfx5210_64x_port13, qfx5210_64x_port14, qfx5210_64x_port15, qfx5210_64x_port16, qfx5210_64x_port17, qfx5210_64x_port18, qfx5210_64x_port19, qfx5210_64x_port20, qfx5210_64x_port21, qfx5210_64x_port22, qfx5210_64x_port23, qfx5210_64x_port24, qfx5210_64x_port25, qfx5210_64x_port26, qfx5210_64x_port27, qfx5210_64x_port28, qfx5210_64x_port29, qfx5210_64x_port30, qfx5210_64x_port31, qfx5210_64x_port32, qfx5210_64x_port33, qfx5210_64x_port34, qfx5210_64x_port35, qfx5210_64x_port36, qfx5210_64x_port37, qfx5210_64x_port38, qfx5210_64x_port39, qfx5210_64x_port40, qfx5210_64x_port41, qfx5210_64x_port42, qfx5210_64x_port43, qfx5210_64x_port44, qfx5210_64x_port45, qfx5210_64x_port46, qfx5210_64x_port47, qfx5210_64x_port48, qfx5210_64x_port49, qfx5210_64x_port50, qfx5210_64x_port51, qfx5210_64x_port52, qfx5210_64x_port53, qfx5210_64x_port54, qfx5210_64x_port55, qfx5210_64x_port56, qfx5210_64x_port57, qfx5210_64x_port58, qfx5210_64x_port59, qfx5210_64x_port60, qfx5210_64x_port61, qfx5210_64x_port62, qfx5210_64x_port63, qfx5210_64x_port64 }; #define I2C_DEV_ID(x) { #x, x} static const struct i2c_device_id sfp_device_id[] = { I2C_DEV_ID(qfx5210_64x_port1), I2C_DEV_ID(qfx5210_64x_port2), I2C_DEV_ID(qfx5210_64x_port3), I2C_DEV_ID(qfx5210_64x_port4), I2C_DEV_ID(qfx5210_64x_port5), I2C_DEV_ID(qfx5210_64x_port6), I2C_DEV_ID(qfx5210_64x_port7), I2C_DEV_ID(qfx5210_64x_port8), I2C_DEV_ID(qfx5210_64x_port9), I2C_DEV_ID(qfx5210_64x_port10), I2C_DEV_ID(qfx5210_64x_port11), I2C_DEV_ID(qfx5210_64x_port12), I2C_DEV_ID(qfx5210_64x_port13), I2C_DEV_ID(qfx5210_64x_port14), I2C_DEV_ID(qfx5210_64x_port15), I2C_DEV_ID(qfx5210_64x_port16), I2C_DEV_ID(qfx5210_64x_port17), I2C_DEV_ID(qfx5210_64x_port18), I2C_DEV_ID(qfx5210_64x_port19), I2C_DEV_ID(qfx5210_64x_port20), I2C_DEV_ID(qfx5210_64x_port21), I2C_DEV_ID(qfx5210_64x_port22), I2C_DEV_ID(qfx5210_64x_port23), I2C_DEV_ID(qfx5210_64x_port24), I2C_DEV_ID(qfx5210_64x_port25), I2C_DEV_ID(qfx5210_64x_port26), I2C_DEV_ID(qfx5210_64x_port27), I2C_DEV_ID(qfx5210_64x_port28), I2C_DEV_ID(qfx5210_64x_port29), I2C_DEV_ID(qfx5210_64x_port30), I2C_DEV_ID(qfx5210_64x_port31), I2C_DEV_ID(qfx5210_64x_port32), I2C_DEV_ID(qfx5210_64x_port33), I2C_DEV_ID(qfx5210_64x_port34), I2C_DEV_ID(qfx5210_64x_port35), I2C_DEV_ID(qfx5210_64x_port36), I2C_DEV_ID(qfx5210_64x_port37), I2C_DEV_ID(qfx5210_64x_port38), I2C_DEV_ID(qfx5210_64x_port39), I2C_DEV_ID(qfx5210_64x_port40), I2C_DEV_ID(qfx5210_64x_port41), I2C_DEV_ID(qfx5210_64x_port42), I2C_DEV_ID(qfx5210_64x_port43), I2C_DEV_ID(qfx5210_64x_port44), I2C_DEV_ID(qfx5210_64x_port45), I2C_DEV_ID(qfx5210_64x_port46), I2C_DEV_ID(qfx5210_64x_port47), I2C_DEV_ID(qfx5210_64x_port48), I2C_DEV_ID(qfx5210_64x_port49), I2C_DEV_ID(qfx5210_64x_port50), I2C_DEV_ID(qfx5210_64x_port51), I2C_DEV_ID(qfx5210_64x_port52), I2C_DEV_ID(qfx5210_64x_port53), I2C_DEV_ID(qfx5210_64x_port54), I2C_DEV_ID(qfx5210_64x_port55), I2C_DEV_ID(qfx5210_64x_port56), I2C_DEV_ID(qfx5210_64x_port57), I2C_DEV_ID(qfx5210_64x_port58), I2C_DEV_ID(qfx5210_64x_port59), I2C_DEV_ID(qfx5210_64x_port60), I2C_DEV_ID(qfx5210_64x_port61), I2C_DEV_ID(qfx5210_64x_port62), I2C_DEV_ID(qfx5210_64x_port63), I2C_DEV_ID(qfx5210_64x_port64), { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, sfp_device_id); /* Platform dependent --- */ enum driver_type_e { DRIVER_TYPE_SFP_MSA, DRIVER_TYPE_SFP_DDM, DRIVER_TYPE_QSFP, DRIVER_TYPE_XFP }; /* Each client has this additional data */ struct eeprom_data { char valid; /* !=0 if registers are valid */ unsigned long last_updated; /* In jiffies */ struct bin_attribute bin; /* eeprom data */ }; struct qsfp_data { char valid; /* !=0 if registers are valid */ unsigned long last_updated; /* In jiffies */ u8 status[3]; /* bit0:port0, bit1:port1 and so on */ /* index 0 => tx_fail 1 => tx_disable 2 => rx_loss */ u8 device_id; struct eeprom_data eeprom; }; struct sfp_port_data { struct mutex update_lock; enum driver_type_e driver_type; int port; /* CPLD port index */ u64 present; /* present status, bit0:port0, bit1:port1 and so on */ struct qsfp_data *qsfp; struct i2c_client *client; #if (MULTIPAGE_SUPPORT == 1) int use_smbus; u8 *writebuf; unsigned write_max; #endif }; #if (MULTIPAGE_SUPPORT == 1) static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, char *buf, loff_t off, size_t len, qsfp_opcode_e opcode); #endif static ssize_t show_port_number(struct device *dev, struct device_attribute *da, char *buf) { struct i2c_client *client = to_i2c_client(dev); struct sfp_port_data *data = i2c_get_clientdata(client); return sprintf(buf, "%d\n", CPLD_PORT_TO_FRONT_PORT(data->port)); } /* Platform dependent +++ */ static struct sfp_port_data *sfp_update_present(struct i2c_client *client) { struct sfp_port_data *data = i2c_get_clientdata(client); int i = 0; int status = -1; u8 regs[] = {0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77}; DEBUG_PRINT("Starting sfp present status update"); mutex_lock(&data->update_lock); /* Read present status of port 1~64 */ data->present = 0; for (i = 0; i < ARRAY_SIZE(regs); i++) { status = juniper_i2c_cpld_read(0x60, regs[i]); if (status < 0) { DEBUG_PRINT("cpld(0x60) reg(0x%x) err %d", regs[i], status); goto exit; } DEBUG_PRINT("Present status = 0x%lx", data->present); data->present |= (u64)status << (i*8); } DEBUG_PRINT("Present status = 0x%lx", data->present); exit: mutex_unlock(&data->update_lock); return (status < 0) ? ERR_PTR(status) : data; } /* Platform dependent --- */ static int sfp_is_port_present(struct i2c_client *client, int port) { struct sfp_port_data *data = i2c_get_clientdata(client); data = sfp_update_present(client); if (IS_ERR(data)) { return PTR_ERR(data); } return (data->present & BIT_INDEX(data->port)) ? 0 : 1; /* Platform dependent */ } /* Platform dependent +++ */ static ssize_t show_present(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); if (PRESENT_ALL == attr->index) { int i; u8 values[8] = {0}; struct sfp_port_data *data = sfp_update_present(client); if (IS_ERR(data)) { return PTR_ERR(data); } for (i = 0; i < ARRAY_SIZE(values); i++) { values[i] = ~(u8)(data->present >> (i * 8)); } /* Return values 1 -> 64 in order */ return sprintf(buf, "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]); } else { struct sfp_port_data *data = i2c_get_clientdata(client); int present = sfp_is_port_present(client, data->port); if (IS_ERR_VALUE(present)) { return present; } /* PRESENT */ return sprintf(buf, "%d\n", present); } } /* Platform dependent --- */ static struct sfp_port_data *qsfp_update_tx_rx_status(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct sfp_port_data *data = i2c_get_clientdata(client); int i, status = -1; u8 buf = 0; u8 reg[] = {SFF8436_TX_FAULT_ADDR, SFF8436_TX_DISABLE_ADDR, SFF8436_RX_LOS_ADDR}; if (time_before(jiffies, data->qsfp->last_updated + HZ + HZ / 2) && data->qsfp->valid) { return data; } DEBUG_PRINT("Starting sfp tx rx status update"); mutex_lock(&data->update_lock); data->qsfp->valid = 0; memset(data->qsfp->status, 0, sizeof(data->qsfp->status)); /* Notify device to update tx fault/ tx disable/ rx los status */ for (i = 0; i < ARRAY_SIZE(reg); i++) { status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); if (unlikely(status < 0)) { goto exit; } } msleep(200); /* Read actual tx fault/ tx disable/ rx los status */ for (i = 0; i < ARRAY_SIZE(reg); i++) { status = sfp_eeprom_read(client, reg[i], &buf, sizeof(buf)); if (unlikely(status < 0)) { goto exit; } DEBUG_PRINT("qsfp reg(0x%x) status = (0x%x)", reg[i], data->qsfp->status[i]); data->qsfp->status[i] = (buf & 0xF); } data->qsfp->valid = 1; data->qsfp->last_updated = jiffies; exit: mutex_unlock(&data->update_lock); return (status < 0) ? ERR_PTR(status) : data; } static ssize_t qsfp_show_tx_rx_status(struct device *dev, struct device_attribute *da, char *buf) { int present; u8 val = 0; struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct sfp_port_data *data = i2c_get_clientdata(client); present = sfp_is_port_present(client, data->port); if (IS_ERR_VALUE(present)) { return present; } if (present == 0) { /* port is not present */ return -ENXIO; } data = qsfp_update_tx_rx_status(dev); if (IS_ERR(data)) { return PTR_ERR(data); } switch (attr->index) { case TX_FAULT: val = !!(data->qsfp->status[2] & 0xF); break; case TX_FAULT1: case TX_FAULT2: case TX_FAULT3: case TX_FAULT4: val = !!(data->qsfp->status[2] & BIT_INDEX(attr->index - TX_FAULT1)); break; case TX_DISABLE: val = data->qsfp->status[1] & 0xF; break; case TX_DISABLE1: case TX_DISABLE2: case TX_DISABLE3: case TX_DISABLE4: val = !!(data->qsfp->status[1] & BIT_INDEX(attr->index - TX_DISABLE1)); break; case RX_LOS: val = !!(data->qsfp->status[0] & 0xF); break; case RX_LOS1: case RX_LOS2: case RX_LOS3: case RX_LOS4: val = !!(data->qsfp->status[0] & BIT_INDEX(attr->index - RX_LOS1)); break; default: break; } return sprintf(buf, "%d\n", val); } static ssize_t qsfp_set_tx_disable(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { long disable; int status; struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct i2c_client *client = to_i2c_client(dev); struct sfp_port_data *data = i2c_get_clientdata(client); status = sfp_is_port_present(client, data->port); if (IS_ERR_VALUE(status)) { return status; } if (!status) { /* port is not present */ return -ENXIO; } status = kstrtol(buf, 10, &disable); if (status) { return status; } data = qsfp_update_tx_rx_status(dev); if (IS_ERR(data)) { return PTR_ERR(data); } mutex_lock(&data->update_lock); if (attr->index == TX_DISABLE) { if (disable) { data->qsfp->status[1] |= 0xF; } else { data->qsfp->status[1] &= ~0xF; } } else {/* TX_DISABLE1 ~ TX_DISABLE4*/ if (disable) { data->qsfp->status[1] |= (1 << (attr->index - TX_DISABLE1)); } else { data->qsfp->status[1] &= ~(1 << (attr->index - TX_DISABLE1)); } } DEBUG_PRINT("index = (%d), status = (0x%x)", attr->index, data->qsfp->status[1]); status = sfp_eeprom_write(data->client, SFF8436_TX_DISABLE_ADDR, &data->qsfp->status[1], sizeof(data->qsfp->status[1])); if (unlikely(status < 0)) { count = status; } mutex_unlock(&data->update_lock); return count; } static ssize_t sfp_eeprom_write(struct i2c_client *client, u8 command, const char *data, int data_len) { #if USE_I2C_BLOCK_READ int status, retry = I2C_RW_RETRY_COUNT; if (data_len > I2C_SMBUS_BLOCK_MAX) { data_len = I2C_SMBUS_BLOCK_MAX; } while (retry) { status = i2c_smbus_write_i2c_block_data(client, command, data_len, data); if (unlikely(status < 0)) { msleep(I2C_RW_RETRY_INTERVAL); retry--; continue; } break; } if (unlikely(status < 0)) { return status; } return data_len; #else int status, retry = I2C_RW_RETRY_COUNT; while (retry) { status = i2c_smbus_write_byte_data(client, command, *data); if (unlikely(status < 0)) { msleep(I2C_RW_RETRY_INTERVAL); retry--; continue; } break; } if (unlikely(status < 0)) { return status; } return 1; #endif } #if (MULTIPAGE_SUPPORT == 0) static ssize_t sfp_port_write(struct sfp_port_data *data, const char *buf, loff_t off, size_t count) { ssize_t retval = 0; if (unlikely(!count)) { return count; } /* * Write data to chip, protecting against concurrent updates * from this host, but not from other I2C masters. */ mutex_lock(&data->update_lock); while (count) { ssize_t status; status = sfp_eeprom_write(data->client, off, buf, count); if (status <= 0) { if (retval == 0) { retval = status; } break; } buf += status; off += status; count -= status; retval += status; } mutex_unlock(&data->update_lock); return retval; } #endif static ssize_t sfp_bin_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { int present; struct sfp_port_data *data; DEBUG_PRINT("%s(%d) offset = (%d), count = (%d)", off, count); data = dev_get_drvdata(container_of(kobj, struct device, kobj)); present = sfp_is_port_present(data->client, data->port); if (IS_ERR_VALUE(present)) { return present; } if (present == 0) { /* port is not present */ return -ENODEV; } #if (MULTIPAGE_SUPPORT == 1) return sfp_port_read_write(data, buf, off, count, QSFP_WRITE_OP); #else return sfp_port_write(data, buf, off, count); #endif } static ssize_t sfp_eeprom_read(struct i2c_client *client, u8 command, u8 *data, int data_len) { #if USE_I2C_BLOCK_READ int status, retry = I2C_RW_RETRY_COUNT; if (data_len > I2C_SMBUS_BLOCK_MAX) { data_len = I2C_SMBUS_BLOCK_MAX; } while (retry) { status = i2c_smbus_read_i2c_block_data(client, command, data_len, data); if (unlikely(status < 0)) { msleep(I2C_RW_RETRY_INTERVAL); retry--; continue; } break; } if (unlikely(status < 0)) { goto abort; } if (unlikely(status != data_len)) { status = -EIO; goto abort; } //result = data_len; abort: return status; #else int status, retry = I2C_RW_RETRY_COUNT; while (retry) { status = i2c_smbus_read_byte_data(client, command); if (unlikely(status < 0)) { msleep(I2C_RW_RETRY_INTERVAL); retry--; continue; } break; } if (unlikely(status < 0)) { dev_dbg(&client->dev, "sfp read byte data failed, command(0x%2x), data(0x%2x)\r\n", command, status); goto abort; } *data = (u8)status; status = 1; abort: return status; #endif } #if (MULTIPAGE_SUPPORT == 1) /*-------------------------------------------------------------------------*/ /* * This routine computes the addressing information to be used for * a given r/w request. * * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51), * the page, and the offset. * * Handles both SFP and QSFP. * For SFP, offset 0-255 are on client[0], >255 is on client[1] * Offset 256-383 are on the lower half of client[1] * Pages are accessible on the upper half of client[1]. * Offset >383 are in 128 byte pages mapped into the upper half * * For QSFP, all offsets are on client[0] * offset 0-127 are on the lower half of client[0] (no paging) * Pages are accessible on the upper half of client[1]. * Offset >127 are in 128 byte pages mapped into the upper half * * Callers must not read/write beyond the end of a client or a page * without recomputing the client/page. Hence offset (within page) * plus length must be less than or equal to 128. (Note that this * routine does not have access to the length of the call, hence * cannot do the validity check.) * * Offset within Lower Page 00h and Upper Page 00h are not recomputed */ static uint8_t sff_8436_translate_offset(struct sfp_port_data *port_data, loff_t *offset, struct i2c_client **client) { unsigned page = 0; *client = port_data->client; /* * if offset is in the range 0-128... * page doesn't matter (using lower half), return 0. * offset is already correct (don't add 128 to get to paged area) */ if (*offset < SFF_8436_PAGE_SIZE) return page; /* note, page will always be positive since *offset >= 128 */ page = (*offset >> 7)-1; /* 0x80 places the offset in the top half, offset is last 7 bits */ *offset = SFF_8436_PAGE_SIZE + (*offset & 0x7f); return page; /* note also returning client and offset */ } static ssize_t sff_8436_eeprom_read(struct sfp_port_data *port_data, struct i2c_client *client, char *buf, unsigned offset, size_t count) { struct i2c_msg msg[2]; u8 msgbuf[2]; unsigned long timeout, read_time; int status, i; memset(msg, 0, sizeof(msg)); switch (port_data->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: /*smaller eeproms can work given some SMBus extension calls */ if (count > I2C_SMBUS_BLOCK_MAX) count = I2C_SMBUS_BLOCK_MAX; break; case I2C_SMBUS_WORD_DATA: /* Check for odd length transaction */ count = (count == 1) ? 1 : 2; break; case I2C_SMBUS_BYTE_DATA: count = 1; break; default: /* * When we have a better choice than SMBus calls, use a * combined I2C message. Write address; then read up to * io_limit data bytes. msgbuf is u8 and will cast to our * needs. */ i = 0; msgbuf[i++] = offset; msg[0].addr = client->addr; msg[0].buf = msgbuf; msg[0].len = i; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = count; } /* * Reads fail if the previous write didn't complete yet. We may * loop a few times until this one succeeds, waiting at least * long enough for one entire page write to work. */ timeout = jiffies + msecs_to_jiffies(write_timeout); do { read_time = jiffies; switch (port_data->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: status = i2c_smbus_read_i2c_block_data(client, offset, count, buf); break; case I2C_SMBUS_WORD_DATA: status = i2c_smbus_read_word_data(client, offset); if (status >= 0) { buf[0] = status & 0xff; if (count == 2) buf[1] = status >> 8; status = count; } break; case I2C_SMBUS_BYTE_DATA: status = i2c_smbus_read_byte_data(client, offset); if (status >= 0) { buf[0] = status; status = count; } break; default: status = i2c_transfer(client->adapter, msg, 2); if (status == 2) status = count; } dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies); if (status == count) /* happy path */ return count; if (status == -ENXIO) /* no module present */ return status; /* REVISIT: at HZ=100, this is sloooow */ msleep(1); } while (time_before(read_time, timeout)); return -ETIMEDOUT; } static ssize_t sff_8436_eeprom_write(struct sfp_port_data *port_data, struct i2c_client *client, const char *buf, unsigned offset, size_t count) { struct i2c_msg msg; ssize_t status; unsigned long timeout, write_time; unsigned next_page_start; int i = 0; /* write max is at most a page * (In this driver, write_max is actually one byte!) */ if (count > port_data->write_max) count = port_data->write_max; /* shorten count if necessary to avoid crossing page boundary */ next_page_start = roundup(offset + 1, SFF_8436_PAGE_SIZE); if (offset + count > next_page_start) count = next_page_start - offset; switch (port_data->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: /*smaller eeproms can work given some SMBus extension calls */ if (count > I2C_SMBUS_BLOCK_MAX) count = I2C_SMBUS_BLOCK_MAX; break; case I2C_SMBUS_WORD_DATA: /* Check for odd length transaction */ count = (count == 1) ? 1 : 2; break; case I2C_SMBUS_BYTE_DATA: count = 1; break; default: /* If we'll use I2C calls for I/O, set up the message */ msg.addr = client->addr; msg.flags = 0; /* msg.buf is u8 and casts will mask the values */ msg.buf = port_data->writebuf; msg.buf[i++] = offset; memcpy(&msg.buf[i], buf, count); msg.len = i + count; break; } /* * Reads fail if the previous write didn't complete yet. We may * loop a few times until this one succeeds, waiting at least * long enough for one entire page write to work. */ timeout = jiffies + msecs_to_jiffies(write_timeout); do { write_time = jiffies; switch (port_data->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: status = i2c_smbus_write_i2c_block_data(client, offset, count, buf); if (status == 0) status = count; break; case I2C_SMBUS_WORD_DATA: if (count == 2) { status = i2c_smbus_write_word_data(client, offset, (u16)((buf[0])|(buf[1] << 8))); } else { /* count = 1 */ status = i2c_smbus_write_byte_data(client, offset, buf[0]); } if (status == 0) status = count; break; case I2C_SMBUS_BYTE_DATA: status = i2c_smbus_write_byte_data(client, offset, buf[0]); if (status == 0) status = count; break; default: status = i2c_transfer(client->adapter, &msg, 1); if (status == 1) status = count; break; } dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", count, offset, (long int) status, jiffies); if (status == count) return count; /* REVISIT: at HZ=100, this is sloooow */ msleep(1); } while (time_before(write_time, timeout)); return -ETIMEDOUT; } static ssize_t sff_8436_eeprom_update_client(struct sfp_port_data *port_data, char *buf, loff_t off, size_t count, qsfp_opcode_e opcode) { struct i2c_client *client; ssize_t retval = 0; u8 page = 0; loff_t phy_offset = off; int ret = 0; page = sff_8436_translate_offset(port_data, &phy_offset, &client); dev_dbg(&client->dev, "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%ld, opcode:%d\n", off, page, phy_offset, (long int) count, opcode); if (page > 0) { ret = sff_8436_eeprom_write(port_data, client, &page, SFF_8436_PAGE_SELECT_REG, 1); if (ret < 0) { dev_dbg(&client->dev, "Write page register for page %d failed ret:%d!\n", page, ret); return ret; } } while (count) { ssize_t status; if (opcode == QSFP_READ_OP) { status = sff_8436_eeprom_read(port_data, client, buf, phy_offset, count); } else { status = sff_8436_eeprom_write(port_data, client, buf, phy_offset, count); } if (status <= 0) { if (retval == 0) retval = status; break; } buf += status; phy_offset += status; count -= status; retval += status; } if (page > 0) { /* return the page register to page 0 (why?) */ page = 0; ret = sff_8436_eeprom_write(port_data, client, &page, SFF_8436_PAGE_SELECT_REG, 1); if (ret < 0) { dev_err(&client->dev, "Restore page register to page %d failed ret:%d!\n", page, ret); return ret; } } return retval; } /* * Figure out if this access is within the range of supported pages. * Note this is called on every access because we don't know if the * module has been replaced since the last call. * If/when modules support more pages, this is the routine to update * to validate and allow access to additional pages. * * Returns updated len for this access: * - entire access is legal, original len is returned. * - access begins legal but is too long, len is truncated to fit. * - initial offset exceeds supported pages, return -EINVAL */ static ssize_t sff_8436_page_legal(struct sfp_port_data *port_data, loff_t off, size_t len) { struct i2c_client *client = port_data->client; u8 regval; int status; size_t maxlen; if (off < 0) return -EINVAL; if (port_data->driver_type == DRIVER_TYPE_SFP_MSA) { /* SFP case */ /* if no pages needed, we're good */ if ((off + len) <= SFF_8472_EEPROM_UNPAGED_SIZE) return len; /* if offset exceeds possible pages, we're not good */ if (off >= SFF_8472_EEPROM_SIZE) return -EINVAL; /* in between, are pages supported? */ status = sff_8436_eeprom_read(port_data, client, ®val, SFF_8472_PAGEABLE_REG, 1); if (status < 0) return status; /* error out (no module?) */ if (regval & SFF_8472_PAGEABLE) { /* Pages supported, trim len to the end of pages */ maxlen = SFF_8472_EEPROM_SIZE - off; } else { /* pages not supported, trim len to unpaged size */ maxlen = SFF_8472_EEPROM_UNPAGED_SIZE - off; } len = (len > maxlen) ? maxlen : len; dev_dbg(&client->dev, "page_legal, SFP, off %lld len %ld\n", off, (long int) len); } else if (port_data->driver_type == DRIVER_TYPE_QSFP || port_data->driver_type == DRIVER_TYPE_XFP) { /* QSFP case */ /* if no pages needed, we're good */ if ((off + len) <= SFF_8436_EEPROM_UNPAGED_SIZE) return len; /* if offset exceeds possible pages, we're not good */ if (off >= SFF_8436_EEPROM_SIZE) return -EINVAL; /* in between, are pages supported? */ status = sff_8436_eeprom_read(port_data, client, ®val, SFF_8436_PAGEABLE_REG, 1); if (status < 0) return status; /* error out (no module?) */ if (regval & SFF_8436_NOT_PAGEABLE) { /* pages not supported, trim len to unpaged size */ maxlen = SFF_8436_EEPROM_UNPAGED_SIZE - off; } else { /* Pages supported, trim len to the end of pages */ maxlen = SFF_8436_EEPROM_SIZE - off; } len = (len > maxlen) ? maxlen : len; dev_dbg(&client->dev, "page_legal, QSFP, off %lld len %ld\n", off, (long int) len); } else { return -EINVAL; } return len; } static ssize_t sfp_port_read_write(struct sfp_port_data *port_data, char *buf, loff_t off, size_t len, qsfp_opcode_e opcode) { struct i2c_client *client = port_data->client; int chunk; int status = 0; ssize_t retval; size_t pending_len = 0, chunk_len = 0; loff_t chunk_offset = 0, chunk_start_offset = 0; if (unlikely(!len)) return len; /* * Read data from chip, protecting against concurrent updates * from this host, but not from other I2C masters. */ mutex_lock(&port_data->update_lock); /* * Confirm this access fits within the device suppored addr range */ len = sff_8436_page_legal(port_data, off, len); if (len < 0) { status = len; goto err; } /* * For each (128 byte) chunk involved in this request, issue a * separate call to sff_eeprom_update_client(), to * ensure that each access recalculates the client/page * and writes the page register as needed. * Note that chunk to page mapping is confusing, is different for * QSFP and SFP, and never needs to be done. Don't try! */ pending_len = len; /* amount remaining to transfer */ retval = 0; /* amount transferred */ for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) { /* * Compute the offset and number of bytes to be read/write * * 1. start at offset 0 (within the chunk), and read/write * the entire chunk * 2. start at offset 0 (within the chunk) and read/write less * than entire chunk * 3. start at an offset not equal to 0 and read/write the rest * of the chunk * 4. start at an offset not equal to 0 and read/write less than * (end of chunk - offset) */ chunk_start_offset = chunk * SFF_8436_PAGE_SIZE; if (chunk_start_offset < off) { chunk_offset = off; if ((off + pending_len) < (chunk_start_offset + SFF_8436_PAGE_SIZE)) chunk_len = pending_len; else chunk_len = (chunk+1)*SFF_8436_PAGE_SIZE - off;/*SFF_8436_PAGE_SIZE - off;*/ } else { chunk_offset = chunk_start_offset; if (pending_len > SFF_8436_PAGE_SIZE) chunk_len = SFF_8436_PAGE_SIZE; else chunk_len = pending_len; } dev_dbg(&client->dev, "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n", off, (long int) len, chunk_start_offset, chunk_offset, (long int) chunk_len, (long int) pending_len); /* * note: chunk_offset is from the start of the EEPROM, * not the start of the chunk */ status = sff_8436_eeprom_update_client(port_data, buf, chunk_offset, chunk_len, opcode); if (status != chunk_len) { /* This is another 'no device present' path */ dev_dbg(&client->dev, "sff_8436_update_client for chunk %d chunk_offset %lld chunk_len %ld failed %d!\n", chunk, chunk_offset, (long int) chunk_len, status); goto err; } buf += status; pending_len -= status; retval += status; } mutex_unlock(&port_data->update_lock); return retval; err: mutex_unlock(&port_data->update_lock); return status; } #else static ssize_t sfp_port_read(struct sfp_port_data *data, char *buf, loff_t off, size_t count) { ssize_t retval = 0; if (unlikely(!count)) { DEBUG_PRINT("Count = 0, return"); return count; } /* * Read data from chip, protecting against concurrent updates * from this host, but not from other I2C masters. */ mutex_lock(&data->update_lock); while (count) { ssize_t status; status = sfp_eeprom_read(data->client, off, buf, count); if (status <= 0) { if (retval == 0) { retval = status; } break; } buf += status; off += status; count -= status; retval += status; } mutex_unlock(&data->update_lock); return retval; } #endif static ssize_t sfp_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { int present; struct sfp_port_data *data; DEBUG_PRINT("offset = (%d), count = (%d)", off, count); data = dev_get_drvdata(container_of(kobj, struct device, kobj)); present = sfp_is_port_present(data->client, data->port); if (IS_ERR_VALUE(present)) { return present; } if (present == 0) { /* port is not present */ return -ENODEV; } #if (MULTIPAGE_SUPPORT == 1) return sfp_port_read_write(data, buf, off, count, QSFP_READ_OP); #else return sfp_port_read(data, buf, off, count); #endif } #if (MULTIPAGE_SUPPORT == 1) static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom, size_t size) #else static int sfp_sysfs_eeprom_init(struct kobject *kobj, struct bin_attribute *eeprom) #endif { int err; sysfs_bin_attr_init(eeprom); eeprom->attr.name = EEPROM_NAME; eeprom->attr.mode = S_IWUSR | S_IRUGO; eeprom->read = sfp_bin_read; eeprom->write = sfp_bin_write; #if (MULTIPAGE_SUPPORT == 1) eeprom->size = size; #else eeprom->size = EEPROM_SIZE; #endif /* Create eeprom file */ err = sysfs_create_bin_file(kobj, eeprom); if (err) { return err; } return 0; } static int sfp_sysfs_eeprom_cleanup(struct kobject *kobj, struct bin_attribute *eeprom) { sysfs_remove_bin_file(kobj, eeprom); return 0; } #if (MULTIPAGE_SUPPORT == 0) static int sfp_i2c_check_functionality(struct i2c_client *client) { #if USE_I2C_BLOCK_READ return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK); #else return i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA); #endif } #endif static const struct attribute_group qsfp_group = { .attrs = qsfp_attributes, }; static int qsfp_probe(struct i2c_client *client, const struct i2c_device_id *dev_id, struct qsfp_data **data) { int status; struct qsfp_data *qsfp; #if (MULTIPAGE_SUPPORT == 0) if (!sfp_i2c_check_functionality(client)) { status = -EIO; goto exit; } #endif qsfp = kzalloc(sizeof(struct qsfp_data), GFP_KERNEL); if (!qsfp) { status = -ENOMEM; goto exit; } /* Register sysfs hooks */ status = sysfs_create_group(&client->dev.kobj, &qsfp_group); if (status) { goto exit_free; } /* init eeprom */ #if (MULTIPAGE_SUPPORT == 1) status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin, SFF_8436_EEPROM_SIZE); #else status = sfp_sysfs_eeprom_init(&client->dev.kobj, &qsfp->eeprom.bin); #endif if (status) { goto exit_remove; } *data = qsfp; dev_info(&client->dev, "qsfp '%s'\n", client->name); return 0; exit_remove: sysfs_remove_group(&client->dev.kobj, &qsfp_group); exit_free: kfree(qsfp); exit: return status; } /* Platform dependent +++ */ static int sfp_device_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { int ret = 0; struct sfp_port_data *data = NULL; if (client->addr != SFP_EEPROM_A0_I2C_ADDR) { return -ENODEV; } if (dev_id->driver_data < qfx5210_64x_port1 || dev_id->driver_data > qfx5210_64x_port64) { return -ENXIO; } data = kzalloc(sizeof(struct sfp_port_data), GFP_KERNEL); if (!data) { return -ENOMEM; } #if (MULTIPAGE_SUPPORT == 1) data->use_smbus = 0; /* Use I2C operations unless we're stuck with SMBus extensions. */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { data->use_smbus = I2C_SMBUS_I2C_BLOCK_DATA; } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { data->use_smbus = I2C_SMBUS_WORD_DATA; } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) { data->use_smbus = I2C_SMBUS_BYTE_DATA; } else { ret = -EPFNOSUPPORT; goto exit_kfree; } } if (!data->use_smbus || (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { /* * NOTE: AN-2079 * Finisar recommends that the host implement 1 byte writes * only since this module only supports 32 byte page boundaries. * 2 byte writes are acceptable for PE and Vout changes per * Application Note AN-2071. */ unsigned write_max = 1; if (write_max > io_limit) write_max = io_limit; if (data->use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) write_max = I2C_SMBUS_BLOCK_MAX; data->write_max = write_max; /* buffer (data + address at the beginning) */ data->writebuf = kmalloc(write_max + 2, GFP_KERNEL); if (!data->writebuf) { ret = -ENOMEM; goto exit_kfree; } } else { dev_warn(&client->dev, "cannot write due to controller restrictions."); } if (data->use_smbus == I2C_SMBUS_WORD_DATA || data->use_smbus == I2C_SMBUS_BYTE_DATA) { dev_notice(&client->dev, "Falling back to %s reads, " "performance will suffer\n", data->use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte"); } #endif i2c_set_clientdata(client, data); mutex_init(&data->update_lock); data->port = dev_id->driver_data; data->client = client; data->driver_type = DRIVER_TYPE_QSFP; ret = qsfp_probe(client, dev_id, &data->qsfp); if (ret < 0) { goto exit_kfree_buf; } return ret; exit_kfree_buf: #if (MULTIPAGE_SUPPORT == 1) if (data->writebuf) kfree(data->writebuf); #endif exit_kfree: kfree(data); return ret; } /* Platform dependent --- */ static int qsfp_remove(struct i2c_client *client, struct qsfp_data *data) { sfp_sysfs_eeprom_cleanup(&client->dev.kobj, &data->eeprom.bin); sysfs_remove_group(&client->dev.kobj, &qsfp_group); kfree(data); return 0; } static int sfp_device_remove(struct i2c_client *client) { int ret = 0; struct sfp_port_data *data = i2c_get_clientdata(client); if (data->driver_type == DRIVER_TYPE_QSFP) { ret = qsfp_remove(client, data->qsfp); } kfree(data); return ret; } /* Addresses scanned */ static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; static struct i2c_driver sfp_driver = { .driver = { .name = DRIVER_NAME, }, .probe = sfp_device_probe, .remove = sfp_device_remove, .id_table = sfp_device_id, .address_list = normal_i2c, }; static int __init sfp_init(void) { return i2c_add_driver(&sfp_driver); } static void __exit sfp_exit(void) { i2c_del_driver(&sfp_driver); } MODULE_AUTHOR("Brandon Chuang "); MODULE_DESCRIPTION("juniper qfx5210_64x_sfp driver"); MODULE_LICENSE("GPL"); module_init(sfp_init); module_exit(sfp_exit);