pca9505.c (8395B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * BCM56xx I2C Device Driver for Phillips PCA9505 i2c switch 8 */ 9 10 #include <shared/bsl.h> 11 12 #include <sal/types.h> 13 #include <soc/debug.h> 14 #include <soc/drv.h> 15 #include <soc/error.h> 16 #include <soc/i2c.h> 17 #include <shared/bsl.h> 18 #if defined(SHADOW_SVK) 19 20 #ifdef SHADOW_SVK 21 #define PCA9505_DEV_MAX 3 22 #define PCA9505_PORT_MAX 5 23 #endif 24 25 #define PCA9505_IOP_BASE 0x08 26 #define PCA9505_POL_BASE 0x10 27 #define PCA9505_IOC_BASE 0x18 28 #define PCA9505_MSK_BASE 0x20 29 30 31 int 32 pio_devname(int dev, char **name) 33 { 34 switch (dev) { 35 case 0: 36 *name = I2C_IOP_0; 37 break; 38 case 1: 39 *name = I2C_IOP_1; 40 break; 41 default: 42 *name = I2C_IOP_0; 43 break; 44 } 45 return SOC_E_NONE; 46 } 47 48 49 /* Port configuration */ 50 static struct pca9505_port_config { 51 uint8 io_config; /* port pin: 0=ouput, 1==input */ 52 uint8 mask_intr; /* mask : 0=interrupt, 1==no interrupt*/ 53 uint8 defaults; /* default value in case of output*/ 54 uint8 op_config; /* used only if output, 1=toggle output, 0=directly set the default*/ 55 } pca9505_port_config[][PCA9505_PORT_MAX] = { 56 57 #ifdef SHADOW_SVK 58 { /* device @ SADDR0 */ 59 { 0xff, 0xff, 0x00, 0x00 }, /* port 0: SFP_ABS[0..7] */ 60 { 0x00, 0xff, 0x00, 0x00 }, /* port 1: SFP_TXDIS[0..7] */ 61 { 0xff, 0xff, 0x00, 0x00 }, /* port 2: SFP_TXFLT[0..7] */ 62 { 0xff, 0xff, 0x00, 0x00 }, /* port 3: QSFP_PRS[0..3], QSFP_INT[4..7] */ 63 { 0xff, 0xff, 0x00, 0x00 }, /* port 4: UNUSED */ 64 }, 65 { /* device @ SADDR1 */ 66 { 0xff, 0xff, 0x00, 0x00 }, /* port 0: XON_XOFF[0..7] */ /* changed this 67 to input as per Doug's request as these are overloaded for EB3 address 68 */ 69 { 0xff, 0xff, 0x00, 0x00 }, /* port 1: BRD_ID[0..7] */ 70 { 0x00, 0xff, 0x00, 0x00 }, /* port 2: SHAD_GPIO[0..7] */ 71 { 0xff, 0xff, 0x00, 0x00 }, /* port 3: UNUSED */ 72 { 0xff, 0xff, 0x00, 0x00 }, /* port 4: JTAG_TCE[0] */ 73 }, 74 { /* device @ SADDR0 */ 75 { 0xe0, 0xff, 0x0e, 0x00 }, /* port 0: MOD_ABS, LO-PWR, PRG[3:1], TXDIS */ 76 { 0xe0, 0xff, 0x0e, 0x00 }, /* port 1: MOD_ABS, LO-PWR, PRG[3:1], TXDIS */ 77 { 0xff, 0xff, 0x00, 0x00 }, /* port 2: SFP_TXFLT[0..7] */ 78 { 0xff, 0xff, 0x00, 0x00 }, /* port 3: QSFP_PRS[0..3], QSFP_INT[4..7] */ 79 { 0xff, 0xff, 0x00, 0x00 }, /* port 4: UNUSED */ 80 } 81 #endif 82 }; 83 84 85 STATIC int 86 pca9505_read(int unit, int devno, uint16 addr, uint8 *data, uint32 *len) 87 { 88 89 int rv; 90 uint8 saddr; 91 92 saddr = soc_i2c_addr(unit, devno); 93 94 rv = soc_i2c_read_byte_data(unit, saddr, addr, data); 95 *len = 1; /* Byte device */ 96 LOG_INFO(BSL_LS_SOC_I2C, 97 (BSL_META_U(unit, 98 "iop@0x%x: read 0x%x from addr 0x%x\n"), saddr, *data, addr)); 99 soc_i2c_device(unit, devno)->rbyte++; 100 return rv; 101 } 102 103 104 105 STATIC int 106 pca9505_write(int unit, int devno, uint16 addr, uint8 *data, uint32 len) 107 { 108 int rv; 109 uint8 saddr; 110 111 saddr = soc_i2c_addr(unit, devno); 112 113 rv = soc_i2c_write_byte_data(unit, saddr, addr, *data); 114 LOG_INFO(BSL_LS_SOC_I2C, 115 (BSL_META_U(unit, 116 "iop@0x%x: wrote 0x%x to addr 0x%x\n"), saddr, *data, addr)); 117 soc_i2c_device(unit, devno)->tbyte++; 118 return rv; 119 } 120 121 122 123 STATIC int 124 pca9505_ioctl(int unit, int devno, int opcode, void* data, int len) 125 { 126 return SOC_E_NONE; 127 } 128 129 STATIC int 130 pca9505_saddr_to_devid(int unit, uint8 saddr) { 131 132 int dev_idx = -1; 133 134 #ifdef SHADOW_SVK 135 if (saddr == I2C_IOP_SADDR0) { 136 dev_idx = 0; 137 } else if (saddr == I2C_IOP_SADDR1) { 138 dev_idx = 1; 139 } else { 140 dev_idx = -1; 141 } 142 143 if ((saddr == I2C_IOP_SADDR0) && 144 (soc_property_get(unit, "cfp_enable", 0))) { 145 dev_idx = 2; 146 } 147 #endif 148 return dev_idx; 149 150 } 151 152 STATIC int 153 pca9505_init(int unit, int devno, void* data, int len) 154 { 155 int port; 156 int dev_idx; 157 int rv; 158 uint8 value = 0; 159 uint8 saddr; 160 161 saddr = soc_i2c_addr(unit, devno); 162 163 soc_i2c_devdesc_set(unit, devno, "PCA9505 IO Port"); 164 165 dev_idx = pca9505_saddr_to_devid(unit, saddr); 166 if (dev_idx < 0) { 167 LOG_CLI((BSL_META_U(unit, 168 "%s: invalid address 0x%x\n"), 169 soc_i2c_devname(unit, devno), saddr)); 170 return SOC_E_CONFIG; 171 } 172 173 /* Configure port IO pin direction and mask */ 174 for (port = 0; port < PCA9505_PORT_MAX; port++) { 175 176 LOG_INFO(BSL_LS_SOC_I2C, 177 (BSL_META_U(unit, 178 "iop: Initializing port %d \n"), port)); 179 /* Disable polarity inversion */ 180 rv = pca9505_write(unit, devno, PCA9505_POL_BASE + port, 181 &value, 1); 182 183 /* Configure reasonable value for output */ 184 LOG_INFO(BSL_LS_SOC_I2C, 185 (BSL_META_U(unit, 186 "iop: Setting known output on %d : 0x%x \n"), 187 port, pca9505_port_config[dev_idx][port].defaults)); 188 if (pca9505_port_config[dev_idx][port].op_config) { 189 /* Support for toggling any resets */ 190 uint8 idata = 0; 191 idata = pca9505_port_config[dev_idx][port].defaults & 192 ~(pca9505_port_config[dev_idx][port].op_config); 193 idata |= ~(pca9505_port_config[dev_idx][port].defaults & 194 pca9505_port_config[dev_idx][port].op_config); 195 rv = pca9505_write(unit, devno, PCA9505_IOP_BASE + port, &idata, 1); 196 } 197 rv = pca9505_write(unit, devno, PCA9505_IOP_BASE + port, 198 &pca9505_port_config[dev_idx][port].defaults, 1); 199 LOG_INFO(BSL_LS_SOC_I2C, 200 (BSL_META_U(unit, 201 "iop: Setting interrupt mask on %d : 0x%x \n"), 202 port, pca9505_port_config[dev_idx][port].mask_intr)); 203 rv = pca9505_write(unit, devno, PCA9505_MSK_BASE + port, 204 &pca9505_port_config[dev_idx][port].mask_intr, 1); 205 if (rv != SOC_E_NONE) { 206 LOG_CLI((BSL_META_U(unit, 207 "Failed initializing PCA9505 interrupt mask " 208 "addr=0x%x, port=%d\n"), saddr, port)); 209 } 210 211 /* Setup IO config only after loading correct values */ 212 rv = pca9505_write(unit, devno, PCA9505_IOC_BASE + port, 213 &pca9505_port_config[dev_idx][port].io_config, 1); 214 if (rv != SOC_E_NONE) { 215 LOG_CLI((BSL_META_U(unit, 216 "Failed initializing PCA9505 pin direction " 217 "addr=0x%x, port=%d\n"), saddr, port)); 218 return SOC_E_FAIL; 219 } 220 } 221 return SOC_E_NONE; 222 } 223 224 void 225 pca9505_dump(int dev, int startp, int endp) 226 { 227 uint8 val_0; 228 uint8 val_8; 229 uint8 val_10; 230 uint8 val_18; 231 uint8 val_20; 232 int i; 233 uint8 saddr; 234 235 switch (dev) { 236 case 0: 237 saddr = I2C_IOP_SADDR0; break; 238 case 1: 239 saddr = I2C_IOP_SADDR1; break; 240 case 2: 241 #ifdef SHADOW_SVK 242 saddr = I2C_IOP_SADDR0; break; 243 #endif 244 default: 245 return; 246 } 247 for(i = startp; i < endp; i++) { 248 soc_i2c_read_byte_data(0,saddr, 0x00 + i, &val_0); 249 soc_i2c_read_byte_data(0,saddr, 0x08 + i, &val_8); 250 soc_i2c_read_byte_data(0,saddr, 0x10 + i, &val_10); 251 soc_i2c_read_byte_data(0,saddr, 0x18 + i, &val_18); 252 soc_i2c_read_byte_data(0,saddr, 0x20 + i, &val_20); 253 LOG_CLI((BSL_META("R[0x%02x] = 0x%02x\n"), 0x00 + i, (uint32)val_0)); 254 LOG_CLI((BSL_META("R[0x%02x] = 0x%02x\n"), 0x08 + i, (uint32)val_8)); 255 LOG_CLI((BSL_META("R[0x%02x] = 0x%02x\n"), 0x10 + i, (uint32)val_10)); 256 LOG_CLI((BSL_META("R[0x%02x] = 0x%02x\n"), 0x18 + i, (uint32)val_18)); 257 LOG_CLI((BSL_META("R[0x%02x] = 0x%02x\n"), 0x20 + i, (uint32)val_20)); 258 } 259 } 260 261 262 /* PCA9505 Clock Chip Driver callout */ 263 i2c_driver_t _soc_i2c_pca9505_driver = { 264 0x0, 0x0, /* System assigned bytes */ 265 PCA9505_DEVICE_TYPE, 266 pca9505_read, 267 pca9505_write, 268 pca9505_ioctl, 269 pca9505_init, 270 NULL, 271 }; 272 273 #endif /* SHADOW_SVK */