diff --git a/include/util/i2c.h b/include/util/i2c.h new file mode 100644 index 0000000..a25c3bd --- /dev/null +++ b/include/util/i2c.h @@ -0,0 +1,147 @@ +/* + * \brief Type-safe, fine-grained access to I2C devices + * \author Johannes Schlatow + * \date 2016-12-09 + */ + +/* + * Copyright (C) 2011-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INCLUDE__UTIL__I2C_H_ +#define _INCLUDE__UTIL__I2C_H_ + +/* Genode includes */ +#include + +namespace Genode { + + class I2c_driver_base; + template class I2c_plain_access; + template class I2c; +} + +/** + * Base class for I2C drivers + */ +class Genode::I2c_driver_base +{ + public: + int write_bytes(uint8_t slaveAddr, uint8_t *msgPtr, int byteCount, bool hold=false); + int read_byte(uint8_t slaveAddr, uint8_t *msgPtr); +}; + +/** + * Plain access implementation for I2C + */ +template +class Genode::I2c_plain_access +{ + friend Register_set_plain_access; + + private: + + addr_t const _slave; + DRIVER &_driver; + + /** + * Write 'ACCESS_T' typed 'value' to I2C slave register at 'reg' + */ + template + inline void _write(off_t const reg, ACCESS_T const value) + { + Genode::uint8_t buf[REG_ADDR_SIZE + sizeof(ACCESS_T)]; + + /* write 'reg' into byte buffer using little endian */ + for (size_t i=0; i < REG_ADDR_SIZE; i++) { + buf[i] = reg >> i*8; + } + /* write 'value' into byte buffer using little endian */ + for (size_t i=0; i < sizeof(ACCESS_T); i++) { + buf[i+REG_ADDR_SIZE] = value >> i*8; + } + + /* FIXME how to deal with errors? */ + _driver.write_bytes(_slave, buf, REG_ADDR_SIZE+sizeof(ACCESS_T)) ; + } + + /** + * Read 'ACCESS_T' typed from I2C slave register at 'reg' + */ + template + inline ACCESS_T _read(off_t const ®) const + { + Genode::uint8_t buf[REG_ADDR_SIZE]; + /* write 'reg' into byte buffer using big endian */ + for (size_t i=0; i < REG_ADDR_SIZE; i++) { + buf[i] = reg >> i*8; + } + + /* FIXME how to deal with errors? */ + _driver.write_bytes(_slave, buf, REG_ADDR_SIZE, true); + + /* read data into buffer */ + Genode::uint8_t read_buf[sizeof(ACCESS_T)]; + _driver.read_bytes(_slave, read_buf, sizeof(ACCESS_T)); + + /* convert buffer into ACCESS_T */ + ACCESS_T value = 0; + for (size_t i=0; i < sizeof(ACCESS_T); i++) { + value |= read_buf[i] << i*8; + } + return value; + } + + /** + * Read byte I2C slave register at 'reg' + */ + inline uint8_t _read(off_t const ®) const + { + Genode::uint8_t buf[REG_ADDR_SIZE]; + /* write 'reg' into byte buffer using big endian */ + for (size_t i=0; i < REG_ADDR_SIZE; i++) { + buf[i] = reg >> i*8; + } + + /* FIXME how to deal with errors? */ + _driver.write_bytes(_slave, buf, REG_ADDR_SIZE, true); + + uint8_t value; + _driver.read_bytes(_slave, &value, 1); + return value; + } + + public: + + /** + * Constructor + * + * \param slave address of targeted I2C slave + */ + I2c_plain_access(addr_t const slave, DRIVER &driver) : _slave(slave), _driver(driver) { } +}; + + +/** + * Type-safe, fine-grained access to a I2C devices + * + * For further details refer to the documentation of the 'Register_set' class. + */ +template +struct Genode::I2c : I2c_plain_access, Register_set> +{ + /** + * Constructor + * + * \param slave slave address of targeted I2C device + */ + I2c(addr_t const slave, DRIVER &driver) + : + I2c_plain_access(slave, driver), + Register_set>(*static_cast *>(this)) { } +}; + +#endif /* _INCLUDE__UTIL__I2C_H_ */