1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/i2c/src/jz4730.cc Sun Jan 03 17:28:05 2021 +0100
1.3 @@ -0,0 +1,324 @@
1.4 +/*
1.5 + * I2C support for the JZ4730.
1.6 + *
1.7 + * Copyright (C) 2017, 2018, 2020 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 <l4/devices/i2c-jz4730.h>
1.26 +#include <l4/devices/hw_mmio_register_block.h>
1.27 +
1.28 +#include <l4/sys/icu.h>
1.29 +#include <l4/util/util.h>
1.30 +
1.31 +#include <cstdio>
1.32 +
1.33 +/*
1.34 +I2C pins are dedicated to I2C only and are not GPIO-controlled:
1.35 +
1.36 +I2C0: Y4/SMB0_SDA, V5/SMB0_SCK
1.37 +
1.38 +Note that there is effectively only one I2C channel.
1.39 +*/
1.40 +
1.41 +enum Regs
1.42 +{
1.43 + I2c_data = 0x000, // I2CDR
1.44 + I2c_control = 0x004, // I2CCR
1.45 + I2c_status = 0x008, // I2CSR
1.46 + I2c_clock = 0x00c, // I2CGR
1.47 +};
1.48 +
1.49 +enum I2c_control_bits : unsigned
1.50 +{
1.51 + I2c_control_enable_irq = 0x10, // IEN
1.52 + I2c_control_start = 0x08, // STA
1.53 + I2c_control_stop = 0x04, // STO
1.54 + I2c_control_nack = 0x02, // AC
1.55 + I2c_control_enable = 0x01, // I2CE
1.56 +};
1.57 +
1.58 +enum I2c_status_bits : unsigned
1.59 +{
1.60 + I2c_status_buffer_nempty = 0x10, // STX
1.61 + I2c_status_busy = 0x08, // BUSY
1.62 + I2c_status_transmit_end = 0x04, // TEND
1.63 + I2c_status_data_valid = 0x02, // DRF
1.64 + I2c_status_nack = 0x01, // ACKF
1.65 +};
1.66 +
1.67 +enum I2c_clock_values : unsigned
1.68 +{
1.69 + I2c_clock_max = 0xffff,
1.70 + I2c_clock_min = 0,
1.71 +};
1.72 +
1.73 +
1.74 +
1.75 +// Initialise a channel.
1.76 +
1.77 +I2c_jz4730_channel::I2c_jz4730_channel(l4_addr_t start,
1.78 + Cpm_jz4730_chip *cpm,
1.79 + uint32_t frequency)
1.80 +: _cpm(cpm), _frequency(frequency)
1.81 +{
1.82 + _regs = new Hw::Mmio_register_block<32>(start);
1.83 +}
1.84 +
1.85 +// Enable the channel.
1.86 +
1.87 +void
1.88 +I2c_jz4730_channel::enable()
1.89 +{
1.90 + // Make sure that the I2C clock is available.
1.91 +
1.92 + _cpm->start_i2c();
1.93 +
1.94 + // Set the bus clock frequency.
1.95 +
1.96 + set_frequency();
1.97 +
1.98 + // Enable the channel and interrupts.
1.99 +
1.100 + _regs[I2c_control] = I2c_control_enable | I2c_control_enable_irq;
1.101 + while (!(_regs[I2c_control] & I2c_control_enable));
1.102 +}
1.103 +
1.104 +// Disable the channel.
1.105 +
1.106 +void
1.107 +I2c_jz4730_channel::disable()
1.108 +{
1.109 + _regs[I2c_control] = 0;
1.110 + while (_regs[I2c_control] & I2c_control_enable);
1.111 +}
1.112 +
1.113 +// Set the frequency-related peripheral parameters.
1.114 +
1.115 +void
1.116 +I2c_jz4730_channel::set_frequency()
1.117 +{
1.118 + // The APB clock (PCLK) is used to drive I2C transfers. Its value must be
1.119 + // obtained from the CPM unit and is scaled to kHz in order to keep the
1.120 + // numbers easily representable, as is the bus frequency.
1.121 +
1.122 + uint32_t pclk = _cpm->get_pclock_frequency() / 1000;
1.123 + uint32_t i2c_clk = _frequency / 1000;
1.124 + uint32_t division = pclk / (16 * i2c_clk);
1.125 +
1.126 + if (division > I2c_clock_min)
1.127 + {
1.128 + division -= 1;
1.129 + if (division > I2c_clock_max)
1.130 + division = I2c_clock_max;
1.131 + }
1.132 +
1.133 + _regs[I2c_clock] = division;
1.134 +}
1.135 +
1.136 +// Present the address on the bus.
1.137 +
1.138 +void
1.139 +I2c_jz4730_channel::set_address(uint8_t address, bool read)
1.140 +{
1.141 + start();
1.142 +
1.143 + while (nack());
1.144 +
1.145 + _regs[I2c_data] = (address << 1) | (read ? 1 : 0);
1.146 + send_next();
1.147 +
1.148 + if (read)
1.149 + while ((data_valid() || !transferred()) && !nack());
1.150 + else
1.151 + while (data_valid() && !nack());
1.152 +}
1.153 +
1.154 +// Read data from the bus.
1.155 +
1.156 +unsigned int
1.157 +I2c_jz4730_channel::read(uint8_t address, uint8_t buf[], unsigned int length)
1.158 +{
1.159 + unsigned int nread = 0;
1.160 +
1.161 + set_address(address, true);
1.162 +
1.163 + if (!nack())
1.164 + {
1.165 + if (length == 1)
1.166 + signal_last();
1.167 +
1.168 + while (nread < length)
1.169 + {
1.170 + while (!data_valid());
1.171 +
1.172 + if (nread == length - 2)
1.173 + signal_last();
1.174 +
1.175 + buf[nread++] = _regs[I2c_data];
1.176 + request_next();
1.177 + }
1.178 + }
1.179 +
1.180 + stop();
1.181 +
1.182 + return nread;
1.183 +}
1.184 +
1.185 +// Write data to the bus.
1.186 +
1.187 +unsigned int
1.188 +I2c_jz4730_channel::write(uint8_t address, uint8_t buf[], unsigned int length)
1.189 +{
1.190 + unsigned int nwritten = 0;
1.191 +
1.192 + set_address(address, false);
1.193 +
1.194 + while ((nwritten < length) && !nack())
1.195 + {
1.196 + _regs[I2c_data] = buf[nwritten++];
1.197 + send_next();
1.198 +
1.199 + while (data_valid() && !nack());
1.200 + }
1.201 +
1.202 + stop();
1.203 +
1.204 + while (!transferred());
1.205 +
1.206 + return nwritten;
1.207 +}
1.208 +
1.209 +// Test for data validity.
1.210 +
1.211 +bool
1.212 +I2c_jz4730_channel::data_valid()
1.213 +{
1.214 + return (_regs[I2c_status] & I2c_status_data_valid) ? true : false;
1.215 +}
1.216 +
1.217 +// Request the next byte by clearing the data validity flag.
1.218 +
1.219 +void
1.220 +I2c_jz4730_channel::request_next()
1.221 +{
1.222 + _regs[I2c_status] = _regs[I2c_status] & ~I2c_status_data_valid;
1.223 +}
1.224 +
1.225 +// Indicate data ready for sending.
1.226 +
1.227 +void
1.228 +I2c_jz4730_channel::send_next()
1.229 +{
1.230 + _regs[I2c_status] = _regs[I2c_status] | I2c_status_data_valid;
1.231 +}
1.232 +
1.233 +// Test for non-acknowledgement.
1.234 +
1.235 +bool
1.236 +I2c_jz4730_channel::nack()
1.237 +{
1.238 + return (_regs[I2c_status] & I2c_status_nack) ? true : false;
1.239 +}
1.240 +
1.241 +// Set non-acknowledgement when receiving data.
1.242 +
1.243 +void
1.244 +I2c_jz4730_channel::signal_last()
1.245 +{
1.246 + _regs[I2c_control] = _regs[I2c_control] | I2c_control_nack;
1.247 +}
1.248 +
1.249 +// Test for write transfer completion.
1.250 +
1.251 +bool
1.252 +I2c_jz4730_channel::transferred()
1.253 +{
1.254 + return (_regs[I2c_status] & I2c_status_transmit_end) ? true : false;
1.255 +}
1.256 +
1.257 +// Explicitly start communication.
1.258 +
1.259 +void
1.260 +I2c_jz4730_channel::start()
1.261 +{
1.262 + _regs[I2c_control] = (_regs[I2c_control] & ~I2c_control_nack) | I2c_control_start;
1.263 +}
1.264 +
1.265 +// Explicitly stop communication.
1.266 +
1.267 +void
1.268 +I2c_jz4730_channel::stop()
1.269 +{
1.270 + _regs[I2c_control] = _regs[I2c_control] | I2c_control_stop;
1.271 +}
1.272 +
1.273 +
1.274 +
1.275 +// Initialise the I2C controller.
1.276 +
1.277 +I2c_jz4730_chip::I2c_jz4730_chip(l4_addr_t start, l4_addr_t end,
1.278 + Cpm_jz4730_chip *cpm,
1.279 + uint32_t frequency)
1.280 +: _start(start), _end(end), _cpm(cpm), _frequency(frequency)
1.281 +{
1.282 +}
1.283 +
1.284 +// Obtain a channel object. Only one channel is supported.
1.285 +
1.286 +I2c_jz4730_channel *
1.287 +I2c_jz4730_chip::get_channel(uint8_t channel)
1.288 +{
1.289 + if (channel == 0)
1.290 + return new I2c_jz4730_channel(_start, _cpm, _frequency);
1.291 + else
1.292 + throw -L4_EINVAL;
1.293 +}
1.294 +
1.295 +
1.296 +
1.297 +// C language interface functions.
1.298 +
1.299 +void *jz4730_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency)
1.300 +{
1.301 + return (void *) new I2c_jz4730_chip(start, end, static_cast<Cpm_jz4730_chip *>(cpm), frequency);
1.302 +}
1.303 +
1.304 +void *jz4730_i2c_get_channel(void *i2c, uint8_t channel)
1.305 +{
1.306 + return static_cast<I2c_jz4730_chip *>(i2c)->get_channel(channel);
1.307 +}
1.308 +
1.309 +void jz4730_i2c_disable(void *i2c_channel)
1.310 +{
1.311 + static_cast<I2c_jz4730_channel *>(i2c_channel)->disable();
1.312 +}
1.313 +
1.314 +void jz4730_i2c_enable(void *i2c_channel)
1.315 +{
1.316 + static_cast<I2c_jz4730_channel *>(i2c_channel)->enable();
1.317 +}
1.318 +
1.319 +unsigned int jz4730_i2c_read(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length)
1.320 +{
1.321 + return static_cast<I2c_jz4730_channel *>(i2c_channel)->read(address, buf, length);
1.322 +}
1.323 +
1.324 +unsigned int jz4730_i2c_write(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length)
1.325 +{
1.326 + return static_cast<I2c_jz4730_channel *>(i2c_channel)->write(address, buf, length);
1.327 +}