# HG changeset patch # User Paul Boddie # Date 1697241413 -7200 # Node ID 866e69a975e7be3590c2350ba56c6966c0f5697f # Parent 4490bc9e507e05a6852cb63a137cf1c07dd2280e# Parent 26c2512ab347bf1aea8cf85c926918705652a9cd Introduced the GPIO-based I2C driver to this branch. diff -r 4490bc9e507e -r 866e69a975e7 pkg/devices/lib/i2c/include/i2c-gpio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-gpio.h Sat Oct 14 01:56:53 2023 +0200 @@ -0,0 +1,76 @@ +/* + * Manual GPIO-based I2C communication. + * + * Copyright (C) 2013, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + +/* I2C modifiers. */ + +#define I2C_READ 1 +#define I2C_WRITE 0 + +#ifdef __cplusplus + +class I2c_gpio +{ + Hw::Gpio_chip *_scl_chip; + int I2C_SCL; + Hw::Gpio_chip *_sda_chip; + int I2C_SDA; + + void CLR(int pin); + void SET(int pin); + void IN(int pin); + int PIN(int pin); + void wait(); + +public: + explicit I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda); + + void start(); + void stop(); + void ack(bool ack); + uint8_t recv(); + void recvmany(uint8_t *data, uint8_t len); + bool send(uint8_t data); + bool sendmany(uint8_t *data, uint8_t len); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda); +void i2c_gpio_start(void *channel); +void i2c_gpio_stop(void *channel); +void i2c_gpio_ack(void *channel, bool ack); +uint8_t i2c_gpio_recv(void *channel); +void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len); +bool i2c_gpio_send(void *channel, uint8_t data); +bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len); + +EXTERN_C_END diff -r 4490bc9e507e -r 866e69a975e7 pkg/devices/lib/i2c/src/Makefile --- a/pkg/devices/lib/i2c/src/Makefile Sat Oct 14 01:53:25 2023 +0200 +++ b/pkg/devices/lib/i2c/src/Makefile Sat Oct 14 01:56:53 2023 +0200 @@ -4,10 +4,10 @@ TARGET = libi2c.o.a libi2c.o.so PC_FILENAME := libdrivers-i2c -SRC_CC := jz4730.cc jz4780.cc x1600.cc +SRC_CC := jz4730.cc jz4780.cc x1600.cc gpio.cc PRIVATE_INCDIR += $(PKGDIR)/lib/i2c/include -REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-gpio include $(L4DIR)/mk/lib.mk diff -r 4490bc9e507e -r 866e69a975e7 pkg/devices/lib/i2c/src/gpio.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/gpio.cc Sat Oct 14 01:56:53 2023 +0200 @@ -0,0 +1,243 @@ +/* + * Manual GPIO-based I2C communication. + * + * Copyright (C) 2013, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include "i2c-gpio.h" + +/* Declare the pins for initial I2C communications. */ + +I2c_gpio::I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda) +{ + _scl_chip = scl_chip; + _sda_chip = sda_chip; + I2C_SCL = scl; + I2C_SDA = sda; +} + +void I2c_gpio::CLR(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 0); +} + +void I2c_gpio::SET(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 1); +} + +void I2c_gpio::IN(int pin) +{ + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Input, 0); +} + +int I2c_gpio::PIN(int pin) +{ + return (pin == I2C_SCL ? _scl_chip : _sda_chip)->get(pin); +} + +void I2c_gpio::wait() +{ + usleep(4); +} + +/* Initiate an I2C transaction. */ + +void I2c_gpio::start() +{ + /* Set up the data signal. */ + + CLR(I2C_SCL); + wait(); + SET(I2C_SDA); + + /* During a clock pulse, produce the start condition. */ + + SET(I2C_SCL); + wait(); + CLR(I2C_SDA); +} + +/* Terminate an I2C transaction. */ + +void I2c_gpio::stop() +{ + /* Set up the data signal. */ + + CLR(I2C_SCL); + wait(); + CLR(I2C_SDA); + + /* During a clock pulse, produce the stop condition. */ + + SET(I2C_SCL); + wait(); + SET(I2C_SDA); +} + +/* Send an I2C acknowledgement to a transmitting device. */ + +void I2c_gpio::ack(bool ack) +{ + if (ack) + CLR(I2C_SDA); + else + SET(I2C_SDA); + + SET(I2C_SCL); + wait(); + CLR(I2C_SCL); + + IN(I2C_SDA); +} + +/* Receive a single byte from an I2C device as part of a transaction. */ + +uint8_t I2c_gpio::recv() +{ + uint8_t mask, result = 0; + + IN(I2C_SDA); + CLR(I2C_SCL); + + for (mask = 0x80; mask; mask >>= 1) + { + SET(I2C_SCL); + wait(); + + if (PIN(I2C_SDA)) + result |= mask; + + CLR(I2C_SCL); + wait(); + } + + return result; +} + +/* Receive into a buffer a transmission of the given length in bytes. */ + +void I2c_gpio::recvmany(uint8_t *data, uint8_t len) +{ + uint8_t *end = data + len; + + for (; data != end; data++, len--) + { + *data = recv(); + ack(len > 1); + } +} + +/* Send a single byte of data to an I2C device as part of a transaction, + returning whether the transmission succeeded. */ + +bool I2c_gpio::send(uint8_t data) +{ + uint8_t mask; + bool status; + + CLR(I2C_SCL); + + for (mask = 0x80; mask; mask >>= 1) + { + wait(); + + if (data & mask) + SET(I2C_SDA); + else + CLR(I2C_SDA); + + SET(I2C_SCL); + wait(); + CLR(I2C_SCL); + } + + /* Wait for acknowledgement, failing if none is given. */ + + IN(I2C_SDA); + SET(I2C_SCL); + wait(); + + status = PIN(I2C_SDA); + CLR(I2C_SCL); + return !status; +} + +/* Send from the buffer provided a transmission with the given length to an I2C + device. */ + +bool I2c_gpio::sendmany(uint8_t *data, uint8_t len) +{ + uint8_t *end = data + len; + + for (; data != end; data++) + { + if (!send(*data)) + return false; + + /* NOTE: Should test for the slave holding the clock signal low. */ + } + + return true; +} + + + +// C language interface functions. + +void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda) +{ + return (void *) new I2c_gpio(reinterpret_cast(scl_chip), scl, + reinterpret_cast(sda_chip), sda); +} + +void i2c_gpio_start(void *channel) +{ + static_cast(channel)->start(); +} + +void i2c_gpio_stop(void *channel) +{ + static_cast(channel)->stop(); +} + +void i2c_gpio_ack(void *channel, bool ack) +{ + static_cast(channel)->ack(ack); +} + +uint8_t i2c_gpio_recv(void *channel) +{ + return static_cast(channel)->recv(); +} + +void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len) +{ + static_cast(channel)->recvmany(data, len); +} + +bool i2c_gpio_send(void *channel, uint8_t data) +{ + return static_cast(channel)->send(data); +} + +bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len) +{ + return static_cast(channel)->sendmany(data, len); +}