bus.cc revision 12772
110800SN/A/* 210800SN/A * Copyright (c) 2012 ARM Limited 310800SN/A * All rights reserved 410800SN/A * 510800SN/A * The license below extends only to copyright in the software and shall 610800SN/A * not be construed as granting a license to any other intellectual 710800SN/A * property including but not limited to intellectual property relating 810800SN/A * to a hardware implementation of the functionality of the software 910800SN/A * licensed hereunder. You may use the software subject to the license 1010800SN/A * terms below provided that you ensure that this notice is replicated 1110800SN/A * unmodified and in its entirety in all distributions of the software, 1210800SN/A * modified or unmodified, in source code or in binary form. 1310800SN/A * 1410800SN/A * Redistribution and use in source and binary forms, with or without 1510800SN/A * modification, are permitted provided that the following conditions are 1610800SN/A * met: redistributions of source code must retain the above copyright 1710800SN/A * notice, this list of conditions and the following disclaimer; 1810800SN/A * redistributions in binary form must reproduce the above copyright 1910800SN/A * notice, this list of conditions and the following disclaimer in the 2010800SN/A * documentation and/or other materials provided with the distribution; 2110800SN/A * neither the name of the copyright holders nor the names of its 2210800SN/A * contributors may be used to endorse or promote products derived from 2310800SN/A * this software without specific prior written permission. 2410800SN/A * 2510800SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2610800SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2710800SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2810800SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2910800SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3010800SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3110800SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3210800SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3310800SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3410800SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3510800SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3610800SN/A * 3710800SN/A * Authors: Peter Enns 3810800SN/A */ 3910800SN/A 4011262Sandreas.sandberg@arm.com#include "dev/i2c/bus.hh" 4110800SN/A 4210800SN/A#include "debug/Checkpoint.hh" 4311262Sandreas.sandberg@arm.com#include "dev/i2c/device.hh" 4410800SN/A#include "mem/packet_access.hh" 4510800SN/A 4610800SN/A// clang complains about std::set being overloaded with Packet::set if 4710800SN/A// we open up the entire namespace std 4810800SN/Ausing std::vector; 4910800SN/Ausing std::map; 5010800SN/A 5110800SN/A/** 5210800SN/A * 4KB - see e.g. 5310800SN/A * http://infocenter.arm.com/help/topic/com.arm.doc.dui0440b/Bbajihec.html 5410800SN/A */ 5510800SN/AI2CBus::I2CBus(const I2CBusParams *p) 5612772Snikos.nikoleris@arm.com : BasicPioDevice(p, 0x1000), scl(1), sda(1), state(IDLE), currBit(7), 5710800SN/A i2cAddr(0x00), message(0x00) 5810800SN/A{ 5910800SN/A vector<I2CDevice*> devs = p->devices; 6010800SN/A 6110800SN/A for (auto d : p->devices) { 6210800SN/A devices[d->i2cAddr()] = d; 6310800SN/A } 6410800SN/A} 6510800SN/A 6610800SN/A/** 6710800SN/A * Reads will always be to SB_CONTROLS. The kernel wants to know the state 6810800SN/A * of sda and scl. 6910800SN/A */ 7010800SN/ATick 7110800SN/AI2CBus::read(PacketPtr pkt) 7210800SN/A{ 7310800SN/A assert(pkt->getAddr() == pioAddr + SB_CONTROLS); 7410800SN/A 7510800SN/A pkt->set<uint8_t>((sda << 1) | scl); 7610800SN/A pkt->makeAtomicResponse(); 7710800SN/A return pioDelay; 7810800SN/A} 7910800SN/A 8010800SN/A/** 8110800SN/A * The default i2c bus driver used by the realview pbx board writes to 8210800SN/A * this device one bit at a time. To facilitate making new i2c devices, 8310800SN/A * i2cBus::write takes care of the low-level details of the i2c protocol. 8410800SN/A * See the I2C Specification [1] for a detailed description of the 8510800SN/A * protocol. 8610800SN/A * 8710800SN/A * [1] - http://www.nxp.com/documents/user_manual/UM10204.pdf 8810800SN/A */ 8910800SN/ATick 9010800SN/AI2CBus::write(PacketPtr pkt) 9110800SN/A{ 9210800SN/A assert(pkt->getAddr() == pioAddr + SB_CONTROLS || 9310800SN/A pkt->getAddr() == pioAddr + SB_CONTROLC); 9410800SN/A 9510800SN/A updateSignals(pkt); 9610800SN/A 9710800SN/A // Check if the bus master is starting a new transmission. 9810800SN/A if (isStart(pkt)) { 9910800SN/A state = RECEIVING_ADDR; 10010800SN/A message = 0x00; 10110800SN/A currBit = 7; 10210800SN/A /* Most i2c devices expect something special (e.g., command, 10310800SN/A * register address) in the first byte they receive so they 10410800SN/A * must be notified somehow that this is a new transmission. 10510800SN/A */ 10610800SN/A for (auto& d : devices) { 10710800SN/A d.second->i2cStart(); 10810800SN/A } 10910800SN/A return pioDelay; 11010800SN/A } 11110800SN/A 11210800SN/A // Check if the bus master is ending a transmission. 11310800SN/A if (isEnd(pkt)) { 11410800SN/A state = IDLE; 11510800SN/A return pioDelay; 11610800SN/A } 11710800SN/A 11810800SN/A // Only change state when the clock is transitioning from low to high. 11910800SN/A // This may not perfectly mimic physical i2c devices but the important 12010800SN/A // part is to only do the following once per clock cycle. 12110800SN/A if (isClockSet(pkt)) { 12210800SN/A switch (state) { 12310800SN/A case RECEIVING_ADDR: 12410800SN/A if (currBit >= 0) { 12510800SN/A message |= sda << currBit; 12610800SN/A currBit--; 12710800SN/A } else { 12810800SN/A i2cAddr = message >> 1; 12910800SN/A assert(devices.find(i2cAddr) != devices.end()); 13010800SN/A if (message & 0x01) { 13110800SN/A state = SENDING_DATA; 13210800SN/A message = devices[i2cAddr]->read(); 13310800SN/A } else { 13410800SN/A state = RECEIVING_DATA; 13510800SN/A message = 0x00; 13610800SN/A } 13710800SN/A currBit = 7; 13810800SN/A sda = 0; /* Ack */ 13910800SN/A } 14010800SN/A break; 14110800SN/A case RECEIVING_DATA: 14210800SN/A if (currBit >= 0) { 14310800SN/A message |= sda << currBit; 14410800SN/A currBit--; 14510800SN/A } else { 14610800SN/A devices[i2cAddr]->write(message); 14710800SN/A message = 0x00; 14810800SN/A currBit = 7; 14910800SN/A sda = 0; /* Ack */ 15010800SN/A } 15110800SN/A break; 15210800SN/A case SENDING_DATA: 15310800SN/A if (currBit >= 0) { 15410800SN/A sda = (message >> currBit) & 0x01; 15510800SN/A currBit--; 15610800SN/A } else { 15710800SN/A if (!sda) /* Check for ack from the bus master. */ 15810800SN/A message = devices[i2cAddr]->read(); 15910800SN/A currBit = 7; 16010800SN/A } 16110800SN/A break; 16210800SN/A case IDLE: 16310800SN/A default: 16410800SN/A panic("Invalid state on posedge of clock in I2CBus::write.\n"); 16510800SN/A break; 16610800SN/A } 16710800SN/A } 16810800SN/A 16910800SN/A return pioDelay; 17010800SN/A} 17110800SN/A 17210800SN/Avoid 17310800SN/AI2CBus::updateSignals(PacketPtr pkt) 17410800SN/A{ 17510800SN/A uint8_t msg = pkt->get<uint8_t>(); 17610800SN/A Addr daddr = pkt->getAddr() - pioAddr; 17710800SN/A 17810800SN/A switch (daddr) { 17910800SN/A case SB_CONTROLS: 18010800SN/A scl = (msg & 1) ? 1 : scl; 18110800SN/A sda = (msg & 2) ? 1 : sda; 18210800SN/A break; 18310800SN/A case SB_CONTROLC: 18410800SN/A scl = (msg & 1) ? 0 : scl; 18510800SN/A sda = (msg & 2) ? 0 : sda; 18610800SN/A break; 18710800SN/A default: 18810800SN/A break; 18910800SN/A } 19010800SN/A} 19110800SN/A 19210800SN/Abool 19310800SN/AI2CBus::isClockSet(PacketPtr pkt) const 19410800SN/A{ 19510800SN/A uint8_t msg = pkt->get<uint8_t>(); 19610800SN/A Addr daddr = pkt->getAddr() - pioAddr; 19710800SN/A return daddr == SB_CONTROLS && (msg & 1); 19810800SN/A} 19910800SN/A 20010800SN/Abool 20110800SN/AI2CBus::isStart(PacketPtr pkt) const 20210800SN/A{ 20310800SN/A uint8_t msg = pkt->get<uint8_t>(); 20410800SN/A Addr daddr = pkt->getAddr() - pioAddr; 20510800SN/A return scl && (msg & 2) && daddr == SB_CONTROLC; 20610800SN/A} 20710800SN/A 20810800SN/Abool 20910800SN/AI2CBus::isEnd(PacketPtr pkt) const 21010800SN/A{ 21110800SN/A uint8_t msg = pkt->get<uint8_t>(); 21210800SN/A Addr daddr = pkt->getAddr() - pioAddr; 21310800SN/A return scl && (msg & 2) && daddr == SB_CONTROLS; 21410800SN/A} 21510800SN/Avoid 21610905SN/AI2CBus::serialize(CheckpointOut &cp) const 21710800SN/A{ 21810800SN/A DPRINTF(Checkpoint, "Serializing I2C bus.\n"); 21910800SN/A SERIALIZE_SCALAR(scl); 22010800SN/A SERIALIZE_SCALAR(sda); 22110800SN/A SERIALIZE_ENUM(state); 22210800SN/A SERIALIZE_SCALAR(currBit); 22310800SN/A SERIALIZE_SCALAR(i2cAddr); 22410800SN/A SERIALIZE_SCALAR(message); 22510800SN/A} 22610800SN/A 22710800SN/Avoid 22810905SN/AI2CBus::unserialize(CheckpointIn &cp) 22910800SN/A{ 23010800SN/A DPRINTF(Checkpoint, "Unserializing I2C bus.\n"); 23110800SN/A UNSERIALIZE_SCALAR(scl); 23210800SN/A UNSERIALIZE_SCALAR(sda); 23310800SN/A UNSERIALIZE_ENUM(state); 23410800SN/A UNSERIALIZE_SCALAR(currBit); 23510800SN/A UNSERIALIZE_SCALAR(i2cAddr); 23610800SN/A UNSERIALIZE_SCALAR(message); 23710800SN/A} 23810800SN/A 23910800SN/AI2CBus* 24010800SN/AI2CBusParams::create() 24110800SN/A{ 24210800SN/A return new I2CBus(this); 24310800SN/A} 244