1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/i2c/src/gpio.cc Sat Oct 14 01:55:20 2023 +0200
1.3 @@ -0,0 +1,243 @@
1.4 +/*
1.5 + * Manual GPIO-based I2C communication.
1.6 + *
1.7 + * Copyright (C) 2013, 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <unistd.h>
1.26 +#include "i2c-gpio.h"
1.27 +
1.28 +/* Declare the pins for initial I2C communications. */
1.29 +
1.30 +I2c_gpio::I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda)
1.31 +{
1.32 + _scl_chip = scl_chip;
1.33 + _sda_chip = sda_chip;
1.34 + I2C_SCL = scl;
1.35 + I2C_SDA = sda;
1.36 +}
1.37 +
1.38 +void I2c_gpio::CLR(int pin)
1.39 +{
1.40 + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 0);
1.41 +}
1.42 +
1.43 +void I2c_gpio::SET(int pin)
1.44 +{
1.45 + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 1);
1.46 +}
1.47 +
1.48 +void I2c_gpio::IN(int pin)
1.49 +{
1.50 + (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Input, 0);
1.51 +}
1.52 +
1.53 +int I2c_gpio::PIN(int pin)
1.54 +{
1.55 + return (pin == I2C_SCL ? _scl_chip : _sda_chip)->get(pin);
1.56 +}
1.57 +
1.58 +void I2c_gpio::wait()
1.59 +{
1.60 + usleep(4);
1.61 +}
1.62 +
1.63 +/* Initiate an I2C transaction. */
1.64 +
1.65 +void I2c_gpio::start()
1.66 +{
1.67 + /* Set up the data signal. */
1.68 +
1.69 + CLR(I2C_SCL);
1.70 + wait();
1.71 + SET(I2C_SDA);
1.72 +
1.73 + /* During a clock pulse, produce the start condition. */
1.74 +
1.75 + SET(I2C_SCL);
1.76 + wait();
1.77 + CLR(I2C_SDA);
1.78 +}
1.79 +
1.80 +/* Terminate an I2C transaction. */
1.81 +
1.82 +void I2c_gpio::stop()
1.83 +{
1.84 + /* Set up the data signal. */
1.85 +
1.86 + CLR(I2C_SCL);
1.87 + wait();
1.88 + CLR(I2C_SDA);
1.89 +
1.90 + /* During a clock pulse, produce the stop condition. */
1.91 +
1.92 + SET(I2C_SCL);
1.93 + wait();
1.94 + SET(I2C_SDA);
1.95 +}
1.96 +
1.97 +/* Send an I2C acknowledgement to a transmitting device. */
1.98 +
1.99 +void I2c_gpio::ack(bool ack)
1.100 +{
1.101 + if (ack)
1.102 + CLR(I2C_SDA);
1.103 + else
1.104 + SET(I2C_SDA);
1.105 +
1.106 + SET(I2C_SCL);
1.107 + wait();
1.108 + CLR(I2C_SCL);
1.109 +
1.110 + IN(I2C_SDA);
1.111 +}
1.112 +
1.113 +/* Receive a single byte from an I2C device as part of a transaction. */
1.114 +
1.115 +uint8_t I2c_gpio::recv()
1.116 +{
1.117 + uint8_t mask, result = 0;
1.118 +
1.119 + IN(I2C_SDA);
1.120 + CLR(I2C_SCL);
1.121 +
1.122 + for (mask = 0x80; mask; mask >>= 1)
1.123 + {
1.124 + SET(I2C_SCL);
1.125 + wait();
1.126 +
1.127 + if (PIN(I2C_SDA))
1.128 + result |= mask;
1.129 +
1.130 + CLR(I2C_SCL);
1.131 + wait();
1.132 + }
1.133 +
1.134 + return result;
1.135 +}
1.136 +
1.137 +/* Receive into a buffer a transmission of the given length in bytes. */
1.138 +
1.139 +void I2c_gpio::recvmany(uint8_t *data, uint8_t len)
1.140 +{
1.141 + uint8_t *end = data + len;
1.142 +
1.143 + for (; data != end; data++, len--)
1.144 + {
1.145 + *data = recv();
1.146 + ack(len > 1);
1.147 + }
1.148 +}
1.149 +
1.150 +/* Send a single byte of data to an I2C device as part of a transaction,
1.151 + returning whether the transmission succeeded. */
1.152 +
1.153 +bool I2c_gpio::send(uint8_t data)
1.154 +{
1.155 + uint8_t mask;
1.156 + bool status;
1.157 +
1.158 + CLR(I2C_SCL);
1.159 +
1.160 + for (mask = 0x80; mask; mask >>= 1)
1.161 + {
1.162 + wait();
1.163 +
1.164 + if (data & mask)
1.165 + SET(I2C_SDA);
1.166 + else
1.167 + CLR(I2C_SDA);
1.168 +
1.169 + SET(I2C_SCL);
1.170 + wait();
1.171 + CLR(I2C_SCL);
1.172 + }
1.173 +
1.174 + /* Wait for acknowledgement, failing if none is given. */
1.175 +
1.176 + IN(I2C_SDA);
1.177 + SET(I2C_SCL);
1.178 + wait();
1.179 +
1.180 + status = PIN(I2C_SDA);
1.181 + CLR(I2C_SCL);
1.182 + return !status;
1.183 +}
1.184 +
1.185 +/* Send from the buffer provided a transmission with the given length to an I2C
1.186 + device. */
1.187 +
1.188 +bool I2c_gpio::sendmany(uint8_t *data, uint8_t len)
1.189 +{
1.190 + uint8_t *end = data + len;
1.191 +
1.192 + for (; data != end; data++)
1.193 + {
1.194 + if (!send(*data))
1.195 + return false;
1.196 +
1.197 + /* NOTE: Should test for the slave holding the clock signal low. */
1.198 + }
1.199 +
1.200 + return true;
1.201 +}
1.202 +
1.203 +
1.204 +
1.205 +// C language interface functions.
1.206 +
1.207 +void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda)
1.208 +{
1.209 + return (void *) new I2c_gpio(reinterpret_cast<Hw::Gpio_chip *>(scl_chip), scl,
1.210 + reinterpret_cast<Hw::Gpio_chip *>(sda_chip), sda);
1.211 +}
1.212 +
1.213 +void i2c_gpio_start(void *channel)
1.214 +{
1.215 + static_cast<I2c_gpio *>(channel)->start();
1.216 +}
1.217 +
1.218 +void i2c_gpio_stop(void *channel)
1.219 +{
1.220 + static_cast<I2c_gpio *>(channel)->stop();
1.221 +}
1.222 +
1.223 +void i2c_gpio_ack(void *channel, bool ack)
1.224 +{
1.225 + static_cast<I2c_gpio *>(channel)->ack(ack);
1.226 +}
1.227 +
1.228 +uint8_t i2c_gpio_recv(void *channel)
1.229 +{
1.230 + return static_cast<I2c_gpio *>(channel)->recv();
1.231 +}
1.232 +
1.233 +void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len)
1.234 +{
1.235 + static_cast<I2c_gpio *>(channel)->recvmany(data, len);
1.236 +}
1.237 +
1.238 +bool i2c_gpio_send(void *channel, uint8_t data)
1.239 +{
1.240 + return static_cast<I2c_gpio *>(channel)->send(data);
1.241 +}
1.242 +
1.243 +bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len)
1.244 +{
1.245 + return static_cast<I2c_gpio *>(channel)->sendmany(data, len);
1.246 +}