# HG changeset patch # User Paul Boddie # Date 1698592478 -3600 # Node ID 41cb8a570af2f19267c1c88f5f8647d35dec18d4 # Parent 398898e77d36cc5269502a4729761b66f978d8d2 Added JZ4780 DMA support, merged X1600 and JZ4780 I2C support. Added missing UART4 clock for the JZ4780. diff -r 398898e77d36 -r 41cb8a570af2 conf/landfall-examples/input-keypad.txt --- a/conf/landfall-examples/input-keypad.txt Sat Oct 28 01:15:45 2023 +0200 +++ b/conf/landfall-examples/input-keypad.txt Sun Oct 29 16:14:38 2023 +0100 @@ -1,1 +1,1 @@ -rom/libkeypad_qi_lb60.so \ No newline at end of file +rom/libkeypad_lx16.so \ No newline at end of file diff -r 398898e77d36 -r 41cb8a570af2 conf/landfall-examples/mips-jz4740-panel.txt --- a/conf/landfall-examples/mips-jz4740-panel.txt Sat Oct 28 01:15:45 2023 +0200 +++ b/conf/landfall-examples/mips-jz4740-panel.txt Sun Oct 29 16:14:38 2023 +0100 @@ -1,1 +1,1 @@ -rom/libpanel_qi_lb60.so \ No newline at end of file +rom/libpanel_lx16.so \ No newline at end of file diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/include/clocks.h --- a/pkg/devices/include/clocks.h Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/include/clocks.h Sun Oct 29 16:14:38 2023 +0100 @@ -87,6 +87,7 @@ Clock_uart1, Clock_uart2, Clock_uart3, + Clock_uart4, Clock_udc, Clock_uhc, Clock_uprt, diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/cpm/src/jz4780.cc --- a/pkg/devices/lib/cpm/src/jz4780.cc Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/cpm/src/jz4780.cc Sun Oct 29 16:14:38 2023 +0100 @@ -549,6 +549,12 @@ return (void *) new Cpm_jz4780_chip(cpm_base); } +const char * +jz4780_cpm_clock_type(void *cpm, enum Clock_identifiers clock) +{ + return static_cast(cpm)->clock_type(clock); +} + int jz4780_cpm_have_clock(void *cpm, enum Clock_identifiers clock) { diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/cpm/src/x1600.cc --- a/pkg/devices/lib/cpm/src/x1600.cc Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/cpm/src/x1600.cc Sun Oct 29 16:14:38 2023 +0100 @@ -460,6 +460,7 @@ &clock_uart1, &clock_uart2, &clock_uart3, + &clock_none, // Clock_uart4 &clock_none, // Clock_udc &clock_none, // Clock_uhc &clock_none, // Clock_uprt diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/include/dma-jz4730.h --- a/pkg/devices/lib/dma/include/dma-jz4730.h Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/dma/include/dma-jz4730.h Sun Oct 29 16:14:38 2023 +0100 @@ -44,12 +44,12 @@ Dma_request_uart1_in = 19, Dma_request_uart0_out = 20, Dma_request_uart0_in = 21, - Dma_request_ssi_send_empty = 22, - Dma_request_ssi_recv_full = 23, - Dma_request_aic_send_empty = 24, - Dma_request_aic_recv_full = 25, - Dma_request_msc_send_empty = 26, - Dma_request_msc_recv_full = 27, + Dma_request_ssi_out = 22, + Dma_request_ssi_in = 23, + Dma_request_aic_out = 24, + Dma_request_aic_in = 25, + Dma_request_msc_out = 26, + Dma_request_msc_in = 27, Dma_request_ost2_underflow = 28, }; diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/include/dma-jz4780.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/include/dma-jz4780.h Sun Oct 29 16:14:38 2023 +0100 @@ -0,0 +1,191 @@ +/* + * DMA support for the JZ4780. + * + * Copyright (C) 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 + */ + +#pragma once + +#include +#include + + + +/* Enumerated types for various transfer parameters. */ + +enum Dma_jz4780_request_type +{ + Dma_request_i2s1_out = 4, + Dma_request_i2s1_in = 5, + Dma_request_i2s0_out = 6, + Dma_request_i2s0_in = 7, + Dma_request_auto = 8, + Dma_request_sadc_in = 9, + Dma_request_uart4_out = 12, + Dma_request_uart4_in = 13, + Dma_request_uart3_out = 14, + Dma_request_uart3_in = 15, + Dma_request_uart2_out = 16, + Dma_request_uart2_in = 17, + Dma_request_uart1_out = 18, + Dma_request_uart1_in = 19, + Dma_request_uart0_out = 20, + Dma_request_uart0_in = 21, + Dma_request_ssi0_out = 22, + Dma_request_ssi0_in = 23, + Dma_request_ssi1_out = 24, + Dma_request_ssi1_in = 25, + Dma_request_msc0_out = 26, + Dma_request_msc0_in = 27, + Dma_request_msc1_out = 28, + Dma_request_msc1_in = 29, + Dma_request_msc2_out = 30, + Dma_request_msc2_in = 31, + Dma_request_pcm0_out = 32, + Dma_request_pcm0_in = 33, + Dma_request_i2c0_out = 36, + Dma_request_i2c0_in = 37, + Dma_request_i2c1_out = 38, + Dma_request_i2c1_in = 39, + Dma_request_i2c2_out = 40, + Dma_request_i2c2_in = 41, + Dma_request_i2c3_out = 42, + Dma_request_i2c3_in = 43, + Dma_request_i2c4_out = 44, + Dma_request_i2c4_in = 45, + Dma_request_des_out = 46, + Dma_request_des_in = 47, +}; + + + +#ifdef __cplusplus + +#include +#include + +// Forward declaration. + +class Dma_jz4780_chip; + + + +// DMA channel. + +class Dma_jz4780_channel +{ +private: + Hw::Register_block<32> _regs; + Dma_jz4780_chip *_chip; + uint8_t _channel; + l4_cap_idx_t _irq = L4_INVALID_CAP; + +public: + Dma_jz4780_channel(Dma_jz4780_chip *chip, uint8_t channel, l4_addr_t start, l4_cap_idx_t irq); + + unsigned int transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4780_request_type type=Dma_request_auto); + + unsigned int wait(); + +protected: + // Transfer property configuration. + + uint32_t encode_req_detect_int_length(uint8_t units); + + uint32_t encode_source_port_width(uint8_t width); + + uint32_t encode_destination_port_width(uint8_t width); + + uint32_t encode_transfer_unit_size(uint8_t size); + + // Transaction control. + + void ack_irq(); + + bool completed(); + + bool error(); + + bool halted(); + + bool wait_for_irq(); + + bool wait_for_irq(unsigned int timeout); +}; + +// DMA device control. + +class Dma_jz4780_chip +{ +private: + Hw::Register_block<32> _regs; + l4_addr_t _start, _end; + Cpm_jz4780_chip *_cpm; + +public: + Dma_jz4780_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4780_chip *cpm); + + void disable(); + + void enable(); + + Dma_jz4780_channel *get_channel(uint8_t channel, l4_cap_idx_t irq); + + // Transaction control. + + void ack_irq(uint8_t channel); + + bool error(); + + bool halted(); + + bool have_interrupt(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *jz4780_dma_init(l4_addr_t start, l4_addr_t end, void *cpm); + +void jz4780_dma_disable(void *dma_chip); + +void jz4780_dma_enable(void *dma_chip); + +void *jz4780_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq); + +unsigned int jz4780_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4780_request_type type); + +unsigned int jz4780_dma_wait(void *dma_channel); + +EXTERN_C_END diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/include/dma-x1600.h --- a/pkg/devices/lib/dma/include/dma-x1600.h Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/dma/include/dma-x1600.h Sun Oct 29 16:14:38 2023 +0100 @@ -30,35 +30,35 @@ enum Dma_x1600_request_type { - Dma_request_auto = 8, - Dma_request_can0_out = 10, - Dma_request_can0_in = 11, - Dma_request_can1_out = 10, - Dma_request_can1_in = 11, - Dma_request_uart3_out = 14, - Dma_request_uart3_in = 15, - Dma_request_uart2_out = 16, - Dma_request_uart2_in = 17, - Dma_request_uart1_out = 18, - Dma_request_uart1_in = 19, - Dma_request_uart0_out = 20, - Dma_request_uart0_in = 21, - Dma_request_ssi0_send_empty = 22, - Dma_request_ssi0_recv_full = 23, - Dma_request_i2c0_send_empty = 36, - Dma_request_i2c0_recv_full = 37, - Dma_request_i2c1_send_empty = 38, - Dma_request_i2c1_recv_full = 39, - Dma_request_ssi_slv_out = 42, - Dma_request_ssi_slv_in = 43, - Dma_request_msc0_send_empty = 52, - Dma_request_msc0_recv_full = 53, - Dma_request_msc1_send_empty = 54, - Dma_request_msc1_recv_full = 55, - Dma_request_sadc_in = 56, - Dma_request_aic_loop_out = 61, - Dma_request_aic_out = 62, - Dma_request_aic_in = 63, + Dma_request_auto = 8, + Dma_request_can0_out = 10, + Dma_request_can0_in = 11, + Dma_request_can1_out = 10, + Dma_request_can1_in = 11, + Dma_request_uart3_out = 14, + Dma_request_uart3_in = 15, + Dma_request_uart2_out = 16, + Dma_request_uart2_in = 17, + Dma_request_uart1_out = 18, + Dma_request_uart1_in = 19, + Dma_request_uart0_out = 20, + Dma_request_uart0_in = 21, + Dma_request_ssi0_out = 22, + Dma_request_ssi0_in = 23, + Dma_request_i2c0_out = 36, + Dma_request_i2c0_in = 37, + Dma_request_i2c1_out = 38, + Dma_request_i2c1_in = 39, + Dma_request_ssi_slv_out = 42, + Dma_request_ssi_slv_in = 43, + Dma_request_msc0_out = 52, + Dma_request_msc0_in = 53, + Dma_request_msc1_out = 54, + Dma_request_msc1_in = 55, + Dma_request_sadc_in = 56, + Dma_request_aic_loop_out = 61, + Dma_request_aic_out = 62, + Dma_request_aic_in = 63, }; diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/src/Makefile --- a/pkg/devices/lib/dma/src/Makefile Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/dma/src/Makefile Sun Oct 29 16:14:38 2023 +0100 @@ -4,7 +4,7 @@ TARGET = libdma.o.a libdma.o.so PC_FILENAME := libdrivers-dma -SRC_CC := jz4730.cc x1600.cc +SRC_CC := jz4730.cc jz4780.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/dma/include diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/src/jz4780.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/dma/src/jz4780.cc Sun Oct 29 16:14:38 2023 +0100 @@ -0,0 +1,520 @@ +/* + * DMA support for the JZ4780. + * NOTE: This should be combined with the X1600 support. + * + * Copyright (C) 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 + +#include + + + +enum Global_regs +{ + Dma_control = 0x1000, // DMAC + Dma_irq_pending = 0x1004, // DIRQP +}; + +enum Channel_regs +{ + Dma_source = 0x00, // DSA + Dma_destination = 0x04, // DTA + Dma_transfer_count = 0x08, // DTC + Dma_request_source = 0x0c, // DRT + Dma_control_status = 0x10, // DCS + Dma_command = 0x14, // DCM + Dma_descriptor_address = 0x18, // DDA + Dma_stride = 0x1c, // DSD +}; + +enum Dma_control_bits : unsigned +{ + Dma_fast_msc_transfer = 0x80000000, // FMSC + Dma_fast_ssi_transfer = 0x40000000, // FSSI + Dma_fast_tssi_transfer = 0x20000000, // FTSSI + Dma_fast_uart_transfer = 0x10000000, // FUART + Dma_fast_aic_transfer = 0x08000000, // FAIC + + Dma_intc_irq_channel_mask = 0x003e0000, // INTCC + Dma_intc_irq_channel_bind = 0x00010000, // INTCE + + Dma_control_trans_halted = 0x00000008, // HLT + Dma_control_address_error = 0x00000004, // AR + Dma_control_special_ch01 = 0x00000002, // CH01 + Dma_control_enable = 0x00000001, // DMAE +}; + +enum Dma_transfer_count_bits : unsigned +{ + Dma_transfer_count_mask = 0x00ffffff, +}; + +enum Dma_request_source_bits : unsigned +{ + Dma_request_type_mask = 0x0000003f, +}; + +enum Dma_control_status_bits : unsigned +{ + Dma_no_descriptor_transfer = 0x80000000, + Dma_8word_descriptor = 0x40000000, + Dma_copy_offset_mask = 0x0000ff00, + Dma_address_error = 0x00000010, + Dma_trans_completed = 0x00000008, + Dma_trans_halted = 0x00000004, + Dma_channel_enable = 0x00000001, + + Dma_copy_offset_shift = 8, +}; + +enum Dma_command_bits : unsigned +{ + Dma_special_source_mask = 0x0c000000, + Dma_special_source_tcsm = 0x00000000, + Dma_special_source_bch_nemc = 0x04000000, + Dma_special_source_reserved_ddr = 0x08000000, + + Dma_special_destination_mask = 0x03000000, + Dma_special_destination_tcsm = 0x00000000, + Dma_special_destination_bch_nemc = 0x01000000, + Dma_special_destination_reserved_ddr = 0x02000000, + + Dma_source_address_increment = 0x00800000, + Dma_source_address_no_increment = 0x00000000, + Dma_destination_address_increment = 0x00400000, + Dma_destination_address_no_increment = 0x00000000, + + Dma_recommended_data_unit_size_mask = 0x000f0000, + Dma_source_port_width_mask = 0x0000c000, + Dma_destination_port_width_mask = 0x00003000, + Dma_transfer_unit_size_mask = 0x00000f00, + + Dma_trans_unit_size_32_bit = 0x00000000, + Dma_trans_unit_size_8_bit = 0x00000100, + Dma_trans_unit_size_16_bit = 0x00000200, + Dma_trans_unit_size_16_byte = 0x00000300, + Dma_trans_unit_size_32_byte = 0x00000400, + Dma_trans_unit_size_64_byte = 0x00000500, + Dma_trans_unit_size_128_byte = 0x00000600, + Dma_trans_unit_size_autonomous = 0x00000700, + + Dma_stride_enable = 0x00000004, + Dma_transfer_irq_enable = 0x00000002, + Dma_descriptor_link_enable = 0x00000001, + + Dma_recommended_data_unit_size_shift = 16, + Dma_source_port_width_shift = 14, + Dma_destination_port_width_shift = 12, + Dma_transfer_unit_size_shift = 8, +}; + +enum Dma_port_width_values : unsigned +{ + Dma_port_width_32_bit = 0, + Dma_port_width_8_bit = 1, + Dma_port_width_16_bit = 2, +}; + + + +// Initialise a channel. + +Dma_jz4780_channel::Dma_jz4780_channel(Dma_jz4780_chip *chip, uint8_t channel, + l4_addr_t start, l4_cap_idx_t irq) +: _chip(chip), _channel(channel), _irq(irq) +{ + _regs = new Hw::Mmio_register_block<32>(start); + + // Initialise the transfer count. + + _regs[Dma_transfer_count] = 0; +} + +// Return the closest interval length greater than or equal to the number of +// units given encoded in the request detection interval length field of the +// control/status register. + +uint32_t +Dma_jz4780_channel::encode_req_detect_int_length(uint8_t units) +{ + static uint8_t lengths[] = {0, 1, 2, 3, 4, 8, 16, 32, 64, 128}; + int i; + + if (!units) + return 0; + + for (i = 0; i <= 9; i++) + { + if (lengths[i] >= units) + break; + } + + return i << Dma_recommended_data_unit_size_shift; +} + +// Encode the appropriate source port width for the given request type. + +uint32_t +Dma_jz4780_channel::encode_source_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_source_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_source_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_source_port_width_shift; + } +} + +// Encode the appropriate destination port width for the given request type. + +uint32_t +Dma_jz4780_channel::encode_destination_port_width(uint8_t width) +{ + switch (width) + { + case 1: + return Dma_port_width_8_bit << Dma_destination_port_width_shift; + + case 2: + return Dma_port_width_16_bit << Dma_destination_port_width_shift; + + default: + return Dma_port_width_32_bit << Dma_destination_port_width_shift; + } +} + +// Encode the transfer unit size. +// NOTE: This does not handle the external case. + +uint32_t +Dma_jz4780_channel::encode_transfer_unit_size(uint8_t size) +{ + switch (size) + { + case 0: + return Dma_trans_unit_size_autonomous; + + case 1: + return Dma_trans_unit_size_8_bit; + + case 2: + return Dma_trans_unit_size_16_bit; + + case 16: + return Dma_trans_unit_size_16_byte; + + case 32: + return Dma_trans_unit_size_32_byte; + + case 64: + return Dma_trans_unit_size_64_byte; + + case 128: + return Dma_trans_unit_size_128_byte; + + default: + return Dma_trans_unit_size_32_bit; + } +} + +// Transfer data between memory locations. + +unsigned int +Dma_jz4780_channel::transfer(uint32_t source, uint32_t destination, + unsigned int count, + bool source_increment, bool destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4780_request_type type) +{ + printf("transfer:%s%s%s%s\n", error() ? " error" : "", + halted() ? " halted" : "", + completed() ? " completed" : "", + _regs[Dma_transfer_count] ? " count" : ""); + + // Ensure an absence of address error and halt conditions globally and in this channel. + + if (error() || halted()) + return 0; + + // Ensure an absence of transaction completed and zero transfer count for this channel. + + if (completed() || _regs[Dma_transfer_count]) + return 0; + + // Disable the channel. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~Dma_channel_enable; + + // Set addresses. + + _regs[Dma_source] = source; + _regs[Dma_destination] = destination; + + // Set transfer count to the number of units. + + unsigned int units = count < Dma_transfer_count_mask ? count : Dma_transfer_count_mask; + + _regs[Dma_transfer_count] = units; + + // Set auto-request for memory-to-memory transfers. Otherwise, set the + // indicated request type. + + _regs[Dma_request_source] = type; + + // For a descriptor, the actual fields would be populated instead of the + // command register, descriptor transfer would be indicated in the control/ + // status register along with the appropriate descriptor size indicator. + + /* NOTE: To be considered... + * request detection interval length (for autonomous mode) + */ + + _regs[Dma_command] = (source_increment ? Dma_source_address_increment : Dma_source_address_no_increment) | + (destination_increment ? Dma_destination_address_increment : Dma_destination_address_no_increment) | + encode_source_port_width(source_width) | + encode_destination_port_width(destination_width) | + encode_transfer_unit_size(transfer_unit_size) | + Dma_transfer_irq_enable; + + // For a descriptor, the descriptor address would be set and the doorbell + // register field for the channel set. + + // Enable the channel (and peripheral). + + _regs[Dma_control_status] = Dma_no_descriptor_transfer | + Dma_channel_enable; + + // Return the number of units to transfer. + + return units; +} + +unsigned int +Dma_jz4780_channel::wait() +{ + // An interrupt will occur upon completion, the completion flag will be set + // and the transfer count will be zero. + + unsigned int remaining = 0; + + do + { + if (!wait_for_irq(1000000)) + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + else + { + printf("status = %x\n", (uint32_t) _regs[Dma_control_status]); + remaining = _regs[Dma_transfer_count]; + ack_irq(); + break; + } + } + while (!error() && !halted() && !completed()); + + // Reset the channel status. + + _regs[Dma_control_status] = _regs[Dma_control_status] & ~(Dma_channel_enable | + Dma_trans_completed | Dma_address_error | + Dma_trans_halted); + _regs[Dma_transfer_count] = 0; + + // Return the number of remaining units. + + return remaining; +} + +// Wait indefinitely for an interrupt request, returning true if one was delivered. + +bool +Dma_jz4780_channel::wait_for_irq() +{ + return !l4_error(l4_irq_receive(_irq, L4_IPC_NEVER)) && _chip->have_interrupt(_channel); +} + +// Wait up to the given timeout (in microseconds) for an interrupt request, +// returning true if one was delivered. + +bool +Dma_jz4780_channel::wait_for_irq(unsigned int timeout) +{ + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))) && _chip->have_interrupt(_channel); +} + +// Acknowledge an interrupt condition. + +void +Dma_jz4780_channel::ack_irq() +{ + _chip->ack_irq(_channel); +} + +// Return whether a transfer has completed. + +bool +Dma_jz4780_channel::completed() +{ + return _regs[Dma_control_status] & Dma_trans_completed ? true : false; +} + +// Return whether an address error condition has arisen. + +bool +Dma_jz4780_channel::error() +{ + return _chip->error() || (_regs[Dma_control_status] & Dma_address_error ? true : false); +} + +// Return whether a transfer has halted. + +bool +Dma_jz4780_channel::halted() +{ + return _chip->halted() || (_regs[Dma_control_status] & Dma_trans_halted ? true : false); +} + + + +// Initialise the I2C controller. + +Dma_jz4780_chip::Dma_jz4780_chip(l4_addr_t start, l4_addr_t end, + Cpm_jz4780_chip *cpm) +: _start(start), _end(end), _cpm(cpm) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the peripheral. + +void +Dma_jz4780_chip::enable() +{ + // Make sure that the DMA clock is available. + + _cpm->start_clock(Clock_dma); + + _regs[Dma_control] = Dma_control_enable; + while (!(_regs[Dma_control] & Dma_control_enable)); +} + +// Disable the channel. + +void +Dma_jz4780_chip::disable() +{ + _regs[Dma_control] = 0; + while (_regs[Dma_control] & Dma_control_enable); +} + +// Obtain a channel object. + +Dma_jz4780_channel * +Dma_jz4780_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) +{ + if (channel < 32) + return new Dma_jz4780_channel(this, channel, _start + 0x20 * channel, irq); + else + throw -L4_EINVAL; +} + +// Return whether an interrupt is pending on the given channel. + +bool +Dma_jz4780_chip::have_interrupt(uint8_t channel) +{ + return _regs[Dma_irq_pending] & (1 << channel) ? true : false; +} + +// Acknowledge an interrupt condition on the given channel. + +void +Dma_jz4780_chip::ack_irq(uint8_t channel) +{ + _regs[Dma_irq_pending] = _regs[Dma_irq_pending] & ~(1 << channel); +} + +// Return whether an address error condition has arisen. + +bool +Dma_jz4780_chip::error() +{ + return _regs[Dma_control] & Dma_control_address_error ? true : false; +} + +// Return whether a transfer has halted. + +bool +Dma_jz4780_chip::halted() +{ + return _regs[Dma_control] & Dma_control_trans_halted ? true : false; +} + + + +// C language interface functions. + +void *jz4780_dma_init(l4_addr_t start, l4_addr_t end, void *cpm) +{ + return (void *) new Dma_jz4780_chip(start, end, static_cast(cpm)); +} + +void jz4780_dma_disable(void *dma_chip) +{ + static_cast(dma_chip)->disable(); +} + +void jz4780_dma_enable(void *dma_chip) +{ + static_cast(dma_chip)->enable(); +} + +void *jz4780_dma_get_channel(void *dma, uint8_t channel, l4_cap_idx_t irq) +{ + return static_cast(dma)->get_channel(channel, irq); +} + +unsigned int jz4780_dma_transfer(void *dma_channel, + uint32_t source, uint32_t destination, + unsigned int count, + int source_increment, int destination_increment, + uint8_t source_width, uint8_t destination_width, + uint8_t transfer_unit_size, + enum Dma_jz4780_request_type type) +{ + return static_cast(dma_channel)->transfer(source, + destination, count, source_increment, destination_increment, source_width, + destination_width, transfer_unit_size, type); +} + +unsigned int jz4780_dma_wait(void *dma_channel) +{ + return static_cast(dma_channel)->wait(); +} diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/dma/src/x1600.cc --- a/pkg/devices/lib/dma/src/x1600.cc Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/dma/src/x1600.cc Sun Oct 29 16:14:38 2023 +0100 @@ -68,7 +68,7 @@ enum Dma_request_source_bits : unsigned { - Dma_request_type_mask = 0x0000001f, + Dma_request_type_mask = 0x0000003f, }; enum Dma_control_status_bits : unsigned diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/include/i2c-common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-common.h Sun Oct 29 16:14:38 2023 +0100 @@ -0,0 +1,123 @@ +/* + * I2C support for the JZ4780 and 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_channel +{ +protected: + Hw::Register_block<32> _regs; + Cpm_chip *_cpm; + uint32_t _frequency; + + // Buffer management. + + unsigned int _pos, _reqpos, _total; + uint8_t *_buf; + + // Status conditions. + + int _fail; + int _stop; + + // Common operations. + + 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(); + +public: + explicit I2c_channel(l4_addr_t start, enum Clock_identifiers clock, + Cpm_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(); +}; + +// I2C device control. + +class I2c_chip +{ +protected: + l4_addr_t _start, _end; + Cpm_chip *_cpm; + uint32_t _frequency; + + virtual unsigned int num_channels() = 0; + +public: + explicit I2c_chip(l4_addr_t start, l4_addr_t end, Cpm_chip *cpm, + uint32_t frequency); + + I2c_channel *get_channel(uint8_t channel); +}; + +#endif /* __cplusplus */ diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/include/i2c-jz4780.h --- a/pkg/devices/lib/i2c/include/i2c-jz4780.h Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/i2c/include/i2c-jz4780.h Sun Oct 29 16:14:38 2023 +0100 @@ -28,91 +28,28 @@ #ifdef __cplusplus -#include -#include +#include // I2C channel. -class I2c_jz4780_channel +class I2c_jz4780_channel : public I2c_channel { -private: - Hw::Register_block<32> _regs; - Cpm_jz4780_chip *_cpm; - uint32_t _frequency; - - // Buffer management. - - unsigned int _pos, _reqpos, _total; - uint8_t *_buf; - - // Status conditions. - - int _fail; - public: - I2c_jz4780_channel(l4_addr_t start, Cpm_jz4780_chip *cpm, - uint32_t frequency); - - void set_target(uint8_t addr); - - // Reading initiation and execution. - - void start_read(uint8_t buf[], unsigned int total); - void read(); - - // Writing initiation and execution. - - void start_write(uint8_t buf[], unsigned int total); - 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(); + explicit I2c_jz4780_channel(l4_addr_t start, enum Clock_identifiers clock, + Cpm_chip *cpm, uint32_t frequency); }; // I2C device control. -class I2c_jz4780_chip +class I2c_jz4780_chip : public I2c_chip { -private: - l4_addr_t _start, _end; - Cpm_jz4780_chip *_cpm; - uint32_t _frequency; +protected: + unsigned int num_channels() + { return 5; } public: - I2c_jz4780_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4780_chip *cpm, - uint32_t frequency); - - I2c_jz4780_channel *get_channel(uint8_t channel); + explicit I2c_jz4780_chip(l4_addr_t start, l4_addr_t end, Cpm_chip *cpm, + uint32_t frequency); }; #endif /* __cplusplus */ @@ -126,17 +63,19 @@ void *jz4780_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency); -void jz4780_i2c_disable(void *i2c_channel); +void *jz4780_i2c_get_channel(void *i2c, uint8_t channel); -void *jz4780_i2c_get_channel(void *i2c, uint8_t channel); +uint32_t jz4780_i2c_get_frequency(void *i2c_channel); void jz4780_i2c_set_target(void *i2c_channel, uint8_t addr); -void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total); +void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); void jz4780_i2c_read(void *i2c_channel); -void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total); +void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop); void jz4780_i2c_write(void *i2c_channel); @@ -150,6 +89,4 @@ int jz4780_i2c_failed(void *i2c_channel); -void jz4780_i2c_stop(void *i2c_channel); - EXTERN_C_END diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/include/i2c-x1600.h --- a/pkg/devices/lib/i2c/include/i2c-x1600.h Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/i2c/include/i2c-x1600.h Sun Oct 29 16:14:38 2023 +0100 @@ -28,93 +28,28 @@ #ifdef __cplusplus -#include -#include +#include // I2C channel. -class I2c_x1600_channel +class I2c_x1600_channel : public I2c_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, enum Clock_identifiers clock, - 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(); + explicit I2c_x1600_channel(l4_addr_t start, enum Clock_identifiers clock, + Cpm_chip *cpm, uint32_t frequency); }; // I2C device control. -class I2c_x1600_chip +class I2c_x1600_chip : public I2c_chip { -private: - l4_addr_t _start, _end; - Cpm_x1600_chip *_cpm; - uint32_t _frequency; +protected: + unsigned int num_channels() + { return 2; } 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); + explicit I2c_x1600_chip(l4_addr_t start, l4_addr_t end, Cpm_chip *cpm, + uint32_t frequency); }; #endif /* __cplusplus */ diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/src/Makefile --- a/pkg/devices/lib/i2c/src/Makefile Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/i2c/src/Makefile Sun Oct 29 16:14:38 2023 +0100 @@ -4,7 +4,7 @@ TARGET = libi2c.o.a libi2c.o.so PC_FILENAME := libdrivers-i2c -SRC_CC := jz4730.cc jz4780.cc x1600.cc gpio.cc +SRC_CC := common.cc jz4730.cc jz4780.cc x1600.cc gpio.cc PRIVATE_INCDIR += $(PKGDIR)/lib/i2c/include diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/src/common.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/common.cc Sun Oct 29 16:14:38 2023 +0100 @@ -0,0 +1,706 @@ +/* + * I2C support for the JZ4780 and 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 + +/* NOTE: The X1600 is very similar to the JZ4780 with the registers + renamed to I2C from SMB, with a few high speed registers, the + I2C_RINTST, I2C_TXFLR and I2C_RXFLR registers, and a few other + registers added, 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 + Int_status = 0x02c, // I2C_INTST (read-only) + Int_mask = 0x030, // I2C_INTM + 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) + I2c_sda_hold_time = 0x07c, // I2C_SDAHD + Trans_abort_status = 0x080, // I2C_ABTSRC (read-only) + 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) + + // X1600 only... + + High_high_count = 0x024, // I2C_HHCNT + High_low_count = 0x028, // I2C_HLCNT + Int_raw_status = 0x034, // I2C_RINTST (read-only) + Tx_fifo_count = 0x074, // I2C_TXFLR (read-only) + Rx_fifo_count = 0x078, // I2C_RXFLR (read-only) + Slv_data_nack = 0x084, // I2CSDNACK + I2c_spike_suppress = 0x0a0, // I2C_FSPKLEN + + // Register block spacing. + + 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_channel::I2c_channel(l4_addr_t start, + enum Clock_identifiers clock, + Cpm_chip *cpm, + uint32_t frequency) +: _cpm(cpm), _frequency(frequency) +{ + _regs = new Hw::Mmio_register_block<32>(start); + _cpm->start_clock(clock); +} + +// Enable the channel. + +void +I2c_channel::enable() +{ + _regs[I2c_enable] = I2c_enable_enabled; + while (!(_regs[I2c_enable_status] & I2c_enable_enabled)); +} + +// Disable the channel. + +void +I2c_channel::disable() +{ + _regs[I2c_enable] = 0; + while (_regs[I2c_enable_status] & I2c_enable_enabled); +} + +// Return the configured frequency. + +uint32_t +I2c_channel::get_frequency() +{ + return _frequency; +} + +// Set the frequency-related peripheral parameters. + +void +I2c_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); + + // NOTE: Permit broader configuration elsewhere. + + _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; + + // 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)); + + // 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_channel::set_target(uint8_t address) +{ + disable(); + set_frequency(); + _regs[I2c_target_address] = address & I2c_target_7bits; + init_parameters(); + enable(); +} + + + +// Reset interrupt flags upon certain conditions. + +void +I2c_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_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_channel::active() +{ + return _regs[I2c_status] & I2c_status_master_act; +} + +// Return whether data is available to receive. + +int +I2c_channel::have_input() +{ + return _regs[I2c_status] & I2c_status_rx_nempty; +} + +// Return whether data is queued for sending. + +int +I2c_channel::have_output() +{ + return !(_regs[I2c_status] & I2c_status_tx_empty); +} + +// Return whether data can be queued for sending. + +int +I2c_channel::can_send() +{ + return _regs[I2c_status] & I2c_status_tx_nfull; +} + +// Return whether a receive operation has failed. + +int +I2c_channel::read_failed() +{ + return _regs[Int_status] & Int_rx_of; +} + +// Return whether a send operation has failed. + +int +I2c_channel::write_failed() +{ + return _regs[Int_status] & Int_tx_abort; +} + +int +I2c_channel::read_done() +{ + return _pos == _total; +} + +int +I2c_channel::write_done() +{ + return (_reqpos == _total) && !have_output(); +} + +unsigned +I2c_channel::have_read() +{ + return _pos; +} + +unsigned +I2c_channel::have_written() +{ + return _reqpos; +} + +int +I2c_channel::failed() +{ + return _fail; +} + + + +// Send read commands for empty queue entries. + +void +I2c_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; + + _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_channel::queue_writes() +{ + unsigned int remaining = _total - _reqpos; + unsigned int can_queue = I2c_fifo_limit; + + if (remaining < can_queue) + can_queue = remaining; + + // 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; + + _regs[I2c_data_command] = I2c_command_write | _buf[_reqpos] | stop; + _reqpos++; + can_queue--; + } +} + +// Store read command results from the queue. + +void +I2c_channel::store_reads() +{ + // Read any input and store it in the buffer. + + while (have_input() && (_pos < _reqpos)) + { + _buf[_pos] = _regs[I2c_data_command] & 0xff; + _pos++; + } +} + +void +I2c_channel::set_read_threshold() +{ + unsigned int queued = _reqpos - _pos; + + if (!queued) + return; + + // Read all expected. + + _regs[Rx_fifo_thold] = queued - 1; +} + +// Read from the target device. + +void +I2c_channel::start_read(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _pos = 0; + _reqpos = 0; + _fail = 0; + _stop = stop; + + reset_flags(); + + _regs[Int_mask] = Int_rx_full | // read condition (reading needed) + Int_rx_of | // abort condition + Int_tx_abort; // general abort condition + + // Perform initial read requests. + + read(); +} + +void +I2c_channel::read() +{ + // Test for the general transfer abort condition. + + if (read_failed() || write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + disable(); + enable(); + 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_channel::start_write(uint8_t buf[], unsigned int total, int stop) +{ + _buf = buf; + _total = total; + _reqpos = 0; + _fail = 0; + _stop = stop; + + reset_flags(); + + // 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_channel::write() +{ + if (write_failed()) + { + _fail = 1; + _regs[Int_mask] = 0; + disable(); + enable(); + return; + } + + if (_regs[Int_status] & Int_tx_empty) + queue_writes(); +} + +// Explicitly stop communication. + +void +I2c_channel::stop() +{ +} + + + +// Initialise the I2C controller. + +I2c_chip::I2c_chip(l4_addr_t start, l4_addr_t end, + Cpm_chip *cpm, + uint32_t frequency) +: _start(start), _end(end), _cpm(cpm), _frequency(frequency) +{ +} + +// Obtain a channel object. + +I2c_channel * +I2c_chip::get_channel(uint8_t channel) +{ + l4_addr_t block = _start + channel * I2c_block_offset; + + // NOTE: Defining all the possible clocks. + + enum Clock_identifiers clocks[] = {Clock_i2c0, Clock_i2c1, Clock_i2c2, Clock_i2c3, Clock_i2c4}; + + if (channel < num_channels()) + return new I2c_channel(block, clocks[channel], _cpm, _frequency); + else + throw -L4_EINVAL; +} diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/src/jz4780.cc --- a/pkg/devices/lib/i2c/src/jz4780.cc Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/i2c/src/jz4780.cc Sun Oct 29 16:14:38 2023 +0100 @@ -1,7 +1,7 @@ /* * I2C support for the JZ4780. * - * Copyright (C) 2017, 2018, 2021 Paul Boddie + * 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 @@ -20,13 +20,6 @@ */ #include -#include - -#include -#include -#include - -#include /* I2C pins: @@ -42,573 +35,16 @@ See: http://mipscreator.imgtec.com/CI20/hardware/board/ci20_jz4780_v2.0.pdf */ -enum Regs -{ - Smb_control = 0x000, // SMBCON - Smb_target_address = 0x004, // SMBTAR - Smb_slave_address = 0x008, // SMBSAR - Smb_data_command = 0x010, // SMBDC - Std_high_count = 0x014, // SMBSHCNT - Std_low_count = 0x018, // SMBSLCNT - Fast_high_count = 0x01c, // SMBFHCNT - Fast_low_count = 0x020, // SMBFLCNT - Int_status = 0x02c, // SMBINTST (read-only) - Int_mask = 0x030, // SMBINTM - - Rx_fifo_thold = 0x038, // SMBRXTL - Tx_fifo_thold = 0x03c, // SMBTXTL - Int_combined_clear = 0x040, // SMBCINT (read-only) - Int_rx_uf_clear = 0x044, // SMBCRXUF (read-only) - Int_rx_of_clear = 0x048, // SMBCRXOF (read-only) - Int_tx_of_clear = 0x04c, // SMBCTXOF (read-only) - Int_rd_req_clear = 0x050, // SMBCRXREQ (read-only) - Int_tx_abort_clear = 0x054, // SMBCTXABT (read-only) - Int_rx_done_clear = 0x058, // SMBCRXDN (read-only) - Int_activity_clear = 0x05c, // SMBCACT (read-only) - Int_stop_clear = 0x060, // SMBCSTP (read-only) - Int_start_clear = 0x064, // SMBCSTT (read-only) - Int_call_clear = 0x068, // SMBCGC (read-only) - Smb_enable = 0x06c, // SMBENB - Smb_status = 0x070, // SMBST (read-only) - - Tx_fifo_count = 0x074, // SMBTXFLR (read-only) - Rx_fifo_count = 0x078, // SMBRXFLR (read-only) - - Trans_abort_status0 = 0x080, // SMBABTSRC (read-only) - Trans_abort_status1 = 0x084, // ... (read-only) - - Smb_dma_ctrl = 0x088, // SMBDMACR - Smb_trans_data_lvl = 0x08c, // SMBDMATDLR - Smb_recv_data_lvl = 0x090, // SMBDMARDLR - Smb_sda_setup_time = 0x094, // SMBSDASU - Smb_ack_call = 0x098, // SMBACKGC - - Smb_enable_status = 0x09c, // SMBENBST (read-only) - Smb_sda_hold_time = 0x0d0, // SMBSDAHD - - Smb_block_offset = 0x1000 -}; - -enum Smb_control_bits : unsigned -{ - Smb_no_stop_empty = 0x80, // STPHLD (no STP condition when queue empty) - Smb_disable_slave = 0x40, // SLVDIS (slave disabled) - Smb_enable_restart = 0x20, // REST - Smb_enable_master = 0x01, // MD (master enabled) - Smb_speed_bit = 1, // SPD -}; - -enum Smb_enable_bits : unsigned -{ - Smb_enable_enabled = 0x01, // SMBEN -}; - -enum Smb_status_bits : unsigned -{ - Smb_status_master_act = 0x20, // MSTACT (master active) - Smb_status_rx_nempty = 0x08, // RFNE (read queue not empty) - Smb_status_tx_empty = 0x04, // TFE (write queue empty) - Smb_status_tx_nfull = 0x02, // TFNF (write queue not full) - Smb_status_active = 0x01, // ACT (device active as master or slave) -}; - -enum Smb_target_bits : unsigned -{ - Smb_target_7bits = 0x7f, -}; - -enum Smb_hold_control_bits : unsigned -{ - Smb_hold_enable = 0x100, // HDENB - Smb_hold_disable = 0x000, // HDENB - Smb_hold_mask = 0x1ff, -}; - -enum Smb_setup_control_bits : unsigned -{ - Smb_setup_mask = 0x0ff, -}; - -enum Smb_command_bits : unsigned -{ - Smb_command_read = 0x100, // CMD - Smb_command_write = 0x000, // CMD -}; - -enum Smb_fifo_bits : unsigned -{ - Smb_fifo_limit = 16, -}; - -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_jz4780_channel::I2c_jz4780_channel(l4_addr_t start, - Cpm_jz4780_chip *cpm, + enum Clock_identifiers clock, + Cpm_chip *cpm, uint32_t frequency) -: _cpm(cpm), _frequency(frequency) -{ - _regs = new Hw::Mmio_register_block<32>(start); -} - -// Enable the channel. - -void -I2c_jz4780_channel::enable() -{ - _regs[Smb_enable] = Smb_enable_enabled; - while (!(_regs[Smb_enable_status] & Smb_enable_enabled)); -} - -// Disable the channel. - -void -I2c_jz4780_channel::disable() -{ - _regs[Smb_enable] = 0; - while (_regs[Smb_enable_status] & Smb_enable_enabled); -} - -// Set the frequency-related peripheral parameters. - -void -I2c_jz4780_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 SMB_CLK here and is scaled to - // kHz in order to keep the numbers easily representable, as is the bus - // frequency. - - uint32_t smb_clk = _cpm->get_frequency(Clock_pclock) / 1000; - uint32_t i2c_clk = _frequency / 1000; - unsigned int speed = (i2c_clk <= 100) ? 1 : 2; - - _regs[Smb_control] = _regs[Smb_control] | (speed << Smb_speed_bit) | - Smb_disable_slave | - Smb_enable_restart | - Smb_enable_master; - - // According to the programming manual, if the PCLK period is T{SMB_CLK} - // then the I2C clock period is... - - // T{SCL} = T{SCL_high} + T{SCL_low} - - // Where... - - // T{SCL_low} = T{SMB_CLK} * (#cycles for low signal) - // T{SCL_high} = T{SMB_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{SMB_CLK} * (#cycles for low signal)) >= T{min_SCL_low} - // T{SMB_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 = SMBxHCNT + 8 - // #cycles for low signal = SMBxLCNT + 1 - - // From earlier, this yields... - - // T{SMB_CLK} * (SMBxLCNT + 1) >= T{min_SCL_low} - // T{SMB_CLK} * (SMBxHCNT + 8) >= T{min_SCL_high} - - // Rearranging... - - // SMBxLCNT >= (T{min_SCL_low} / T{SMB_CLK}) - 1 - // >= T{min_SCL_low} * SMB_CLK - 1 - - // SMBxHCNT >= (T{min_SCL_high} / T{SMB_CLK}) - 8 - // >= T{min_SCL_high} * SMB_CLK - 8 - - // Introducing the definitions for the high and low periods... - - // SMBxLCNT >= T{min_SCL} * (T{min_SCL_low} / (T{min_SCL_low} + T{min_SCL_high})) * SMB_CLK - 1 - // >= (T{min_SCL_low} / T{min_SCL}) * SMB_CLK / I2C_CLK - 1 - - // SMBxHCNT >= T{min_SCL} * (T{min_SCL_high} / (T{min_SCL_low} + T{min_SCL_high})) * SMB_CLK - 8 - // >= (T{min_SCL_high} / T{min_SCL}) * SMB_CLK / I2C_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 - // SCL low 4.7us 1.3us - // SCL high 4.0us 0.6us + - // SCL period 8.7us 1.9us = - - if (i2c_clk <= 100) // 100 kHz - { - low_count = (smb_clk * 47) / (i2c_clk * 87) - 1; - high_count = (smb_clk * 40) / (i2c_clk * 87) - 8; - low_reg = Std_low_count; - high_reg = Std_high_count; - } - else - { - low_count = (smb_clk * 13) / (i2c_clk * 19) - 1; - high_count = (smb_clk * 6) / (i2c_clk * 19) - 8; - low_reg = Fast_low_count; - high_reg = Fast_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; - - // Data hold and setup times: - - // Standard Fast - // t{HD;DAT} 300ns 300ns - // t{SU;DAT} 250ns 100ns - - // T{delay} = (SMBSDAHD + 1) * T{SMB_CLK} - // SMBSDAHD = T{delay} / T{SMB_CLK} - 1 - // SMBSDAHD = SMB_CLK * T{delay} - 1 - - hold_count = (smb_clk * 300) / 1000000 - 1; - - _regs[Smb_sda_hold_time] = (_regs[Smb_sda_hold_time] & ~Smb_hold_mask) | - (hold_count >= 0 ? Smb_hold_enable : Smb_hold_disable) | - (hold_count < 0 ? 0 : hold_count < 255 ? hold_count : 255); - - // T{delay} = (SMBSDASU - 1) * T{SMB_CLK} - // SMBSDASU = T{delay} / T{SMB_CLK} + 1 - // SMBSDASU = SMB_CLK * T{delay} + 1 - - if (i2c_clk <= 100) - setup_count = (smb_clk * 250) / 1000000 + 1; - else - setup_count = (smb_clk * 100) / 1000000 + 1; - - _regs[Smb_sda_setup_time] = (_regs[Smb_sda_setup_time] & ~Smb_setup_mask) | - (setup_count < 255 ? setup_count : 255); -} - -// Set the target address and enable transfer. -// NOTE: Only supporting 7-bit addresses currently. - -void -I2c_jz4780_channel::set_target(uint8_t address) -{ - disable(); - set_frequency(); - _regs[Smb_target_address] = address & Smb_target_7bits; - enable(); - init_parameters(); -} - - - -// Reset interrupt flags upon certain conditions. - -void -I2c_jz4780_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_jz4780_channel::init_parameters() -{ - // Handle read queue conditions for data, write queue conditions for commands. - - reset_flags(); - - _regs[Int_mask] = Int_rx_full | // read condition (reading needed) - Int_rx_of | // abort condition - Int_tx_empty | // write condition (writing needed) - Int_tx_abort; // abort condition - - _regs[Tx_fifo_thold] = 0; // write when 0 in queue - - // Make sure that the stop condition does not occur automatically. - - _regs[Smb_control] = _regs[Smb_control] | Smb_no_stop_empty; -} - - - -// Return whether the device is active. - -int -I2c_jz4780_channel::active() +: I2c_channel(start, clock, cpm, frequency) { - return _regs[Smb_status] & Smb_status_master_act; -} - -// Return whether data is available to receive. - -int -I2c_jz4780_channel::have_input() -{ - return _regs[Smb_status] & Smb_status_rx_nempty; -} - -// Return whether data is queued for sending. - -int -I2c_jz4780_channel::have_output() -{ - return !(_regs[Smb_status] & Smb_status_tx_empty); -} - -// Return whether data can be queued for sending. - -int -I2c_jz4780_channel::can_send() -{ - return _regs[Smb_status] & Smb_status_tx_nfull; -} - -// Return whether a receive operation has failed. - -int -I2c_jz4780_channel::read_failed() -{ - return _regs[Int_status] & Int_rx_of; -} - -// Return whether a send operation has failed. - -int -I2c_jz4780_channel::write_failed() -{ - return _regs[Int_status] & Int_tx_abort; -} - -int -I2c_jz4780_channel::read_done() -{ - return _pos == _total; -} - -int -I2c_jz4780_channel::write_done() -{ - return _reqpos == _total; -} - -unsigned -I2c_jz4780_channel::have_read() -{ - return _pos; -} - -unsigned -I2c_jz4780_channel::have_written() -{ - return _reqpos; -} - -int -I2c_jz4780_channel::failed() -{ - return _fail; -} - - - -// Send read commands for empty queue entries. - -void -I2c_jz4780_channel::queue_reads() -{ - unsigned int remaining = _total - _reqpos; - unsigned int queued = _reqpos - _pos; - - // Permit one more issued read request due to the behaviour of the peripheral - // to withhold a request unless the stop condition has been issued. - - unsigned int can_queue = Smb_fifo_limit - queued + 1; - - // 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()) - { - _regs[Smb_data_command] = Smb_command_read; - _reqpos++; - can_queue--; - } - - // Issue the stop condition after the final read request. - // In practice, an extra read request works better since it does not risk a - // transmission abort condition and would permit following transactions. - // However, it does risk causing an address autoincrement with some devices. - - if (_total == _reqpos) - _regs[Smb_data_command] = Smb_command_read; - //stop(); - - // Update the threshold to be notified of any reduced remaining amount. - - set_read_threshold(); -} - -// Send write commands for empty queue entries. - -void -I2c_jz4780_channel::queue_writes() -{ - unsigned int remaining = _total - _reqpos; - unsigned int can_queue = Smb_fifo_limit; - - if (remaining < can_queue) - can_queue = remaining; - - // Queue write requests for any remaining queue entries. - - while (can_queue && can_send()) - { - _regs[Smb_data_command] = Smb_command_write | _buf[_reqpos]; - _reqpos++; - can_queue--; - } -} - -// Store read command results from the queue. - -void -I2c_jz4780_channel::store_reads() -{ - // Read any input and store it in the buffer. - - while (have_input() && (_pos < _reqpos)) - { - _buf[_pos] = _regs[Smb_data_command] & 0xff; - _pos++; - } -} - -void -I2c_jz4780_channel::set_read_threshold() -{ - unsigned int queued = _reqpos - _pos; - - if (!queued) - return; - - // Read all expected. - - _regs[Rx_fifo_thold] = queued - 1; -} - -// Read from the target device. - -void -I2c_jz4780_channel::start_read(uint8_t buf[], unsigned int total) -{ - _buf = buf; - _total = total; - _pos = 0; - _reqpos = 0; - _fail = 0; - - set_read_threshold(); -} - -void -I2c_jz4780_channel::read() -{ - if (read_failed() || write_failed()) - { - _fail = 1; - return; - } - - if (_regs[Int_status] & Int_rx_full) - store_reads(); - if (_regs[Int_status] & Int_tx_empty) - queue_reads(); -} - -// Write to the target device. - -void -I2c_jz4780_channel::start_write(uint8_t buf[], unsigned int total) -{ - _buf = buf; - _total = total; - _reqpos = 0; - _fail = 0; -} - -void -I2c_jz4780_channel::write() -{ - if (write_failed()) - { - _fail = 1; - return; - } - - if (_regs[Int_status] & Int_tx_empty) - queue_writes(); -} - -// Explicitly stop communication. - -void -I2c_jz4780_channel::stop() -{ - _regs[Smb_control] = _regs[Smb_control] & ~Smb_no_stop_empty; } @@ -616,27 +52,10 @@ // Initialise the I2C controller. I2c_jz4780_chip::I2c_jz4780_chip(l4_addr_t start, l4_addr_t end, - Cpm_jz4780_chip *cpm, + Cpm_chip *cpm, uint32_t frequency) -: _start(start), _end(end), _cpm(cpm), _frequency(frequency) -{ -} - -// Obtain a channel object. - -I2c_jz4780_channel * -I2c_jz4780_chip::get_channel(uint8_t channel) +: I2c_chip(start, end, cpm, frequency) { - l4_addr_t block = _start + channel * Smb_block_offset; - enum Clock_identifiers bits[] = {Clock_i2c0, Clock_i2c1, Clock_i2c2, Clock_i2c3, Clock_i2c4}; - - if (channel < 5) - { - _cpm->start_clock(bits[channel]); - return new I2c_jz4780_channel(block, _cpm, _frequency); - } - else - throw -L4_EINVAL; } @@ -645,12 +64,7 @@ void *jz4780_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency) { - return (void *) new I2c_jz4780_chip(start, end, static_cast(cpm), frequency); -} - -void jz4780_i2c_disable(void *i2c_channel) -{ - static_cast(i2c_channel)->disable(); + return (void *) new I2c_jz4780_chip(start, end, static_cast(cpm), frequency); } void *jz4780_i2c_get_channel(void *i2c, uint8_t channel) @@ -658,14 +72,20 @@ return static_cast(i2c)->get_channel(channel); } +uint32_t jz4780_i2c_get_frequency(void *i2c_channel) +{ + return static_cast(i2c_channel)->get_frequency(); +} + void jz4780_i2c_set_target(void *i2c_channel, uint8_t addr) { static_cast(i2c_channel)->set_target(addr); } -void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total) +void jz4780_i2c_start_read(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) { - static_cast(i2c_channel)->start_read(buf, total); + static_cast(i2c_channel)->start_read(buf, total, stop); } void jz4780_i2c_read(void *i2c_channel) @@ -673,9 +93,10 @@ static_cast(i2c_channel)->read(); } -void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total) +void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total, + int stop) { - static_cast(i2c_channel)->start_write(buf, total); + static_cast(i2c_channel)->start_write(buf, total, stop); } void jz4780_i2c_write(void *i2c_channel) @@ -707,8 +128,3 @@ { return static_cast(i2c_channel)->failed(); } - -void jz4780_i2c_stop(void *i2c_channel) -{ - static_cast(i2c_channel)->stop(); -} diff -r 398898e77d36 -r 41cb8a570af2 pkg/devices/lib/i2c/src/x1600.cc --- a/pkg/devices/lib/i2c/src/x1600.cc Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/devices/lib/i2c/src/x1600.cc Sun Oct 29 16:14:38 2023 +0100 @@ -20,146 +20,6 @@ */ #include -#include - -#include -#include -#include - -/* NOTE: This peripheral is very similar to the JZ4780 with the registers - renamed to I2C from SMB, 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) -}; @@ -167,508 +27,9 @@ I2c_x1600_channel::I2c_x1600_channel(l4_addr_t start, enum Clock_identifiers clock, - Cpm_x1600_chip *cpm, + Cpm_chip *cpm, uint32_t frequency) -: _cpm(cpm), _frequency(frequency) -{ - _regs = new Hw::Mmio_register_block<32>(start); - _cpm->start_clock(clock); -} - -// 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); - - // NOTE: Permit broader configuration elsewhere. - - _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; - - // 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)); - - // 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) -{ - disable(); - set_frequency(); - _regs[I2c_target_address] = address & I2c_target_7bits; - init_parameters(); - enable(); -} - - - -// 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) && !have_output(); -} - -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; - - _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; - - // 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; - - _regs[I2c_data_command] = I2c_command_write | _buf[_reqpos] | stop; - _reqpos++; - can_queue--; - } -} - -// Store read command results from the queue. - -void -I2c_x1600_channel::store_reads() -{ - // 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; -} - -// 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; - - reset_flags(); - - _regs[Int_mask] = Int_rx_full | // read condition (reading needed) - Int_rx_of | // abort condition - Int_tx_abort; // general abort condition - - // Perform initial read requests. - - read(); -} - -void -I2c_x1600_channel::read() -{ - // Test for the general transfer abort condition. - - if (read_failed() || write_failed()) - { - _fail = 1; - _regs[Int_mask] = 0; - disable(); - enable(); - 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; - - reset_flags(); - - // 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() -{ - if (write_failed()) - { - _fail = 1; - _regs[Int_mask] = 0; - disable(); - enable(); - return; - } - - if (_regs[Int_status] & Int_tx_empty) - queue_writes(); -} - -// Explicitly stop communication. - -void -I2c_x1600_channel::stop() +: I2c_channel(start, clock, cpm, frequency) { } @@ -677,33 +38,19 @@ // 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) + Cpm_chip *cpm, + uint32_t frequency) +: I2c_chip(start, end, cpm, 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 clocks[] = {Clock_i2c0, Clock_i2c1}; - - if (channel < 2) - return new I2c_x1600_channel(block, clocks[channel], _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); + return (void *) new I2c_x1600_chip(start, end, static_cast(cpm), frequency); } void *x1600_i2c_get_channel(void *i2c, uint8_t channel) diff -r 398898e77d36 -r 41cb8a570af2 pkg/landfall-examples/ci20_i2c/ci20_i2c.c --- a/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Sat Oct 28 01:15:45 2023 +0200 +++ b/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Sun Oct 29 16:14:38 2023 +0100 @@ -63,19 +63,18 @@ } static long i2c_read(void *i2c_channel, uint8_t *buf, unsigned length, - l4_cap_idx_t irqcap) + int stop, l4_cap_idx_t irqcap) { l4_msgtag_t tag; - long err; - jz4780_i2c_start_read(i2c_channel, buf, length); + jz4780_i2c_start_read(i2c_channel, buf, length, stop); while (!jz4780_i2c_read_done(i2c_channel)) { - tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(1, 20))); + tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_from_us(1000))); - if ((err = l4_ipc_error(tag, l4_utcb()))) - return err; + if (l4_ipc_error(tag, l4_utcb())) + return 0; if (jz4780_i2c_failed(i2c_channel)) return 0; @@ -87,19 +86,19 @@ } static long i2c_write(void *i2c_channel, uint8_t *buf, unsigned length, - l4_cap_idx_t irqcap) + int stop, l4_cap_idx_t irqcap) { l4_msgtag_t tag; long err; - jz4780_i2c_start_write(i2c_channel, buf, length); + jz4780_i2c_start_write(i2c_channel, buf, length, stop); while (!jz4780_i2c_write_done(i2c_channel)) { - tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(1, 20))); + tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_from_us(1000))); if ((err = l4_ipc_error(tag, l4_utcb()))) - return err; + return 0; if (jz4780_i2c_failed(i2c_channel)) return 0; @@ -115,9 +114,7 @@ long pos; memset(buffer, 0, length); - pos = i2c_read(i2c_channel, buffer, length, irqcap); - - jz4780_i2c_stop(i2c_channel); + pos = i2c_read(i2c_channel, buffer, length, 1, irqcap); return pos; } @@ -125,7 +122,7 @@ static long i2c_get_reg(void *i2c_channel, uint8_t reg, uint8_t *buffer, long length, l4_cap_idx_t irqcap) { buffer[0] = reg; - i2c_write(i2c_channel, buffer, 1, irqcap); + i2c_write(i2c_channel, buffer, 1, 0, irqcap); return i2c_get(i2c_channel, buffer, length, irqcap); } @@ -137,9 +134,7 @@ buf[0] = reg; memcpy(buf + 1, buffer, length); - pos = i2c_write(i2c_channel, buf, length + 1, irqcap); - - jz4780_i2c_stop(i2c_channel); + pos = i2c_write(i2c_channel, buf, length + 1, 1, irqcap); return pos; } @@ -459,8 +454,8 @@ sleep(1); } - jz4780_i2c_disable(i2c0); - jz4780_i2c_disable(i2c4); + //jz4780_i2c_disable(i2c0); + //jz4780_i2c_disable(i2c4); /* Detach from the interrupt. */