1/* 2 * Copyright (c) 2012 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Peter Enns 38 */ 39 40#include "dev/i2c/bus.hh" 41 42#include "debug/Checkpoint.hh" 43#include "dev/i2c/device.hh" 44#include "mem/packet_access.hh" 45 46// clang complains about std::set being overloaded with Packet::set if 47// we open up the entire namespace std 48using std::vector; 49using std::map; 50 51/** 52 * 4KB - see e.g. 53 * http://infocenter.arm.com/help/topic/com.arm.doc.dui0440b/Bbajihec.html 54 */ 55I2CBus::I2CBus(const I2CBusParams *p) 56 : BasicPioDevice(p, 0x1000), scl(1), sda(1), state(IDLE), currBit(7), 57 i2cAddr(0x00), message(0x00) 58{ 59 vector<I2CDevice*> devs = p->devices; 60 61 for (auto d : p->devices) { 62 devices[d->i2cAddr()] = d; 63 } 64} 65 66/** 67 * Reads will always be to SB_CONTROLS. The kernel wants to know the state 68 * of sda and scl. 69 */ 70Tick 71I2CBus::read(PacketPtr pkt) 72{ 73 assert(pkt->getAddr() == pioAddr + SB_CONTROLS); 74 75 pkt->setRaw<uint8_t>((sda << 1) | scl); 76 pkt->makeAtomicResponse(); 77 return pioDelay; 78} 79 80/** 81 * The default i2c bus driver used by the realview pbx board writes to 82 * this device one bit at a time. To facilitate making new i2c devices, 83 * i2cBus::write takes care of the low-level details of the i2c protocol. 84 * See the I2C Specification [1] for a detailed description of the 85 * protocol. 86 * 87 * [1] - http://www.nxp.com/documents/user_manual/UM10204.pdf 88 */ 89Tick 90I2CBus::write(PacketPtr pkt) 91{ 92 assert(pkt->getAddr() == pioAddr + SB_CONTROLS || 93 pkt->getAddr() == pioAddr + SB_CONTROLC); 94 95 updateSignals(pkt); 96 97 // Check if the bus master is starting a new transmission. 98 if (isStart(pkt)) { 99 state = RECEIVING_ADDR; 100 message = 0x00; 101 currBit = 7; 102 /* Most i2c devices expect something special (e.g., command, 103 * register address) in the first byte they receive so they 104 * must be notified somehow that this is a new transmission. 105 */ 106 for (auto& d : devices) { 107 d.second->i2cStart(); 108 } 109 return pioDelay; 110 } 111 112 // Check if the bus master is ending a transmission. 113 if (isEnd(pkt)) { 114 state = IDLE; 115 return pioDelay; 116 } 117 118 // Only change state when the clock is transitioning from low to high. 119 // This may not perfectly mimic physical i2c devices but the important 120 // part is to only do the following once per clock cycle. 121 if (isClockSet(pkt)) { 122 switch (state) { 123 case RECEIVING_ADDR: 124 if (currBit >= 0) { 125 message |= sda << currBit; 126 currBit--; 127 } else { 128 i2cAddr = message >> 1; 129 assert(devices.find(i2cAddr) != devices.end()); 130 if (message & 0x01) { 131 state = SENDING_DATA; 132 message = devices[i2cAddr]->read(); 133 } else { 134 state = RECEIVING_DATA; 135 message = 0x00; 136 } 137 currBit = 7; 138 sda = 0; /* Ack */ 139 } 140 break; 141 case RECEIVING_DATA: 142 if (currBit >= 0) { 143 message |= sda << currBit; 144 currBit--; 145 } else { 146 devices[i2cAddr]->write(message); 147 message = 0x00; 148 currBit = 7; 149 sda = 0; /* Ack */ 150 } 151 break; 152 case SENDING_DATA: 153 if (currBit >= 0) { 154 sda = (message >> currBit) & 0x01; 155 currBit--; 156 } else { 157 if (!sda) /* Check for ack from the bus master. */ 158 message = devices[i2cAddr]->read(); 159 currBit = 7; 160 } 161 break; 162 case IDLE: 163 default: 164 panic("Invalid state on posedge of clock in I2CBus::write.\n"); 165 break; 166 } 167 } 168 169 return pioDelay; 170} 171 172void 173I2CBus::updateSignals(PacketPtr pkt) 174{ 175 uint8_t msg = pkt->getRaw<uint8_t>(); 176 Addr daddr = pkt->getAddr() - pioAddr; 177 178 switch (daddr) { 179 case SB_CONTROLS: 180 scl = (msg & 1) ? 1 : scl; 181 sda = (msg & 2) ? 1 : sda; 182 break; 183 case SB_CONTROLC: 184 scl = (msg & 1) ? 0 : scl; 185 sda = (msg & 2) ? 0 : sda; 186 break; 187 default: 188 break; 189 } 190} 191 192bool 193I2CBus::isClockSet(PacketPtr pkt) const 194{ 195 uint8_t msg = pkt->getRaw<uint8_t>(); 196 Addr daddr = pkt->getAddr() - pioAddr; 197 return daddr == SB_CONTROLS && (msg & 1); 198} 199 200bool 201I2CBus::isStart(PacketPtr pkt) const 202{ 203 uint8_t msg = pkt->getRaw<uint8_t>(); 204 Addr daddr = pkt->getAddr() - pioAddr; 205 return scl && (msg & 2) && daddr == SB_CONTROLC; 206} 207 208bool 209I2CBus::isEnd(PacketPtr pkt) const 210{ 211 uint8_t msg = pkt->getRaw<uint8_t>(); 212 Addr daddr = pkt->getAddr() - pioAddr; 213 return scl && (msg & 2) && daddr == SB_CONTROLS; 214} 215void 216I2CBus::serialize(CheckpointOut &cp) const 217{ 218 DPRINTF(Checkpoint, "Serializing I2C bus.\n"); 219 SERIALIZE_SCALAR(scl); 220 SERIALIZE_SCALAR(sda); 221 SERIALIZE_ENUM(state); 222 SERIALIZE_SCALAR(currBit); 223 SERIALIZE_SCALAR(i2cAddr); 224 SERIALIZE_SCALAR(message); 225} 226 227void 228I2CBus::unserialize(CheckpointIn &cp) 229{ 230 DPRINTF(Checkpoint, "Unserializing I2C bus.\n"); 231 UNSERIALIZE_SCALAR(scl); 232 UNSERIALIZE_SCALAR(sda); 233 UNSERIALIZE_ENUM(state); 234 UNSERIALIZE_SCALAR(currBit); 235 UNSERIALIZE_SCALAR(i2cAddr); 236 UNSERIALIZE_SCALAR(message); 237} 238 239I2CBus* 240I2CBusParams::create() 241{ 242 return new I2CBus(this); 243} 244