# HG changeset patch # User Paul Boddie # Date 1695756470 -7200 # Node ID fbd6a7385dc2756cabccb64ab27e73962ef1b6cd # Parent f315dd9dbb9637cc629ebe87cdf3ae01e218db77 Added tentative X1600 I2C support which will undoubtedly need more work. diff -r f315dd9dbb96 -r fbd6a7385dc2 pkg/devices/lib/i2c/include/i2c-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-x1600.h Tue Sep 26 21:27:50 2023 +0200 @@ -0,0 +1,161 @@ +/* + * I2C support for the X1600. + * + * Copyright (C) 2017, 2018, 2019, 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 + + + +#ifdef __cplusplus + +#include +#include + +// I2C channel. + +class I2c_x1600_channel +{ +private: + Hw::Register_block<32> _regs; + Cpm_x1600_chip *_cpm; + uint32_t _frequency; + + // Buffer management. + + unsigned int _pos, _reqpos, _total; + uint8_t *_buf; + + // Status conditions. + + int _fail; + int _stop; + +public: + I2c_x1600_channel(l4_addr_t start, Cpm_x1600_chip *cpm, + uint32_t frequency); + + uint32_t get_frequency(); + void set_target(uint8_t addr); + + // Reading initiation and execution. + + void start_read(uint8_t buf[], unsigned int total, int stop = 0); + void read(); + + // Writing initiation and execution. + + void start_write(uint8_t buf[], unsigned int total, int stop = 0); + void write(); + + // Transaction control. + + void stop(); + void disable(); + + // Specific status conditions. + + unsigned int have_read(); + unsigned int have_written(); + int read_done(); + int write_done(); + + int failed(); + int read_failed(); + int write_failed(); + +private: + void enable(); + + int active(); + int have_input(); + int have_output(); + int can_send(); + + void reset_flags(); + void init_parameters(); + void set_frequency(); + + void set_read_threshold(); + void queue_reads(); + void queue_writes(); + void store_reads(); +}; + +// I2C device control. + +class I2c_x1600_chip +{ +private: + l4_addr_t _start, _end; + Cpm_x1600_chip *_cpm; + uint32_t _frequency; + +public: + I2c_x1600_chip(l4_addr_t start, l4_addr_t end, Cpm_x1600_chip *cpm, + uint32_t frequency); + + I2c_x1600_channel *get_channel(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, + uint32_t frequency); + +void x1600_i2c_disable(void *i2c_channel); + +void *x1600_i2c_get_channel(void *i2c, uint8_t channel); + +uint32_t x1600_i2c_get_frequency(void *i2c_channel); + +void x1600_i2c_set_target(void *i2c_channel, uint8_t addr); + +void x1600_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); + +void x1600_i2c_read(void *i2c_channel); + +void x1600_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); + +void x1600_i2c_write(void *i2c_channel); + +int x1600_i2c_read_done(void *i2c_channel); + +int x1600_i2c_write_done(void *i2c_channel); + +unsigned int x1600_i2c_have_read(void *i2c_channel); + +unsigned int x1600_i2c_have_written(void *i2c_channel); + +int x1600_i2c_failed(void *i2c_channel); + +void x1600_i2c_stop(void *i2c_channel); + +EXTERN_C_END diff -r f315dd9dbb96 -r fbd6a7385dc2 pkg/devices/lib/i2c/src/Makefile --- a/pkg/devices/lib/i2c/src/Makefile Tue Sep 26 01:13:47 2023 +0200 +++ b/pkg/devices/lib/i2c/src/Makefile Tue Sep 26 21:27:50 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libi2c.o.a libi2c.o.so PC_FILENAME := libdrivers-i2c -SRC_CC := jz4730.cc jz4780.cc +SRC_CC := jz4730.cc jz4780.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/i2c/include diff -r f315dd9dbb96 -r fbd6a7385dc2 pkg/devices/lib/i2c/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/x1600.cc Tue Sep 26 21:27:50 2023 +0200 @@ -0,0 +1,803 @@ +/* + * I2C support for the X1600. + * + * Copyright (C) 2017, 2018, 2021, 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 + +#include +#include +#include + +#include + +/* NOTE: This peripheral is very similar to the JZ4780 with the registers + renamed to I2C from I2C, with a few high speed registers added, and + with I2C_SDAHD appearing at a different location. */ + +enum Regs +{ + I2c_control = 0x000, // I2C_CON + I2c_target_address = 0x004, // I2C_TAR + I2c_slave_address = 0x008, // I2C_SAR + I2c_master_code = 0x00c, // I2C_HS_MADDR + I2c_data_command = 0x010, // I2C_DC + Std_high_count = 0x014, // I2C_SHCNT + Std_low_count = 0x018, // I2C_SLCNT + Fast_high_count = 0x01c, // I2C_FHCNT + Fast_low_count = 0x020, // I2C_FLCNT + High_high_count = 0x024, // I2C_HHCNT + High_low_count = 0x028, // I2C_HLCNT + Int_status = 0x02c, // I2C_INTST (read-only) + Int_mask = 0x030, // I2C_INTM + Int_raw_status = 0x034, // I2C_RINTST (read-only) + Rx_fifo_thold = 0x038, // I2C_RXTL + Tx_fifo_thold = 0x03c, // I2C_TXTL + Int_combined_clear = 0x040, // I2C_CINT (read-only) + Int_rx_uf_clear = 0x044, // I2C_CRXUF (read-only) + Int_rx_of_clear = 0x048, // I2C_CRXOF (read-only) + Int_tx_of_clear = 0x04c, // I2C_CTXOF (read-only) + Int_rd_req_clear = 0x050, // I2C_CRXREQ (read-only) + Int_tx_abort_clear = 0x054, // I2C_CTXABT (read-only) + Int_rx_done_clear = 0x058, // I2C_CRXDN (read-only) + Int_activity_clear = 0x05c, // I2C_CACT (read-only) + Int_stop_clear = 0x060, // I2C_CSTP (read-only) + Int_start_clear = 0x064, // I2C_CSTT (read-only) + Int_call_clear = 0x068, // I2C_CGC (read-only) + I2c_enable = 0x06c, // I2C_ENB + I2c_status = 0x070, // I2C_ST (read-only) + Tx_fifo_count = 0x074, // I2C_TXFLR (read-only) + Rx_fifo_count = 0x078, // I2C_RXFLR (read-only) + I2c_sda_hold_time = 0x07c, // I2C_SDAHD + Trans_abort_status = 0x080, // I2C_ABTSRC (read-only) + Slv_data_nack = 0x084, // I2CSDNACK + I2c_dma_ctrl = 0x088, // I2C_DMACR + I2c_trans_data_lvl = 0x08c, // I2C_DMATDLR + I2c_recv_data_lvl = 0x090, // I2C_DMARDLR + I2c_sda_setup_time = 0x094, // I2C_SDASU + I2c_ack_call = 0x098, // I2C_ACKGC + I2c_enable_status = 0x09c, // I2C_ENBST (read-only) + I2c_spike_suppress = 0x0a0, // I2C_FSPKLEN + + I2c_block_offset = 0x1000 +}; + +enum I2c_control_bits : unsigned +{ + I2c_disable_slave = 0x40, // SLVDIS (slave disabled) + I2c_enable_restart = 0x20, // RESTART + I2c_master_10bit = 0x10, // MATP (read-only) + I2c_slave_10bit = 0x08, // SATP + I2c_speed_mode_mask = 0x06, // SPEED + I2c_enable_master = 0x01, // MD (master enabled) + I2c_speed_bit = 1, // SPD +}; + +enum I2c_speed_mode_values : unsigned +{ + I2c_speed_standard = 1, + I2c_speed_fast = 2, + I2c_speed_high = 3, +}; + +enum I2c_enable_bits : unsigned +{ + I2c_enable_enabled = 0x01, // I2CEN +}; + +enum I2c_status_bits : unsigned +{ + I2c_status_master_act = 0x20, // MSTACT (master active) + I2c_status_rx_nempty = 0x08, // RFNE (read queue not empty) + I2c_status_tx_empty = 0x04, // TFE (write queue empty) + I2c_status_tx_nfull = 0x02, // TFNF (write queue not full) + I2c_status_active = 0x01, // ACT (device active as master or slave) +}; + +enum I2c_target_bits : unsigned +{ + I2c_target_master_10bit = 0x1000, + I2c_target_special = 0x0800, // SPECIAL: perform general call or start byte + I2c_target_start_byte = 0x0400, // Special: start byte (1) or general call (0) + I2c_target_10bits = 0x3ff, // Mask for 10-bit address + I2c_target_7bits = 0x7f, // Mask for 7-bit address +}; + +enum I2c_hold_control_bits : unsigned +{ + /* The hold enable flag has been removed since the JZ4780 and the hold time + field widened. */ + + I2c_hold_mask = 0xffff, +}; + +enum I2c_setup_control_bits : unsigned +{ + I2c_setup_mask = 0x0ff, // SDASU +}; + +enum I2c_command_bits : unsigned +{ + I2c_command_restart = 0x400, // RESTART: explicit restart before next byte + I2c_command_stop = 0x200, // STOP: explicit stop after next byte + I2c_command_no_stop = 0x000, + I2c_command_read = 0x100, // CMD + I2c_command_write = 0x000, // CMD +}; + +enum I2c_fifo_bits : unsigned +{ + I2c_fifo_limit = 64, // RXTL, TXTL (256 noted in field description) +}; + +enum Int_bits : unsigned +{ + Int_call = 0x800, // IGC (general call received) + Int_start = 0x400, // ISTT (start/restart condition occurred) + Int_stop = 0x200, // ISTP (stop condition occurred) + Int_activity = 0x100, // IACT (bus activity interrupt) + Int_rx_done = 0x080, // RXDN (read from master device done) + Int_tx_abort = 0x040, // TXABT (transmit abort) + Int_rd_req = 0x020, // RDREQ (read request from master device) + Int_tx_empty = 0x010, // TXEMP (threshold reached or passed) + Int_tx_of = 0x008, // TXOF (overflow when writing to queue) + Int_rx_full = 0x004, // RXFL (threshold reached or exceeded) + Int_rx_of = 0x002, // RXOF (overflow from device) + Int_rx_uf = 0x001, // RXUF (underflow when reading from queue) +}; + + + +// Initialise a channel. + +I2c_x1600_channel::I2c_x1600_channel(l4_addr_t start, + Cpm_x1600_chip *cpm, + uint32_t frequency) +: _cpm(cpm), _frequency(frequency) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the channel. + +void +I2c_x1600_channel::enable() +{ + _regs[I2c_enable] = I2c_enable_enabled; + while (!(_regs[I2c_enable_status] & I2c_enable_enabled)); +} + +// Disable the channel. + +void +I2c_x1600_channel::disable() +{ + _regs[I2c_enable] = 0; + while (_regs[I2c_enable_status] & I2c_enable_enabled); +} + +// Return the configured frequency. + +uint32_t +I2c_x1600_channel::get_frequency() +{ + return _frequency; +} + +// Set the frequency-related peripheral parameters. + +void +I2c_x1600_channel::set_frequency() +{ + // The APB clock (PCLK) is used to drive I2C transfers. Its value must be + // obtained from the CPM unit. It is known as I2C_DEV_CLK here and is scaled + // to kHz in order to keep the numbers easily representable, as is the bus + // frequency. + + uint32_t i2c_dev_clk = _cpm->get_frequency(Clock_pclock) / 1000; + + // Note that this is not I2C_DEV_CLK but the actual I2C bus frequency. + + uint32_t i2c_clk = _frequency / 1000; + + // Select the appropriate speed. + + unsigned int speed = (i2c_clk <= 100) ? I2c_speed_standard + : (i2c_clk <= 400 ? I2c_speed_fast + : I2c_speed_high); + + _regs[I2c_control] = _regs[I2c_control] | (speed << I2c_speed_bit) | + I2c_disable_slave | + I2c_enable_restart | + I2c_enable_master; + + // According to the programming manual, if the PCLK period is T{I2C_DEV_CLK} + // then the I2C clock period is... + + // T{SCL} = T{SCL_high} + T{SCL_low} + + // Where... + + // T{SCL_low} = T{I2C_DEV_CLK} * (#cycles for low signal) + // T{SCL_high} = T{I2C_DEV_CLK} * (#cycles for high signal) + + // Since, with minimum periods being defined... + + // T{SCL} >= T{min_SCL} + // T{SCL_low} >= T{min_SCL_low} + // T{SCL_high} >= T{min_SCL_high} + // T{min_SCL} = T{min_SCL_low} + T{min_SCL_high} + + // Then the following applies... + + // T{I2C_DEV_CLK} * (#cycles for low signal)) >= T{min_SCL_low} + // T{I2C_DEV_CLK} * (#cycles for high signal) >= T{min_SCL_high} + + // To work with different clock speeds while maintaining the low-to-high + // ratios: + + // T{min_SCL_low} = T{min_SCL} * T{min_SCL_low} / T{min_SCL} + // = T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) + + // T{min_SCL_high} = T{min_SCL} * T{min_SCL_high} / T{min_SCL} + // = T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) + + // Constraints are given with respect to the high and low count registers. + + // #cycles for high signal = I2CxHCNT + 8 + // #cycles for low signal = I2CxLCNT + 1 + + // From earlier, this yields... + + // T{I2C_DEV_CLK} * (I2CxLCNT + 1) >= T{min_SCL_low} + // T{I2C_DEV_CLK} * (I2CxHCNT + 8) >= T{min_SCL_high} + + // Rearranging... + + // I2CxLCNT >= (T{min_SCL_low} / T{I2C_DEV_CLK}) - 1 + // >= T{min_SCL_low} * I2C_DEV_CLK - 1 + + // I2CxHCNT >= (T{min_SCL_high} / T{I2C_DEV_CLK}) - 8 + // >= T{min_SCL_high} * I2C_DEV_CLK - 8 + + // Introducing the definitions for the high and low periods... + + // I2CxLCNT >= T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) * I2C_DEV_CLK - 1 + // >= (T{min_SCL_low} / T{min_SCL}) * I2C_DEV_CLK / I2C_BUS_CLK - 1 + + // I2CxHCNT >= T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) * I2C_DEV_CLK - 8 + // >= (T{min_SCL_high} / T{min_SCL}) * I2C_DEV_CLK / I2C_BUS_CLK - 8 + + uint32_t high_reg, low_reg; + uint32_t high_count, low_count; + int32_t hold_count; + uint32_t setup_count; + + // Level hold times: + + // Standard Fast High + // SCL low 4.7us 1.3us 0.5us + // SCL high 4.0us 0.6us 0.26us + + // SCL period 8.7us 1.9us 0.76us = + + // See: UM10204 "I2C-bus specification and user manual" + // Table 10: t{LOW} and t{HIGH} + + if (i2c_clk <= 100) // 100 kHz + { + low_count = (i2c_dev_clk * 47) / (i2c_clk * 87) - 1; + high_count = (i2c_dev_clk * 40) / (i2c_clk * 87) - 8; + low_reg = Std_low_count; + high_reg = Std_high_count; + } + else if (i2c_clk <= 400) // 400 kHz + { + low_count = (i2c_dev_clk * 13) / (i2c_clk * 19) - 1; + high_count = (i2c_dev_clk * 6) / (i2c_clk * 19) - 8; + low_reg = Fast_low_count; + high_reg = Fast_high_count; + } + else // > 400 kHz + { + // Note how the frequencies are scaled to accommodate the extra precision + // required. + + low_count = (i2c_dev_clk / 10 * 50) / (i2c_clk / 10 * 76) - 1; + high_count = (i2c_dev_clk / 10 * 26) / (i2c_clk / 10 * 76) - 8; + low_reg = High_low_count; + high_reg = High_high_count; + } + + // Minimum counts are 8 and 6 for low and high respectively. + + _regs[low_reg] = low_count < 8 ? 8 : low_count; + _regs[high_reg] = high_count < 6 ? 6 : high_count; + + //printf("low_count: %d\n", low_count); + //printf("high_count: %d\n", high_count); + + // Data hold and setup times: + + // Standard Fast High + // t{HD;DAT} 300ns 300ns 300ns + // t{SU;DAT} 250ns 100ns 50ns + + // See: UM10204 "I2C-bus specification and user manual" + // Table 10: t{HD;DAT} and t{SU;DAT}, also note [3] + + // T{delay} = (I2CSDAHD + 2) * T{I2C_DEV_CLK} + // I2CSDAHD = T{delay} / T{I2C_DEV_CLK} - 2 + // I2CSDAHD = I2C_DEV_CLK * T{delay} - 2 + + // Since the device clock is in kHz (scaled down by 1000) and the times are + // given in ns (scaled up by 1000000000), a division of 1000000 is introduced. + + hold_count = (i2c_dev_clk * 300) / 1000000 - 1; + + _regs[I2c_sda_hold_time] = (_regs[I2c_sda_hold_time] & ~I2c_hold_mask) | + (hold_count < 0 ? 0 + : (hold_count < (int) I2c_hold_mask ? (uint32_t) hold_count + : I2c_hold_mask)); + + //printf("i2c_dev_clk: %d\n", i2c_dev_clk); + //printf("SDA hold: %x\n", hold_count); + + // I2C_SDASU is apparently not used in master mode. + + // T{delay} = (I2CSDASU - 1) * T{I2C_DEV_CLK} + // I2CSDASU = T{delay} / T{I2C_DEV_CLK} + 1 + // I2CSDASU = I2C_DEV_CLK * T{delay} + 1 + + if (i2c_clk <= 100) + setup_count = (i2c_dev_clk * 250) / 1000000 + 1; + else if (i2c_clk <= 400) + setup_count = (i2c_dev_clk * 100) / 1000000 + 1; + else + setup_count = (i2c_dev_clk * 50) / 1000000 + 1; + + _regs[I2c_sda_setup_time] = (_regs[I2c_sda_setup_time] & ~I2c_setup_mask) | + (setup_count < I2c_setup_mask ? setup_count : I2c_setup_mask); +} + +// Set the target address and enable transfer. +// NOTE: Only supporting 7-bit addresses currently. + +void +I2c_x1600_channel::set_target(uint8_t address) +{ + //printf("set_target: %x\n", address); + disable(); + set_frequency(); + _regs[I2c_target_address] = address & I2c_target_7bits; + enable(); + init_parameters(); + //printf("I2c_enable_status: %x\n", (uint32_t) _regs[I2c_enable_status]); + //printf("I2c_status: %x\n", (uint32_t) _regs[I2c_status]); + //printf("Int_mask: %x\n", (uint32_t) _regs[Int_mask]); + //printf("Int_status: %x\n", (uint32_t) _regs[Int_status]); + printf("Int_raw_status: %x\n", (uint32_t) _regs[Int_raw_status]); +} + + + +// Reset interrupt flags upon certain conditions. + +void +I2c_x1600_channel::reset_flags() +{ + volatile uint32_t r; + + _regs[Int_mask] = 0; + + // Read from the register to clear interrupts. + + r = _regs[Int_combined_clear]; + (void) r; +} + +// Initialise interrupt flags and queue thresholds for reading and writing. + +void +I2c_x1600_channel::init_parameters() +{ + // Handle read queue conditions for data, write queue conditions for commands. + + reset_flags(); + + _regs[Tx_fifo_thold] = 0; // write when 0 in queue +} + + + +// Return whether the device is active. + +int +I2c_x1600_channel::active() +{ + return _regs[I2c_status] & I2c_status_master_act; +} + +// Return whether data is available to receive. + +int +I2c_x1600_channel::have_input() +{ + return _regs[I2c_status] & I2c_status_rx_nempty; +} + +// Return whether data is queued for sending. + +int +I2c_x1600_channel::have_output() +{ + return !(_regs[I2c_status] & I2c_status_tx_empty); +} + +// Return whether data can be queued for sending. + +int +I2c_x1600_channel::can_send() +{ + return _regs[I2c_status] & I2c_status_tx_nfull; +} + +// Return whether a receive operation has failed. + +int +I2c_x1600_channel::read_failed() +{ + return _regs[Int_status] & Int_rx_of; +} + +// Return whether a send operation has failed. + +int +I2c_x1600_channel::write_failed() +{ + return _regs[Int_status] & Int_tx_abort; +} + +int +I2c_x1600_channel::read_done() +{ + return _pos == _total; +} + +int +I2c_x1600_channel::write_done() +{ + return _reqpos == _total; +} + +unsigned +I2c_x1600_channel::have_read() +{ + return _pos; +} + +unsigned +I2c_x1600_channel::have_written() +{ + return _reqpos; +} + +int +I2c_x1600_channel::failed() +{ + return _fail; +} + + + +// Send read commands for empty queue entries. + +void +I2c_x1600_channel::queue_reads() +{ + unsigned int remaining = _total - _reqpos; + unsigned int queued = _reqpos - _pos; + unsigned int can_queue = I2c_fifo_limit - queued; + + // Keep the number of reads in progress below the length of the read queue. + + if (!can_queue) + return; + + // At most, only queue as many reads as are remaining. + + if (remaining < can_queue) + can_queue = remaining; + + // Queue read requests for any remaining queue entries. + + while (can_queue && can_send()) + { + uint32_t stop = _stop && (_reqpos == _total - 1) ? I2c_command_stop : I2c_command_no_stop; + printf("Queue read %d/%d %s\n", _reqpos, _total - 1, stop ? "stop" : "continue"); + + _regs[I2c_data_command] = I2c_command_read | stop; + _reqpos++; + can_queue--; + } + + // Update the threshold to be notified of any reduced remaining amount. + + set_read_threshold(); +} + +// Send write commands for empty queue entries. + +void +I2c_x1600_channel::queue_writes() +{ + unsigned int remaining = _total - _reqpos; + unsigned int can_queue = I2c_fifo_limit; + + if (remaining < can_queue) + can_queue = remaining; + + printf("queue_writes: %d %s\n", can_queue, can_send() ? "can send" : "cannot send"); + + // Queue write requests for any remaining queue entries. + + while (can_queue && can_send()) + { + uint32_t stop = _stop && (_reqpos == _total - 1) ? I2c_command_stop : I2c_command_no_stop; + printf("Queue write %d/%d %s\n", _reqpos, _total - 1, stop ? "stop" : "continue"); + + _regs[I2c_data_command] = I2c_command_write | _buf[_reqpos] | stop; + _reqpos++; + can_queue--; + } + + printf("Tx_fifo_count = %d\n", (uint32_t) _regs[Tx_fifo_count]); +} + +// Store read command results from the queue. + +void +I2c_x1600_channel::store_reads() +{ + printf("store_reads: %s\n", have_input() ? "input" : "no input"); + + // Read any input and store it in the buffer. + + while (have_input() && (_pos < _reqpos)) + { + _buf[_pos] = _regs[I2c_data_command] & 0xff; + _pos++; + } +} + +void +I2c_x1600_channel::set_read_threshold() +{ + unsigned int queued = _reqpos - _pos; + + if (!queued) + return; + + // Read all expected. + + _regs[Rx_fifo_thold] = queued - 1; + printf("Rx_fifo_thold = %d\n", (uint32_t) _regs[Rx_fifo_thold]); +} + +// Read from the target device. + +void +I2c_x1600_channel::start_read(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _pos = 0; + _reqpos = 0; + _fail = 0; + _stop = stop; + + printf("start_read: %d\n", total); + + _regs[Int_mask] = Int_rx_full | // read condition (reading needed) + Int_rx_of | // abort condition + Int_tx_abort; // abort condition + + // Perform initial read requests. + + read(); +} + +void +I2c_x1600_channel::read() +{ + printf("Rx_fifo_count = %d\n", (uint32_t) _regs[Rx_fifo_count]); + printf("Int_raw_status: %x\n", (uint32_t) _regs[Int_raw_status]); + printf("Trans_abort_status: %x\n", (uint32_t) _regs[Trans_abort_status]); + + if (read_failed() || write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + return; + } + + if (_regs[Int_status] & Int_rx_full) + store_reads(); + + // Always attempt to queue more read requests. + + queue_reads(); +} + +// Write to the target device. + +void +I2c_x1600_channel::start_write(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _reqpos = 0; + _fail = 0; + _stop = stop; + + printf("start_write: %d\n", total); + + // Enable interrupts for further writes. + + _regs[Int_mask] = Int_tx_empty | // write condition (writing needed) + Int_tx_abort; // abort condition + + // Perform initial writes. + + write(); +} + +void +I2c_x1600_channel::write() +{ + printf("Tx_fifo_count = %d\n", (uint32_t) _regs[Tx_fifo_count]); + printf("Int_raw_status: %x\n", (uint32_t) _regs[Int_raw_status]); + printf("Trans_abort_status: %x\n", (uint32_t) _regs[Trans_abort_status]); + + if (write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + return; + } + + if (_regs[Int_status] & Int_tx_empty) + queue_writes(); +} + +// Explicitly stop communication. + +void +I2c_x1600_channel::stop() +{ +} + + + +// Initialise the I2C controller. + +I2c_x1600_chip::I2c_x1600_chip(l4_addr_t start, l4_addr_t end, + Cpm_x1600_chip *cpm, + uint32_t frequency) +: _start(start), _end(end), _cpm(cpm), _frequency(frequency) +{ +} + +// Obtain a channel object. + +I2c_x1600_channel * +I2c_x1600_chip::get_channel(uint8_t channel) +{ + l4_addr_t block = _start + channel * I2c_block_offset; + enum Clock_identifiers bits[] = {Clock_i2c0, Clock_i2c1}; + + if (channel < 2) + { + _cpm->start_clock(bits[channel]); + return new I2c_x1600_channel(block, _cpm, _frequency); + } + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *x1600_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency) +{ + return (void *) new I2c_x1600_chip(start, end, static_cast(cpm), frequency); +} + +void x1600_i2c_disable(void *i2c_channel) +{ + static_cast(i2c_channel)->disable(); +} + +void *x1600_i2c_get_channel(void *i2c, uint8_t channel) +{ + return static_cast(i2c)->get_channel(channel); +} + +uint32_t x1600_i2c_get_frequency(void *i2c_channel) +{ + return static_cast(i2c_channel)->get_frequency(); +} + +void x1600_i2c_set_target(void *i2c_channel, uint8_t addr) +{ + static_cast(i2c_channel)->set_target(addr); +} + +void x1600_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) +{ + static_cast(i2c_channel)->start_read(buf, total, stop); +} + +void x1600_i2c_read(void *i2c_channel) +{ + static_cast(i2c_channel)->read(); +} + +void x1600_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) +{ + static_cast(i2c_channel)->start_write(buf, total, stop); +} + +void x1600_i2c_write(void *i2c_channel) +{ + static_cast(i2c_channel)->write(); +} + +int x1600_i2c_read_done(void *i2c_channel) +{ + return static_cast(i2c_channel)->read_done(); +} + +int x1600_i2c_write_done(void *i2c_channel) +{ + return static_cast(i2c_channel)->write_done(); +} + +unsigned int x1600_i2c_have_read(void *i2c_channel) +{ + return static_cast(i2c_channel)->have_read(); +} + +unsigned int x1600_i2c_have_written(void *i2c_channel) +{ + return static_cast(i2c_channel)->have_written(); +} + +int x1600_i2c_failed(void *i2c_channel) +{ + return static_cast(i2c_channel)->failed(); +} + +void x1600_i2c_stop(void *i2c_channel) +{ + static_cast(i2c_channel)->stop(); +}