| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2012 Freescale Semiconductor, Inc. |
| * Copyright 2020 NXP |
| */ |
| |
| #include "vsc3316_3308.h" |
| #include <log.h> |
| |
| #define REVISION_ID_REG 0x7E |
| #define INTERFACE_MODE_REG 0x79 |
| #define CURRENT_PAGE_REGISTER 0x7F |
| #define CONNECTION_CONFIG_PAGE 0x00 |
| #define INPUT_STATE_REG 0x13 |
| #define GLOBAL_INPUT_ISE1 0x51 |
| #define GLOBAL_INPUT_ISE2 0x52 |
| #define GLOBAL_INPUT_GAIN 0x53 |
| #define GLOBAL_INPUT_LOS 0x55 |
| #define GLOBAL_OUTPUT_PE1 0x56 |
| #define GLOBAL_OUTPUT_PE2 0x57 |
| #define GLOBAL_OUTPUT_LEVEL 0x58 |
| #define GLOBAL_OUTPUT_TERMINATION 0x5A |
| #define GLOBAL_CORE_CNTRL 0x5D |
| #define OUTPUT_MODE_PAGE 0x23 |
| #define CORE_CONTROL_PAGE 0x25 |
| #define CORE_CONFIG_REG 0x75 |
| |
| int vsc_if_enable(unsigned int vsc_addr) |
| { |
| u8 data; |
| |
| debug("VSC:Configuring VSC at I2C address 0x%2x" |
| " for 2-wire interface\n", vsc_addr); |
| |
| /* enable 2-wire Serial InterFace (I2C) */ |
| data = 0x02; |
| #ifdef CONFIG_DM_I2C |
| int ret, bus_num = 0; |
| struct udevice *dev; |
| |
| ret = i2c_get_chip_for_busnum(bus_num, vsc_addr, |
| 1, &dev); |
| if (ret) { |
| printf("%s: Cannot find udev for a bus %d\n", __func__, |
| bus_num); |
| return ret; |
| } |
| |
| return dm_i2c_write(dev, INTERFACE_MODE_REG, &data, 1); |
| #else |
| return i2c_write(vsc_addr, INTERFACE_MODE_REG, 1, &data, 1); |
| #endif |
| } |
| |
| int vsc3316_config(unsigned int vsc_addr, int8_t con_arr[][2], |
| unsigned int num_con) |
| { |
| unsigned int i; |
| u8 rev_id = 0; |
| int ret; |
| |
| debug("VSC:Initializing VSC3316 at I2C address 0x%2x" |
| " for Tx\n", vsc_addr); |
| |
| #ifdef CONFIG_DM_I2C |
| int bus_num = 0; |
| struct udevice *dev; |
| |
| ret = i2c_get_chip_for_busnum(bus_num, vsc_addr, |
| 1, &dev); |
| if (ret) { |
| printf("%s: Cannot find udev for a bus %d\n", __func__, |
| bus_num); |
| return ret; |
| } |
| |
| ret = dm_i2c_read(dev, REVISION_ID_REG, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output |
| */ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], con_arr[i][0]); |
| |
| /* input state - page 0x13 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Configuring the required input of the switch */ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][0], 0x80); |
| |
| /* Setting Global Input LOS threshold value */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_LOS, 0x60); |
| |
| /* config output mode - page 0x23 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn ON the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], 0); |
| |
| /* configure global core control register, Turn on Global core power */ |
| dm_i2c_reg_write(dev, GLOBAL_CORE_CNTRL, 0); |
| |
| #else |
| ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output */ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]); |
| |
| /* input state - page 0x13 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Configuring the required input of the switch */ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][0], 0x80); |
| |
| /* Setting Global Input LOS threshold value */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0x60); |
| |
| /* config output mode - page 0x23 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn ON the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], 0); |
| |
| /* configure global core control register, Turn on Global core power */ |
| i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0); |
| #endif |
| |
| vsc_wp_config(vsc_addr); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_SYS_FSL_B4860QDS_XFI_ERR |
| int vsc3308_config_adjust(unsigned int vsc_addr, const int8_t con_arr[][2], |
| unsigned int num_con) |
| { |
| unsigned int i; |
| u8 rev_id = 0; |
| int ret; |
| |
| debug("VSC:Initializing VSC3308 at I2C address 0x%x for Tx\n", |
| vsc_addr); |
| |
| #ifdef CONFIG_DM_I2C |
| int bus_num = 0; |
| struct udevice *dev; |
| |
| ret = i2c_get_chip_for_busnum(bus_num, vsc_addr, |
| 1, &dev); |
| if (ret) { |
| printf("%s: Cannot find udev for a bus %d\n", __func__, |
| bus_num); |
| return ret; |
| } |
| |
| ret = dm_i2c_read(dev, REVISION_ID_REG, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Configure Global Input ISE */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_ISE1, 0); |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_ISE2, 0); |
| |
| /* Configure Tx/Rx Global Output PE1 */ |
| dm_i2c_reg_write(dev, GLOBAL_OUTPUT_PE1, 0); |
| |
| /* Configure Tx/Rx Global Output PE2 */ |
| dm_i2c_reg_write(dev, GLOBAL_OUTPUT_PE2, 0); |
| |
| /* Configure Tx/Rx Global Input GAIN */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_GAIN, 0x3F); |
| |
| /* Setting Global Input LOS threshold value */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_LOS, 0xE0); |
| |
| /* Setting Global output termination */ |
| dm_i2c_reg_write(dev, GLOBAL_OUTPUT_TERMINATION, 0); |
| |
| /* Configure Tx/Rx Global Output level */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) |
| dm_i2c_reg_write(dev, GLOBAL_OUTPUT_LEVEL, 4); |
| else |
| dm_i2c_reg_write(dev, GLOBAL_OUTPUT_LEVEL, 2); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output |
| */ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], con_arr[i][0]); |
| |
| /* input state - page 0x13 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Turning off all the required input of the switch */ |
| for (i = 0; i < num_con; i++) |
| dm_i2c_reg_write(dev, con_arr[i][0], 1); |
| |
| /* only turn on specific Tx/Rx requested by the XFI erratum */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) { |
| dm_i2c_reg_write(dev, 2, 0); |
| dm_i2c_reg_write(dev, 3, 0); |
| } else { |
| dm_i2c_reg_write(dev, 0, 0); |
| dm_i2c_reg_write(dev, 1, 0); |
| } |
| |
| /* config output mode - page 0x23 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn off the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], 1); |
| |
| /* only turn on specific Tx/Rx requested by the XFI erratum */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) { |
| dm_i2c_reg_write(dev, 0, 0); |
| dm_i2c_reg_write(dev, 1, 0); |
| } else { |
| dm_i2c_reg_write(dev, 3, 0); |
| dm_i2c_reg_write(dev, 4, 0); |
| } |
| |
| /* configure global core control register, Turn on Global core power */ |
| dm_i2c_reg_write(dev, GLOBAL_CORE_CNTRL, 0); |
| #else |
| ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Configure Global Input ISE */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE1, 0); |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE2, 0); |
| |
| /* Configure Tx/Rx Global Output PE1 */ |
| i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_PE1, 0); |
| |
| /* Configure Tx/Rx Global Output PE2 */ |
| i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_PE2, 0); |
| |
| /* Configure Tx/Rx Global Input GAIN */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_GAIN, 0x3F); |
| |
| /* Setting Global Input LOS threshold value */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0xE0); |
| |
| /* Setting Global output termination */ |
| i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_TERMINATION, 0); |
| |
| /* Configure Tx/Rx Global Output level */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) |
| i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_LEVEL, 4); |
| else |
| i2c_reg_write(vsc_addr, GLOBAL_OUTPUT_LEVEL, 2); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output */ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]); |
| |
| /* input state - page 0x13 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Turning off all the required input of the switch */ |
| for (i = 0; i < num_con; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][0], 1); |
| |
| /* only turn on specific Tx/Rx requested by the XFI erratum */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) { |
| i2c_reg_write(vsc_addr, 2, 0); |
| i2c_reg_write(vsc_addr, 3, 0); |
| } else { |
| i2c_reg_write(vsc_addr, 0, 0); |
| i2c_reg_write(vsc_addr, 1, 0); |
| } |
| |
| /* config output mode - page 0x23 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn off the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], 1); |
| |
| /* only turn on specific Tx/Rx requested by the XFI erratum */ |
| if (vsc_addr == VSC3308_TX_ADDRESS) { |
| i2c_reg_write(vsc_addr, 0, 0); |
| i2c_reg_write(vsc_addr, 1, 0); |
| } else { |
| i2c_reg_write(vsc_addr, 3, 0); |
| i2c_reg_write(vsc_addr, 4, 0); |
| } |
| |
| /* configure global core control register, Turn on Global core power */ |
| i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0); |
| #endif |
| vsc_wp_config(vsc_addr); |
| |
| return 0; |
| } |
| #endif |
| |
| int vsc3308_config(unsigned int vsc_addr, const int8_t con_arr[][2], |
| unsigned int num_con) |
| { |
| unsigned int i; |
| u8 rev_id = 0; |
| int ret; |
| |
| debug("VSC:Initializing VSC3308 at I2C address 0x%x" |
| " for Tx\n", vsc_addr); |
| #ifdef CONFIG_DM_I2C |
| int bus_num = 0; |
| struct udevice *dev; |
| |
| ret = i2c_get_chip_for_busnum(bus_num, vsc_addr, |
| 1, &dev); |
| if (ret) { |
| printf("%s: Cannot find udev for a bus %d\n", __func__, |
| bus_num); |
| return ret; |
| } |
| |
| ret = dm_i2c_read(dev, REVISION_ID_REG, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output |
| */ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], con_arr[i][0]); |
| |
| /*Configure Global Input ISE and gain */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_ISE1, 0x12); |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_ISE2, 0x12); |
| |
| /* input state - page 0x13 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Turning ON the required input of the switch */ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][0], 0); |
| |
| /* Setting Global Input LOS threshold value */ |
| dm_i2c_reg_write(dev, GLOBAL_INPUT_LOS, 0x60); |
| |
| /* config output mode - page 0x23 */ |
| dm_i2c_reg_write(dev, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn ON the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| dm_i2c_reg_write(dev, con_arr[i][1], 0); |
| |
| /* configure global core control register, Turn on Global core power */ |
| dm_i2c_reg_write(dev, GLOBAL_CORE_CNTRL, 0); |
| #else |
| ret = i2c_read(vsc_addr, REVISION_ID_REG, 1, &rev_id, 1); |
| if (ret < 0) { |
| printf("VSC:0x%x could not read REV_ID from device.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| if (rev_id != 0xab) { |
| printf("VSC: device at address 0x%x is not VSC3316/3308.\n", |
| vsc_addr); |
| return -ENODEV; |
| } |
| |
| ret = vsc_if_enable(vsc_addr); |
| if (ret) { |
| printf("VSC:0x%x could not configured for 2-wire I/F.\n", |
| vsc_addr); |
| return ret; |
| } |
| |
| /* config connections - page 0x00 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, CONNECTION_CONFIG_PAGE); |
| |
| /* Making crosspoint connections, by connecting required |
| * input to output */ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], con_arr[i][0]); |
| |
| /*Configure Global Input ISE and gain */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE1, 0x12); |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_ISE2, 0x12); |
| |
| /* input state - page 0x13 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, INPUT_STATE_REG); |
| /* Turning ON the required input of the switch */ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][0], 0); |
| |
| /* Setting Global Input LOS threshold value */ |
| i2c_reg_write(vsc_addr, GLOBAL_INPUT_LOS, 0x60); |
| |
| /* config output mode - page 0x23 */ |
| i2c_reg_write(vsc_addr, CURRENT_PAGE_REGISTER, OUTPUT_MODE_PAGE); |
| /* Turn ON the Output driver correspond to required output*/ |
| for (i = 0; i < num_con ; i++) |
| i2c_reg_write(vsc_addr, con_arr[i][1], 0); |
| |
| /* configure global core control register, Turn on Global core power */ |
| i2c_reg_write(vsc_addr, GLOBAL_CORE_CNTRL, 0); |
| #endif |
| vsc_wp_config(vsc_addr); |
| |
| return 0; |
| } |
| |
| void vsc_wp_config(unsigned int vsc_addr) |
| { |
| debug("VSC:Configuring VSC at address:0x%x for WP\n", vsc_addr); |
| |
| /* For new crosspoint configuration to occur, WP bit of |
| * CORE_CONFIG_REG should be set 1 and then reset to 0 */ |
| #ifdef CONFIG_DM_I2C |
| int ret, bus_num = 0; |
| struct udevice *dev; |
| |
| ret = i2c_get_chip_for_busnum(bus_num, vsc_addr, |
| 1, &dev); |
| if (ret) { |
| printf("%s: Cannot find udev for a bus %d\n", __func__, |
| bus_num); |
| return; |
| } |
| |
| dm_i2c_reg_write(dev, CORE_CONFIG_REG, 0x01); |
| dm_i2c_reg_write(dev, CORE_CONFIG_REG, 0x0); |
| #else |
| i2c_reg_write(vsc_addr, CORE_CONFIG_REG, 0x01); |
| i2c_reg_write(vsc_addr, CORE_CONFIG_REG, 0x0); |
| #endif |
| } |