pcf8574.c (5969B)
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 PCF8574 Parallel port. 8 * 9 * On P48 systems, the parallel port is typically connected to two 10 * MUX's used to control I2C slave addressing on the common I2C bus. 11 * 12 * 13 * o-------[MUX1]--[MUX2] 14 * | | | 15 * | | | 16 * | | | 17 * [PCF8574] (SxA) (SyB) 18 * | | | 19 * 0=========O=======0================I2C 20 * 21 * 22 * SxA = Slave X with Address Bits A 23 * SxB = Slave Y with Address Bits B 24 */ 25 26 #include <shared/bsl.h> 27 28 #include <sal/types.h> 29 #include <soc/debug.h> 30 #include <soc/drv.h> 31 #include <soc/error.h> 32 #include <soc/i2c.h> 33 #include <shared/bsl.h> 34 35 STATIC int 36 pcf8574_read(int unit, int devno, 37 uint16 addr, uint8* data, uint32* len) 38 { 39 40 int rv; 41 uint8 saddr = soc_i2c_addr(unit, devno); 42 43 rv = soc_i2c_read_byte(unit, saddr, data); 44 *len = 1; /* Byte device */ 45 /* LOG_CLI((BSL_META_U(unit, 46 "lpt0: read 0x%x from HW\n"),*data)); */ 47 soc_i2c_device(unit, devno)->rbyte++; 48 return rv; 49 } 50 51 STATIC int 52 pcf8574_write(int unit, int devno, 53 uint16 addr, uint8* data, uint32 len) 54 { 55 int rv; 56 uint8 saddr = soc_i2c_addr(unit, devno); 57 58 rv = soc_i2c_write_byte(unit, saddr, *data); 59 /* LOG_CLI((BSL_META_U(unit, 60 "lpt0: wrote 0x%x to HW\n"),*data)); */ 61 soc_i2c_device(unit, devno)->tbyte++; 62 return rv; 63 } 64 65 66 67 STATIC int 68 pcf8574_ioctl(int unit, int devno, 69 int opcode, void* data, int len) 70 { 71 return SOC_E_NONE; 72 } 73 74 STATIC int 75 pcf8574_init(int unit, int devno, 76 void* data, int len) 77 { 78 uint32 bytes; 79 uint8 lpt_val = 0; 80 uint8 saddr = soc_i2c_addr(unit, devno); 81 82 if (saddr == I2C_LPT_SADDR0) { 83 pcf8574_read(unit, devno, 0, &lpt_val, &bytes); 84 if (lpt_val == 0xFF) { 85 /* This is probably immediately after power-on; reset to zero. */ 86 lpt_val = 0; 87 pcf8574_write(unit, devno, 0, &lpt_val, 1); 88 } 89 soc_i2c_devdesc_set(unit, devno, "PCF8574 MUX control"); 90 LOG_VERBOSE(BSL_LS_SOC_COMMON, 91 (BSL_META_U(unit, 92 "%s: mux control 0x%x\n"), 93 soc_i2c_devname(unit, devno), lpt_val)); 94 } else if (saddr == I2C_LPT_SADDR1) { 95 /* This device used as input; write 0xFF to disable quasi-outputs */ 96 lpt_val = 0xFF; 97 pcf8574_write(unit, devno, 0, &lpt_val, 1); 98 99 /* Now read what is being driven onto the quasi-inputs */ 100 /* coverity[callee_ptr_arith: FALSE] */ 101 pcf8574_read(unit, devno, 0, &lpt_val, &bytes); 102 103 /* Try to read from the PCA9554 device */ 104 if (lpt_val == 0xFF || lpt_val == 0x0) { 105 lpt_val = 0; /* Write 0x00 to command register first */ 106 pcf8574_write(unit, devno, 0, &lpt_val, 1); 107 pcf8574_read(unit, devno, 0, &lpt_val, &bytes); 108 } 109 110 soc_i2c_devdesc_set(unit, devno, "PCF8574 or PCA9554 Baseboard ID"); 111 LOG_VERBOSE(BSL_LS_SOC_COMMON, 112 (BSL_META_U(unit, 113 "%s: baseboard id 0x%x\n"), 114 soc_i2c_devname(unit, devno), lpt_val)); 115 SOC_CONTROL(unit)->board_type = lpt_val; 116 } else if (saddr == I2C_LPT_SADDR2) { 117 /* 118 * The power-up state is the appropriate default; 119 * no need to manually initialize the HCLK PCF8574. 120 */ 121 soc_i2c_devdesc_set(unit, devno, "PCF8574 HCLK control"); 122 LOG_VERBOSE(BSL_LS_SOC_COMMON, 123 (BSL_META_U(unit, 124 "%s: hclk control 0x%x\n"), 125 soc_i2c_devname(unit, devno), lpt_val)); 126 } else if (saddr == I2C_LPT_SADDR3) { 127 pcf8574_read(unit, devno, 0, &lpt_val, &bytes); 128 if (lpt_val == 0xFF) { 129 /* This is probably immediately after power-on; reset to zero. */ 130 lpt_val = 0x0; 131 pcf8574_write(unit, devno, 0, &lpt_val, 1); 132 } 133 pcf8574_write(unit, devno, 0, &lpt_val, 1); 134 soc_i2c_devdesc_set(unit, devno, "PCF8574 POE control"); 135 LOG_VERBOSE(BSL_LS_SOC_COMMON, 136 (BSL_META_U(unit, 137 "%s: poe control 0x%x\n"), 138 soc_i2c_devname(unit, devno), lpt_val)); 139 } else if (saddr == I2C_LPT_SADDR4) { 140 soc_i2c_devdesc_set(unit, devno, "PCF8574 synthesizer frequency selector M"); 141 LOG_VERBOSE(BSL_LS_SOC_COMMON, 142 (BSL_META_U(unit, 143 "%s: synth freq select B 0x%x\n"), 144 soc_i2c_devname(unit, devno), lpt_val)); 145 } else if (saddr == I2C_LPT_SADDR5) { 146 soc_i2c_devdesc_set(unit, devno, "PCF8574 synthesizer frequency selector N"); 147 LOG_VERBOSE(BSL_LS_SOC_COMMON, 148 (BSL_META_U(unit, 149 "%s: synth freq select A 0x%x\n"), 150 soc_i2c_devname(unit, devno), lpt_val)); 151 } else if (saddr == I2C_LPT_SADDR6) { 152 soc_i2c_devdesc_set(unit, devno, "PCF8574 PPD clock delay"); 153 LOG_VERBOSE(BSL_LS_SOC_COMMON, 154 (BSL_META_U(unit, 155 "%s: clock selector 0x%x\n"), 156 soc_i2c_devname(unit, devno), lpt_val)); 157 } else if (saddr == I2C_LPT_SADDR7) { 158 soc_i2c_devdesc_set(unit, devno, "PCF8574 PPD clock divider"); 159 LOG_VERBOSE(BSL_LS_SOC_COMMON, 160 (BSL_META_U(unit, 161 "%s: clock selector 0x%x\n"), 162 soc_i2c_devname(unit, devno), lpt_val)); 163 } else { 164 soc_i2c_devdesc_set(unit, devno, "PCF8574 Parallel Port"); 165 } 166 return SOC_E_NONE; 167 } 168 169 170 /* PCF8574 Clock Chip Driver callout */ 171 i2c_driver_t _soc_i2c_pcf8574_driver = { 172 0x0, 0x0, /* System assigned bytes */ 173 PCF8574_DEVICE_TYPE, 174 pcf8574_read, 175 pcf8574_write, 176 pcf8574_ioctl, 177 pcf8574_init, 178 NULL, 179 }; 180