diff --git a/modules/mqnic/Makefile b/modules/mqnic/Makefile index a6cc7d764..292e351d8 100644 --- a/modules/mqnic/Makefile +++ b/modules/mqnic/Makefile @@ -1,7 +1,18 @@ # object files to build obj-m += mqnic.o -mqnic-objs += mqnic_main.o mqnic_dev.o mqnic_netdev.o mqnic_port.o mqnic_ethtool.o mqnic_i2c.o mqnic_ptp.o mqnic_tx.o mqnic_rx.o mqnic_cq.o mqnic_eq.o +mqnic-y += mqnic_main.o +mqnic-y += mqnic_dev.o +mqnic-y += mqnic_netdev.o +mqnic-y += mqnic_port.o +mqnic-y += mqnic_ptp.o +mqnic-y += mqnic_i2c.o +mqnic-y += mqnic_board.o +mqnic-y += mqnic_tx.o +mqnic-y += mqnic_rx.o +mqnic-y += mqnic_cq.o +mqnic-y += mqnic_eq.o +mqnic-y += mqnic_ethtool.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/modules/mqnic/mqnic.h b/modules/mqnic/mqnic.h index 0db09ace4..787708592 100644 --- a/modules/mqnic/mqnic.h +++ b/modules/mqnic/mqnic.h @@ -50,7 +50,14 @@ either expressed or implied, of The Regents of the University of California. #include "mqnic_hw.h" -struct mqnic_i2c_priv +struct mqnic_dev; + +struct mqnic_board_ops { + int (*init)(struct mqnic_dev *mqnic); + void (*deinit)(struct mqnic_dev *mqnic); +}; + +struct mqnic_i2c_bus { struct mqnic_dev *mqnic; @@ -63,6 +70,11 @@ struct mqnic_i2c_priv uint32_t scl_out_mask; uint32_t sda_in_mask; uint32_t sda_out_mask; + + struct list_head head; + + struct i2c_algo_bit_data algo; + struct i2c_adapter adapter; }; struct mqnic_dev { @@ -106,10 +118,10 @@ struct mqnic_dev { struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; + struct mqnic_board_ops *board_ops; + + struct list_head i2c_bus; int i2c_adapter_count; - struct i2c_algo_bit_data i2c_algo[MQNIC_MAX_I2C_ADAPTERS]; - struct i2c_adapter i2c_adapter[MQNIC_MAX_I2C_ADAPTERS]; - struct mqnic_i2c_priv i2c_priv[MQNIC_MAX_I2C_ADAPTERS]; int mod_i2c_client_count; struct i2c_client *mod_i2c_client[MQNIC_MAX_IF]; @@ -324,8 +336,16 @@ void mqnic_unregister_phc(struct mqnic_dev *mdev); ktime_t mqnic_read_cpl_ts(struct mqnic_dev *mdev, struct mqnic_ring *ring, const struct mqnic_cpl *cpl); // mqnic_i2c.c -int mqnic_init_i2c(struct mqnic_dev *mqnic); -void mqnic_remove_i2c(struct mqnic_dev *mqnic); +struct mqnic_i2c_bus *mqnic_i2c_bus_create(struct mqnic_dev *mqnic, u8 __iomem *reg); +struct i2c_adapter *mqnic_i2c_adapter_create(struct mqnic_dev *mqnic, u8 __iomem *reg); +void mqnic_i2c_bus_release(struct mqnic_i2c_bus *bus); +void mqnic_i2c_adapter_release(struct i2c_adapter *adapter); +int mqnic_i2c_init(struct mqnic_dev *mqnic); +void mqnic_i2c_deinit(struct mqnic_dev *mqnic); + +// mqnic_board.c +int mqnic_board_init(struct mqnic_dev *mqnic); +void mqnic_board_deinit(struct mqnic_dev *mqnic); // mqnic_eq.c int mqnic_create_eq_ring(struct mqnic_priv *priv, struct mqnic_eq_ring **ring_ptr, int size, int stride, int index, u8 __iomem *hw_addr); diff --git a/modules/mqnic/mqnic_board.c b/modules/mqnic/mqnic_board.c new file mode 100644 index 000000000..704b20f47 --- /dev/null +++ b/modules/mqnic/mqnic_board.c @@ -0,0 +1,468 @@ +/* + +Copyright 2021, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +#include "mqnic.h" + +#include +#include +#include + +static const struct property_entry i2c_mux_props[] = { + PROPERTY_ENTRY_BOOL("i2c-mux-idle-disconnect"), + { } +}; + +static struct i2c_client *create_i2c_client(struct i2c_adapter *adapter, const char *type, int addr, const struct property_entry *props) +{ + struct i2c_client *client; + struct i2c_board_info board_info; + int err; + + if (!adapter) + return NULL; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, type, I2C_NAME_SIZE); + board_info.addr = addr; + board_info.properties = props; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0) + client = i2c_new_client_device(adapter, &board_info); +#else + client = i2c_new_device(adapter, &board_info); +#endif + + if (!client) + return NULL; + + // force driver load (mainly for muxes so we can talk to downstream devices) + err = device_attach(&client->dev); + if (err < 0) + goto err_free_client; + + return client; + +err_free_client: + i2c_unregister_device(client); + return NULL; +} + +static struct i2c_adapter *get_i2c_mux_channel(struct i2c_client *mux, u32 chan_id) +{ + struct i2c_mux_core *muxc; + + if (!mux) + return NULL; + + muxc = i2c_get_clientdata(mux); + + if (!muxc || chan_id >= muxc->num_adapters) + return NULL; + + return muxc->adapter[chan_id]; +} + +static int read_mac_from_eeprom(struct mqnic_dev *mqnic, struct i2c_client *eeprom, int offset, char *mac) +{ + int ret; + + if (!eeprom) + { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM; no EEPROM I2C client registered"); + return -1; + } + + ret = i2c_smbus_read_i2c_block_data(eeprom, offset, 6, mac); + if (ret < 0) + { + dev_warn(mqnic->dev, "Failed to read MAC from EEPROM"); + return -1; + } + + return 0; +} + +static int init_mac_list_from_eeprom_base(struct mqnic_dev *mqnic, struct i2c_client *eeprom, int offset, int count) +{ + int ret, k; + char mac[6]; + + count = min(count, MQNIC_MAX_IF); + + ret = read_mac_from_eeprom(mqnic, eeprom, offset, mac); + if (ret < 0) + { + return ret; + } + + if (!is_valid_ether_addr(mac)) + { + dev_warn(mqnic->dev, "EEPROM does not contain a valid base MAC"); + return -1; + } + + mqnic->mac_count = count; + for (k = 0; k < mqnic->mac_count; k++) + { + memcpy(mqnic->mac_list[k], mac, ETH_ALEN); + mqnic->mac_list[k][ETH_ALEN-1] += k; + } + + return count; +} + +static int mqnic_generic_board_init(struct mqnic_dev *mqnic) +{ + struct i2c_adapter *adapter; + struct i2c_client *mux; + int ret = 0; + + mqnic->mod_i2c_client_count = 0; + + if (mqnic_i2c_init(mqnic)) + { + dev_err(mqnic->dev, "Failed to initialize I2C subsystem"); + return -1; + } + + switch (mqnic->board_id) { + case MQNIC_BOARD_ID_NETFPGA_SUME: + // FPGA IC12 + // TCA9548 IC31 0x74 + // CH0: SFP1 IC3 0x50 + // CH1: SFP2 IC5 0x50 + // CH2: SFP3 IC6 0x50 + // CH3: SFP4 IC8 0x50 + // CH4: DDR3 IC27 0x51 + // DDR3 IC28 0x52 + // SI5324 IC20 0x68 + // CH5: FMC + // CH6: PCON + // CH7: PMOD J11 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // IC31 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); + + // IC3 SFP1 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50, NULL); + + // IC5 SFP2 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50, NULL); + + // IC6 SFP3 + mqnic->mod_i2c_client[2] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); + + // IC8 SFP4 + mqnic->mod_i2c_client[3] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50, NULL); + + mqnic->mod_i2c_client_count = 4; + + break; + case MQNIC_BOARD_ID_VCU108: + // FPGA U1 + // TCA9548 U28 0x74 + // CH0: SI570 Osc U32 0x5D + // CH1: TCA6416 Port Exp U89 0x21 + // CH2: QSFP U145 0x50 + // CH3: NC + // CH4: SI5328 U57 0x68 + // CH5: HDMI U52 0x39 + // CH6: SYSMON U1 0x32 + // CH7: NC + // PCA9544 U80 0x75 + // CH0: PMBUS + // CH1: FMC_HPC0 J22 + // CH2: FMC_HPC1 J2 + // CH3: M24C08 EEPROM U12 0x54 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // U28 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); + + // U145 QSFP + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); + + // U80 PCA9544 I2C MUX + mux = create_i2c_client(adapter, "pca9544", 0x75, i2c_mux_props); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54, NULL); + + mqnic->mod_i2c_client_count = 1; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_VCU118: + // FPGA U1 + // TCA9548 U28 0x74 + // CH0: SI570 Osc U32 0x5D + // CH1: NC + // CH2: QSFP1 U145 0x50 + // CH3: QSFP2 U123 0x50 + // CH4: SI5328 U57 0x68 + // CH5: SI570 Osc U18 0x5D + // CH6: SYSMON U1 0x32 + // CH7: FIREFLY J6 0x50 + // TCA9548 U80 0x75 + // CH0: PMBUS + // CH1: FMCP_HSPC J22 + // CH2: FMC_HPC1 J2 + // CH3: M24C08 EEPROM U12 0x54 + // CH4: INA_PMBUS + // CH5: SI570 Osc U38 0x5D + // CH6: NC + // CH7: NC + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // U28 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); + + // U145 QSFP1 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); + + // U123 QSFP2 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50, NULL); + + // U80 PCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x75, i2c_mux_props); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54, NULL); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_VCU1525: + case MQNIC_BOARD_ID_AU200: + case MQNIC_BOARD_ID_AU250: + // FPGA U13 + // PCA9546 U28 0x74 + // CH0: QSFP0 J7 0x50 + // CH1: QSFP1 J9 0x50 + // CH2: M24C08 EEPROM U62 0x54 + // SI570 Osc U14 0x5D + // CH3: SYSMON U13 0x32 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // U28 TCA9546 I2C MUX + mux = create_i2c_client(adapter, "pca9546", 0x74, i2c_mux_props); + + // J7 QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50, NULL); + + // J9 QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50, NULL); + + // U12 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c08", 0x54, NULL); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_ZCU106: + // FPGA U1 / MSP430 U41 I2C0 + // TCA6416 U61 0x21 + // TCA6416 U97 0x20 + // PCA9544 U60 0x75 + // CH0: PS_PMBUS + // CH1: PL_PMBUS + // CH2: MAXIM_PMBUS + // CH3: SYSMON U1 0x32 + // FPGA U1 / MSP430 U41 I2C1 + // TCA9548 U34 0x74 + // CH0: M24C08 EEPROM U23 0x54 + // CH1: SI5341 U69 0x36 + // CH2: SI570 Osc U42 0x5D + // CH3: SI570 Osc U56 0x5D + // CH4: SI5328 U20 0x68 + // CH5: NC + // CH6: NC + // CH7: NC + // TCA9548 U135 0x75 + // CH0: FMC_HPC0 J5 + // CH1: FMC_HPC1 J4 + // CH2: SYSMON U1 0x32 + // CH3: DDR4 SODIMM 0x51 + // CH4: NC + // CH5: NC + // CH6: SFP1 P2 0x50 + // CH7: SFP0 P1 0x50 + + request_module("i2c_mux_pca954x"); + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // U34 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); + + // U23 I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c08", 0x54, NULL); + + // U135 TCA9548 I2C MUX + mux = create_i2c_client(adapter, "pca9548", 0x75, i2c_mux_props); + + // P1 SFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 7), "24c02", 0x50, NULL); + + // P2 SFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 6), "24c02", 0x50, NULL); + + mqnic->mod_i2c_client_count = 2; + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + case MQNIC_BOARD_ID_FB2CG_KU15P: + // FPGA U1 I2C0 + // QSFP0 J3 0x50 + // FPGA U1 I2C1 + // QSFP1 J4 0x50 + + request_module("at24"); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); + + // QSFP0 + mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50, NULL); + + // I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_1); + + // QSFP1 + mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50, NULL); + + mqnic->mod_i2c_client_count = 2; + + break; + case MQNIC_BOARD_ID_EXANIC_X10: + case MQNIC_BOARD_ID_EXANIC_X25: + case MQNIC_BOARD_ID_ADM_PCIE_9V3: + + // create I2C adapter + adapter = mqnic_i2c_adapter_create(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_1); + + // I2C EEPROM + mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x50, NULL); + + // read MACs from EEPROM + init_mac_list_from_eeprom_base(mqnic, mqnic->eeprom_i2c_client, 0, MQNIC_MAX_IF); + + break; + default: + dev_warn(mqnic->dev, "Unknown board ID, not performing any board-specific init"); + } + + return ret; +} + +static void mqnic_generic_board_deinit(struct mqnic_dev *mqnic) +{ + int k; + + // unregister I2C clients + for (k = 0; k < ARRAY_SIZE(mqnic->mod_i2c_client); k++) + { + if (mqnic->mod_i2c_client[k]) + { + i2c_unregister_device(mqnic->mod_i2c_client[k]); + mqnic->mod_i2c_client[k] = NULL; + } + } + + if (mqnic->eeprom_i2c_client) + { + i2c_unregister_device(mqnic->eeprom_i2c_client); + mqnic->eeprom_i2c_client = NULL; + } + + mqnic_i2c_deinit(mqnic); +} + +static struct mqnic_board_ops generic_board_ops = { + .init = mqnic_generic_board_init, + .deinit = mqnic_generic_board_deinit +}; + +int mqnic_board_init(struct mqnic_dev *mqnic) +{ + switch (mqnic->board_id) { + default: + mqnic->board_ops = &generic_board_ops; + } + + if (!mqnic->board_ops) + return -1; + + return mqnic->board_ops->init(mqnic); +} + +void mqnic_board_deinit(struct mqnic_dev *mqnic) +{ + if (!mqnic->board_ops) + return; + + mqnic->board_ops->deinit(mqnic); +} diff --git a/modules/mqnic/mqnic_i2c.c b/modules/mqnic/mqnic_i2c.c index 9da807341..b7eebaa91 100644 --- a/modules/mqnic/mqnic_i2c.c +++ b/modules/mqnic/mqnic_i2c.c @@ -32,142 +32,86 @@ either expressed or implied, of The Regents of the University of California. */ #include "mqnic.h" -#include -#include -#include static void mqnic_i2c_set_scl(void *data, int state) { - struct mqnic_i2c_priv *priv = data; + struct mqnic_i2c_bus *bus = data; if (state) { - iowrite32(ioread32(priv->scl_out_reg) | priv->scl_out_mask, priv->scl_out_reg); + iowrite32(ioread32(bus->scl_out_reg) | bus->scl_out_mask, bus->scl_out_reg); } else { - iowrite32(ioread32(priv->scl_out_reg) & ~priv->scl_out_mask, priv->scl_out_reg); + iowrite32(ioread32(bus->scl_out_reg) & ~bus->scl_out_mask, bus->scl_out_reg); } } static void mqnic_i2c_set_sda(void *data, int state) { - struct mqnic_i2c_priv *priv = data; + struct mqnic_i2c_bus *bus = data; if (state) { - iowrite32(ioread32(priv->sda_out_reg) | priv->sda_out_mask, priv->sda_out_reg); + iowrite32(ioread32(bus->sda_out_reg) | bus->sda_out_mask, bus->sda_out_reg); } else { - iowrite32(ioread32(priv->sda_out_reg) & ~priv->sda_out_mask, priv->sda_out_reg); + iowrite32(ioread32(bus->sda_out_reg) & ~bus->sda_out_mask, bus->sda_out_reg); } } static int mqnic_i2c_get_scl(void *data) { - struct mqnic_i2c_priv *priv = data; + struct mqnic_i2c_bus *bus = data; - return !!(ioread32(priv->scl_in_reg) & priv->scl_in_mask); + return !!(ioread32(bus->scl_in_reg) & bus->scl_in_mask); } static int mqnic_i2c_get_sda(void *data) { - struct mqnic_i2c_priv *priv = data; + struct mqnic_i2c_bus *bus = data; - return !!(ioread32(priv->sda_in_reg) & priv->sda_in_mask); + return !!(ioread32(bus->sda_in_reg) & bus->sda_in_mask); } -static const struct i2c_algo_bit_data mqnic_i2c_algo = { - .setsda = mqnic_i2c_set_sda, - .setscl = mqnic_i2c_set_scl, - .getsda = mqnic_i2c_get_sda, - .getscl = mqnic_i2c_get_scl, - .udelay = 5, - .timeout = 20 -}; - -static const struct property_entry i2c_mux_props[] = { - PROPERTY_ENTRY_BOOL("i2c-mux-idle-disconnect"), - { } -}; - -static struct i2c_client *create_i2c_client(struct i2c_adapter *adapter, const char *type, int addr, const struct property_entry *props) -{ - struct i2c_client *client; - struct i2c_board_info board_info; - int err; - - if (!adapter) - return NULL; - - memset(&board_info, 0, sizeof(board_info)); - strscpy(board_info.type, type, I2C_NAME_SIZE); - board_info.addr = addr; - board_info.properties = props; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0) - client = i2c_new_client_device(adapter, &board_info); -#else - client = i2c_new_device(adapter, &board_info); -#endif - - if (!client) - return NULL; - - // force driver load (mainly for muxes so we can talk to downstream devices) - err = device_attach(&client->dev); - if (err < 0) - goto err_free_client; - - return client; - -err_free_client: - i2c_unregister_device(client); - return NULL; -} - -static struct i2c_adapter *get_i2c_mux_channel(struct i2c_client *mux, u32 chan_id) -{ - struct i2c_mux_core *muxc; - - if (!mux) - return NULL; - - muxc = i2c_get_clientdata(mux); - - if (!muxc || chan_id >= muxc->num_adapters) - return NULL; - - return muxc->adapter[chan_id]; -} - -static struct i2c_adapter *mqnic_create_i2c_adapter(struct mqnic_dev *mqnic, u8 __iomem *reg) +struct mqnic_i2c_bus *mqnic_i2c_bus_create(struct mqnic_dev *mqnic, u8 __iomem *reg) { + struct mqnic_i2c_bus *bus; struct i2c_algo_bit_data *algo; struct i2c_adapter *adapter; - struct mqnic_i2c_priv *priv; - if (mqnic->i2c_adapter_count >= MQNIC_MAX_I2C_ADAPTERS || !reg) + if (!reg) return NULL; - algo = &mqnic->i2c_algo[mqnic->i2c_adapter_count]; - adapter = &mqnic->i2c_adapter[mqnic->i2c_adapter_count]; - priv = &mqnic->i2c_priv[mqnic->i2c_adapter_count]; + bus = kzalloc(sizeof(*bus), GFP_KERNEL); - priv->mqnic = mqnic; - priv->scl_in_reg = reg; - priv->scl_out_reg = reg; - priv->sda_in_reg = reg; - priv->sda_out_reg = reg; - priv->scl_in_mask = MQNIC_REG_GPIO_I2C_SCL_IN; - priv->scl_out_mask = MQNIC_REG_GPIO_I2C_SCL_OUT; - priv->sda_in_mask = MQNIC_REG_GPIO_I2C_SDA_IN; - priv->sda_out_mask = MQNIC_REG_GPIO_I2C_SDA_OUT; + if (!bus) + return NULL; - *algo = mqnic_i2c_algo; - algo->data = priv; + // set private data + bus->mqnic = mqnic; + bus->scl_in_reg = reg; + bus->scl_out_reg = reg; + bus->sda_in_reg = reg; + bus->sda_out_reg = reg; + bus->scl_in_mask = MQNIC_REG_GPIO_I2C_SCL_IN; + bus->scl_out_mask = MQNIC_REG_GPIO_I2C_SCL_OUT; + bus->sda_in_mask = MQNIC_REG_GPIO_I2C_SDA_IN; + bus->sda_out_mask = MQNIC_REG_GPIO_I2C_SDA_OUT; + // bit-bang algorithm setup + algo = &bus->algo; + algo->udelay = 5; + algo->timeout = usecs_to_jiffies(2000);; + algo->setsda = mqnic_i2c_set_sda; + algo->setscl = mqnic_i2c_set_scl; + algo->getsda = mqnic_i2c_get_sda; + algo->getscl = mqnic_i2c_get_scl; + algo->data = bus; + + // adapter setup + adapter = &bus->adapter; adapter->owner = THIS_MODULE; adapter->algo_data = algo; adapter->dev.parent = mqnic->dev; @@ -176,325 +120,69 @@ static struct i2c_adapter *mqnic_create_i2c_adapter(struct mqnic_dev *mqnic, u8 if (i2c_bit_add_bus(adapter)) { dev_err(mqnic->dev, "Failed to register I2C adapter"); - memset(adapter, 0, sizeof(*adapter)); - return NULL; + goto err_free_bus; } + list_add_tail(&bus->head, &mqnic->i2c_bus); + mqnic->i2c_adapter_count++; - return adapter; + return bus; + +err_free_bus: + kfree(bus); + return NULL; } -int mqnic_init_i2c(struct mqnic_dev *mqnic) +struct i2c_adapter *mqnic_i2c_adapter_create(struct mqnic_dev *mqnic, u8 __iomem *reg) { - struct i2c_adapter *adapter; - struct i2c_client *mux; - int ret = 0; + struct mqnic_i2c_bus *bus = mqnic_i2c_bus_create(mqnic, reg); - mqnic->mod_i2c_client_count = 0; + if (!bus) + return NULL; - // Interface I2C bus - switch (mqnic->board_id) { - case MQNIC_BOARD_ID_NETFPGA_SUME: - // FPGA IC12 - // TCA9548 IC31 0x74 - // CH0: SFP1 IC3 0x50 - // CH1: SFP2 IC5 0x50 - // CH2: SFP3 IC6 0x50 - // CH3: SFP4 IC8 0x50 - // CH4: DDR3 IC27 0x51 - // DDR3 IC28 0x52 - // SI5324 IC20 0x68 - // CH5: FMC - // CH6: PCON - // CH7: PMOD J11 - - request_module("i2c_mux_pca954x"); - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // IC31 TCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); - - // IC3 SFP1 - mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50, NULL); - - // IC5 SFP2 - mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50, NULL); - - // IC6 SFP3 - mqnic->mod_i2c_client[2] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); - - // IC8 SFP4 - mqnic->mod_i2c_client[3] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50, NULL); - - mqnic->mod_i2c_client_count = 4; - - break; - case MQNIC_BOARD_ID_VCU108: - // FPGA U1 - // TCA9548 U28 0x74 - // CH0: SI570 Osc U32 0x5D - // CH1: TCA6416 Port Exp U89 0x21 - // CH2: QSFP U145 0x50 - // CH3: NC - // CH4: SI5328 U57 0x68 - // CH5: HDMI U52 0x39 - // CH6: SYSMON U1 0x32 - // CH7: NC - // PCA9544 U80 0x75 - // CH0: PMBUS - // CH1: FMC_HPC0 J22 - // CH2: FMC_HPC1 J2 - // CH3: M24C08 EEPROM U12 0x54 - - request_module("i2c_mux_pca954x"); - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // U28 TCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); - - // U145 QSFP - mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); - - // U80 PCA9544 I2C MUX - mux = create_i2c_client(adapter, "pca9544", 0x75, i2c_mux_props); - - // U12 I2C EEPROM - mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54, NULL); - - mqnic->mod_i2c_client_count = 1; - - break; - case MQNIC_BOARD_ID_VCU118: - // FPGA U1 - // TCA9548 U28 0x74 - // CH0: SI570 Osc U32 0x5D - // CH1: NC - // CH2: QSFP1 U145 0x50 - // CH3: QSFP2 U123 0x50 - // CH4: SI5328 U57 0x68 - // CH5: SI570 Osc U18 0x5D - // CH6: SYSMON U1 0x32 - // CH7: FIREFLY J6 0x50 - // TCA9548 U80 0x75 - // CH0: PMBUS - // CH1: FMCP_HSPC J22 - // CH2: FMC_HPC1 J2 - // CH3: M24C08 EEPROM U12 0x54 - // CH4: INA_PMBUS - // CH5: SI570 Osc U38 0x5D - // CH6: NC - // CH7: NC - - request_module("i2c_mux_pca954x"); - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // U28 TCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); - - // U145 QSFP1 - mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c02", 0x50, NULL); - - // U123 QSFP2 - mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c02", 0x50, NULL); - - // U80 PCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x75, i2c_mux_props); - - // U12 I2C EEPROM - mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 3), "24c08", 0x54, NULL); - - mqnic->mod_i2c_client_count = 2; - - break; - case MQNIC_BOARD_ID_VCU1525: - case MQNIC_BOARD_ID_AU200: - case MQNIC_BOARD_ID_AU250: - // FPGA U13 - // PCA9546 U28 0x74 - // CH0: QSFP0 J7 0x50 - // CH1: QSFP1 J9 0x50 - // CH2: M24C08 EEPROM U62 0x54 - // SI570 Osc U14 0x5D - // CH3: SYSMON U13 0x32 - - request_module("i2c_mux_pca954x"); - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // U28 TCA9546 I2C MUX - mux = create_i2c_client(adapter, "pca9546", 0x74, i2c_mux_props); - - // J7 QSFP0 - mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c02", 0x50, NULL); - - // J9 QSFP1 - mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 1), "24c02", 0x50, NULL); - - // U12 I2C EEPROM - mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 2), "24c08", 0x54, NULL); - - mqnic->mod_i2c_client_count = 2; - - break; - case MQNIC_BOARD_ID_ZCU106: - // FPGA U1 / MSP430 U41 I2C0 - // TCA6416 U61 0x21 - // TCA6416 U97 0x20 - // PCA9544 U60 0x75 - // CH0: PS_PMBUS - // CH1: PL_PMBUS - // CH2: MAXIM_PMBUS - // CH3: SYSMON U1 0x32 - // FPGA U1 / MSP430 U41 I2C1 - // TCA9548 U34 0x74 - // CH0: M24C08 EEPROM U23 0x54 - // CH1: SI5341 U69 0x36 - // CH2: SI570 Osc U42 0x5D - // CH3: SI570 Osc U56 0x5D - // CH4: SI5328 U20 0x68 - // CH5: NC - // CH6: NC - // CH7: NC - // TCA9548 U135 0x75 - // CH0: FMC_HPC0 J5 - // CH1: FMC_HPC1 J4 - // CH2: SYSMON U1 0x32 - // CH3: DDR4 SODIMM 0x51 - // CH4: NC - // CH5: NC - // CH6: SFP1 P2 0x50 - // CH7: SFP0 P1 0x50 - - request_module("i2c_mux_pca954x"); - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // U34 TCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x74, i2c_mux_props); - - // U23 I2C EEPROM - mqnic->eeprom_i2c_client = create_i2c_client(get_i2c_mux_channel(mux, 0), "24c08", 0x54, NULL); - - // U135 TCA9548 I2C MUX - mux = create_i2c_client(adapter, "pca9548", 0x75, i2c_mux_props); - - // P1 SFP0 - mqnic->mod_i2c_client[0] = create_i2c_client(get_i2c_mux_channel(mux, 7), "24c02", 0x50, NULL); - - // P2 SFP1 - mqnic->mod_i2c_client[1] = create_i2c_client(get_i2c_mux_channel(mux, 6), "24c02", 0x50, NULL); - - mqnic->mod_i2c_client_count = 2; - - break; - case MQNIC_BOARD_ID_FB2CG_KU15P: - // FPGA U1 I2C0 - // QSFP0 J3 0x50 - // FPGA U1 I2C1 - // QSFP1 J4 0x50 - - request_module("at24"); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_0); - - // QSFP0 - mqnic->mod_i2c_client[0] = create_i2c_client(adapter, "24c02", 0x50, NULL); - - // I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_1); - - // QSFP1 - mqnic->mod_i2c_client[1] = create_i2c_client(adapter, "24c02", 0x50, NULL); - - mqnic->mod_i2c_client_count = 2; - - break; - } - - // EEPROM I2C bus - switch (mqnic->board_id) { - case MQNIC_BOARD_ID_EXANIC_X10: - case MQNIC_BOARD_ID_EXANIC_X25: - case MQNIC_BOARD_ID_ADM_PCIE_9V3: - - // create I2C adapter - adapter = mqnic_create_i2c_adapter(mqnic, mqnic->hw_addr+MQNIC_REG_GPIO_I2C_1); - - // I2C EEPROM - mqnic->eeprom_i2c_client = create_i2c_client(adapter, "24c02", 0x50, NULL); - - break; - } - - // Read MAC from EEPROM - if (mqnic->eeprom_i2c_client) - { - int r, k; - - r = i2c_smbus_read_i2c_block_data(mqnic->eeprom_i2c_client, 0x00, 6, mqnic->mac_list[0]); - if (r < 0) - { - dev_warn(mqnic->dev, "Failed to read MAC from EEPROM"); - } - else - { - mqnic->mac_count = MQNIC_MAX_IF; - for (k = 1; k < mqnic->mac_count; k++) - { - memcpy(mqnic->mac_list[k], mqnic->mac_list[0], ETH_ALEN); - mqnic->mac_list[k][ETH_ALEN-1] += k; - } - } - } - - return ret; + return &bus->adapter; } -void mqnic_remove_i2c(struct mqnic_dev *mqnic) +void mqnic_i2c_bus_release(struct mqnic_i2c_bus *bus) { - int k; + struct mqnic_dev *mqnic; - // unregister I2C clients - for (k = 0; k < ARRAY_SIZE(mqnic->mod_i2c_client); k++) - { - if (mqnic->mod_i2c_client[k]) - { - i2c_unregister_device(mqnic->mod_i2c_client[k]); - mqnic->mod_i2c_client[k] = NULL; - } - } + if (!bus) + return; - if (mqnic->eeprom_i2c_client) - { - i2c_unregister_device(mqnic->eeprom_i2c_client); - mqnic->eeprom_i2c_client = NULL; - } + mqnic = bus->mqnic; - // delete adapters - for (k = 0; k < ARRAY_SIZE(mqnic->i2c_adapter); k++) - { - if (mqnic->i2c_adapter[k].owner) - { - i2c_del_adapter(&mqnic->i2c_adapter[k]); - } + mqnic->i2c_adapter_count--; - memset(&mqnic->i2c_adapter[k], 0, sizeof(mqnic->i2c_adapter[k])); - } + i2c_del_adapter(&bus->adapter); + list_del(&bus->head); + kfree(bus); } +void mqnic_i2c_adapter_release(struct i2c_adapter *adapter) +{ + struct mqnic_i2c_bus *bus; + if (!adapter) + return; + + bus = container_of(adapter, struct mqnic_i2c_bus, adapter); + mqnic_i2c_bus_release(bus); +} + +int mqnic_i2c_init(struct mqnic_dev *mqnic) +{ + INIT_LIST_HEAD(&mqnic->i2c_bus); + + return 0; +} + +void mqnic_i2c_deinit(struct mqnic_dev *mqnic) +{ + while (!list_empty(&mqnic->i2c_bus)) + { + struct mqnic_i2c_bus *bus = list_first_entry(&mqnic->i2c_bus, typeof(*bus), head); + mqnic_i2c_bus_release(bus); + } +} diff --git a/modules/mqnic/mqnic_main.c b/modules/mqnic/mqnic_main.c index 4b67baa92..72fd1c058 100644 --- a/modules/mqnic/mqnic_main.c +++ b/modules/mqnic/mqnic_main.c @@ -250,12 +250,12 @@ static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent mqnic->irq_map[k] = pci_irq_vector(pdev, k); } - // Set up I2C interfaces - ret = mqnic_init_i2c(mqnic); + // Board-specific init + ret = mqnic_board_init(mqnic); if (ret) { - dev_err(dev, "Failed to register I2C interfaces"); - goto fail_i2c; + dev_err(dev, "Failed to initialize board"); + goto fail_board; } // Enable bus mastering for DMA @@ -320,8 +320,8 @@ fail_init_netdev: } mqnic_unregister_phc(mqnic); pci_clear_master(pdev); -fail_i2c: - mqnic_remove_i2c(mqnic); +fail_board: + mqnic_board_deinit(mqnic); for (k = 0; k < mqnic->irq_count; k++) { pci_free_irq(pdev, k, mqnic); @@ -365,7 +365,7 @@ static void mqnic_pci_remove(struct pci_dev *pdev) mqnic_unregister_phc(mqnic); pci_clear_master(pdev); - mqnic_remove_i2c(mqnic); + mqnic_board_deinit(mqnic); for (k = 0; k < mqnic->irq_count; k++) { pci_free_irq(pdev, k, mqnic);