# HG changeset patch # User Paul Boddie # Date 1609691315 -3600 # Node ID 4fb6ee3d46619c8e3f7cbfa0b4f36d34bae009dd # Parent 8fd418104ecd92f766b665d25b27f01311ab398c# Parent 2891ef300b865c0d4ccf6d7cbba4c11c9555ab93 Merged changes from the default branch. diff -r 8fd418104ecd -r 4fb6ee3d4661 docs/wiki/Getting_Started --- a/docs/wiki/Getting_Started Fri Jan 01 01:49:35 2021 +0100 +++ b/docs/wiki/Getting_Started Sun Jan 03 17:28:35 2021 +0100 @@ -56,13 +56,12 @@ }}} (Here, `$LANDFALL` needs to expand to the location of this distribution -whereas `$L4DIR` needs to expand to the location of the L4Re/Fiasco.OC -software.) +whereas `$L4DIR` needs to expand to the location of the L4Re software.) For example: {{{ -~/L4/Landfall/tools/install.sh ~/L4/src +~/L4/Landfall/tools/install.sh ~/L4/src/l4 }}} (The repository root of the L4Re/Fiasco.OC distribution typically has a diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/cpm/include/cpm-jz4730.h --- a/pkg/devices/lib/cpm/include/cpm-jz4730.h Fri Jan 01 01:49:35 2021 +0100 +++ b/pkg/devices/lib/cpm/include/cpm-jz4730.h Sun Jan 03 17:28:35 2021 +0100 @@ -1,5 +1,7 @@ /* - * Copyright (C) 2017, 2018 Paul Boddie + * CPM (clock and power management) support for the JZ4730. + * + * Copyright (C) 2017, 2018, 2020 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 @@ -69,6 +71,9 @@ void set_lcd_pixel_divider(uint16_t division); void set_lcd_frequencies(uint32_t pclk, uint8_t ratio); + void start_i2c(); + void stop_i2c(); + void start_lcd(); void stop_lcd(); @@ -97,6 +102,9 @@ int jz4730_cpm_have_clock(void *cpm); void jz4730_cpm_start_clock(void *cpm); +void jz4730_cpm_start_i2c(void *cpm); +void jz4730_cpm_stop_i2c(void *cpm); + void jz4730_cpm_start_lcd(void *cpm); void jz4730_cpm_stop_lcd(void *cpm); diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/cpm/src/jz4730.cc --- a/pkg/devices/lib/cpm/src/jz4730.cc Fri Jan 01 01:49:35 2021 +0100 +++ b/pkg/devices/lib/cpm/src/jz4730.cc Sun Jan 03 17:28:35 2021 +0100 @@ -3,7 +3,7 @@ * provided by the jz4730. The power management functionality could be exposed * using a separate driver. * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2020 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 @@ -57,8 +57,27 @@ enum Clock_gate_bits : unsigned { - Clock_gate_lcd = 7, // LCD - Clock_gate_timer = 3, // OST + Clock_gate_uprt = 25, + Clock_gate_udc = 24, + Clock_gate_cim = 23, + Clock_gate_kbc = 22, + Clock_gate_emac = 21, + Clock_gate_uart3 = 20, + Clock_gate_aic_bitclk = 18, + Clock_gate_scc = 14, + Clock_gate_msc = 13, + Clock_gate_ssi = 12, + Clock_gate_pwm1 = 11, + Clock_gate_pmw0 = 10, + Clock_gate_aic_pclk = 9, + Clock_gate_i2c = 8, + Clock_gate_lcd = 7, + Clock_gate_uhc = 6, + Clock_gate_dmac = 5, + Clock_gate_timer = 3, + Clock_gate_uart2 = 2, + Clock_gate_uart1 = 1, + Clock_gate_uart0 = 0, }; enum Lcd_divider_bits : unsigned @@ -269,6 +288,22 @@ +// I2C clock control. + +void +Cpm_jz4730_chip::start_i2c() +{ + _regs[Clock_gate] = _regs[Clock_gate] & ~(1 << Clock_gate_i2c); +} + +void +Cpm_jz4730_chip::stop_i2c() +{ + _regs[Clock_gate] = _regs[Clock_gate] | (1 << Clock_gate_i2c); +} + + + // LCD clock control. void @@ -380,6 +415,18 @@ } void +jz4730_cpm_start_i2c(void *cpm) +{ + static_cast(cpm)->start_i2c(); +} + +void +jz4730_cpm_stop_i2c(void *cpm) +{ + static_cast(cpm)->stop_i2c(); +} + +void jz4730_cpm_start_lcd(void *cpm) { static_cast(cpm)->start_lcd(); diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/i2c/include/i2c-jz4730.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/include/i2c-jz4730.h Sun Jan 03 17:28:35 2021 +0100 @@ -0,0 +1,111 @@ +/* + * I2C support for the JZ4730. + * + * Copyright (C) 2017, 2018, 2019, 2020 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_jz4730_channel +{ +private: + Hw::Register_block<32> _regs; + Cpm_jz4730_chip *_cpm; + uint32_t _frequency; + +public: + I2c_jz4730_channel(l4_addr_t start, Cpm_jz4730_chip *cpm, + uint32_t frequency); + + void disable(); + void enable(); + + unsigned int read(uint8_t address, uint8_t buf[], unsigned int length); + unsigned int write(uint8_t address, uint8_t buf[], unsigned int length); + +protected: + void set_frequency(); + + // Transaction control. + + void set_address(uint8_t address, bool read); + + void request_next(); + void send_next(); + void signal_last(); + + void start(); + void stop(); + + // Specific status conditions. + + bool data_valid(); + bool nack(); + bool transferred(); +}; + +// I2C device control. + +class I2c_jz4730_chip +{ +private: + l4_addr_t _start, _end; + Cpm_jz4730_chip *_cpm; + uint32_t _frequency; + +public: + I2c_jz4730_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4730_chip *cpm, + uint32_t frequency); + + I2c_jz4730_channel *get_channel(uint8_t channel); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *jz4730_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, + uint32_t frequency); + +void *jz4730_i2c_get_channel(void *i2c, uint8_t channel); + +void jz4730_i2c_disable(void *i2c_channel); + +void jz4730_i2c_enable(void *i2c_channel); + +unsigned int jz4730_i2c_read(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length); + +unsigned int jz4730_i2c_write(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length); + +EXTERN_C_END diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/i2c/include/i2c-jz4780.h --- a/pkg/devices/lib/i2c/include/i2c-jz4780.h Fri Jan 01 01:49:35 2021 +0100 +++ b/pkg/devices/lib/i2c/include/i2c-jz4780.h Sun Jan 03 17:28:35 2021 +0100 @@ -1,4 +1,6 @@ /* + * I2C support for the JZ4780. + * * Copyright (C) 2017, 2018, 2019 Paul Boddie * * This program is free software; you can redistribute it and/or diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/i2c/src/Makefile --- a/pkg/devices/lib/i2c/src/Makefile Fri Jan 01 01:49:35 2021 +0100 +++ b/pkg/devices/lib/i2c/src/Makefile Sun Jan 03 17:28:35 2021 +0100 @@ -4,7 +4,7 @@ TARGET = libi2c.o.a libi2c.o.so PC_FILENAME := libdrivers-i2c -SRC_CC := jz4780.cc +SRC_CC := jz4730.cc jz4780.cc PRIVATE_INCDIR += $(PKGDIR)/lib/i2c/include diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/i2c/src/jz4730.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/i2c/src/jz4730.cc Sun Jan 03 17:28:35 2021 +0100 @@ -0,0 +1,324 @@ +/* + * I2C support for the JZ4730. + * + * Copyright (C) 2017, 2018, 2020 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 + +/* +I2C pins are dedicated to I2C only and are not GPIO-controlled: + +I2C0: Y4/SMB0_SDA, V5/SMB0_SCK + +Note that there is effectively only one I2C channel. +*/ + +enum Regs +{ + I2c_data = 0x000, // I2CDR + I2c_control = 0x004, // I2CCR + I2c_status = 0x008, // I2CSR + I2c_clock = 0x00c, // I2CGR +}; + +enum I2c_control_bits : unsigned +{ + I2c_control_enable_irq = 0x10, // IEN + I2c_control_start = 0x08, // STA + I2c_control_stop = 0x04, // STO + I2c_control_nack = 0x02, // AC + I2c_control_enable = 0x01, // I2CE +}; + +enum I2c_status_bits : unsigned +{ + I2c_status_buffer_nempty = 0x10, // STX + I2c_status_busy = 0x08, // BUSY + I2c_status_transmit_end = 0x04, // TEND + I2c_status_data_valid = 0x02, // DRF + I2c_status_nack = 0x01, // ACKF +}; + +enum I2c_clock_values : unsigned +{ + I2c_clock_max = 0xffff, + I2c_clock_min = 0, +}; + + + +// Initialise a channel. + +I2c_jz4730_channel::I2c_jz4730_channel(l4_addr_t start, + Cpm_jz4730_chip *cpm, + uint32_t frequency) +: _cpm(cpm), _frequency(frequency) +{ + _regs = new Hw::Mmio_register_block<32>(start); +} + +// Enable the channel. + +void +I2c_jz4730_channel::enable() +{ + // Make sure that the I2C clock is available. + + _cpm->start_i2c(); + + // Set the bus clock frequency. + + set_frequency(); + + // Enable the channel and interrupts. + + _regs[I2c_control] = I2c_control_enable | I2c_control_enable_irq; + while (!(_regs[I2c_control] & I2c_control_enable)); +} + +// Disable the channel. + +void +I2c_jz4730_channel::disable() +{ + _regs[I2c_control] = 0; + while (_regs[I2c_control] & I2c_control_enable); +} + +// Set the frequency-related peripheral parameters. + +void +I2c_jz4730_channel::set_frequency() +{ + // The APB clock (PCLK) is used to drive I2C transfers. Its value must be + // obtained from the CPM unit and is scaled to kHz in order to keep the + // numbers easily representable, as is the bus frequency. + + uint32_t pclk = _cpm->get_pclock_frequency() / 1000; + uint32_t i2c_clk = _frequency / 1000; + uint32_t division = pclk / (16 * i2c_clk); + + if (division > I2c_clock_min) + { + division -= 1; + if (division > I2c_clock_max) + division = I2c_clock_max; + } + + _regs[I2c_clock] = division; +} + +// Present the address on the bus. + +void +I2c_jz4730_channel::set_address(uint8_t address, bool read) +{ + start(); + + while (nack()); + + _regs[I2c_data] = (address << 1) | (read ? 1 : 0); + send_next(); + + if (read) + while ((data_valid() || !transferred()) && !nack()); + else + while (data_valid() && !nack()); +} + +// Read data from the bus. + +unsigned int +I2c_jz4730_channel::read(uint8_t address, uint8_t buf[], unsigned int length) +{ + unsigned int nread = 0; + + set_address(address, true); + + if (!nack()) + { + if (length == 1) + signal_last(); + + while (nread < length) + { + while (!data_valid()); + + if (nread == length - 2) + signal_last(); + + buf[nread++] = _regs[I2c_data]; + request_next(); + } + } + + stop(); + + return nread; +} + +// Write data to the bus. + +unsigned int +I2c_jz4730_channel::write(uint8_t address, uint8_t buf[], unsigned int length) +{ + unsigned int nwritten = 0; + + set_address(address, false); + + while ((nwritten < length) && !nack()) + { + _regs[I2c_data] = buf[nwritten++]; + send_next(); + + while (data_valid() && !nack()); + } + + stop(); + + while (!transferred()); + + return nwritten; +} + +// Test for data validity. + +bool +I2c_jz4730_channel::data_valid() +{ + return (_regs[I2c_status] & I2c_status_data_valid) ? true : false; +} + +// Request the next byte by clearing the data validity flag. + +void +I2c_jz4730_channel::request_next() +{ + _regs[I2c_status] = _regs[I2c_status] & ~I2c_status_data_valid; +} + +// Indicate data ready for sending. + +void +I2c_jz4730_channel::send_next() +{ + _regs[I2c_status] = _regs[I2c_status] | I2c_status_data_valid; +} + +// Test for non-acknowledgement. + +bool +I2c_jz4730_channel::nack() +{ + return (_regs[I2c_status] & I2c_status_nack) ? true : false; +} + +// Set non-acknowledgement when receiving data. + +void +I2c_jz4730_channel::signal_last() +{ + _regs[I2c_control] = _regs[I2c_control] | I2c_control_nack; +} + +// Test for write transfer completion. + +bool +I2c_jz4730_channel::transferred() +{ + return (_regs[I2c_status] & I2c_status_transmit_end) ? true : false; +} + +// Explicitly start communication. + +void +I2c_jz4730_channel::start() +{ + _regs[I2c_control] = (_regs[I2c_control] & ~I2c_control_nack) | I2c_control_start; +} + +// Explicitly stop communication. + +void +I2c_jz4730_channel::stop() +{ + _regs[I2c_control] = _regs[I2c_control] | I2c_control_stop; +} + + + +// Initialise the I2C controller. + +I2c_jz4730_chip::I2c_jz4730_chip(l4_addr_t start, l4_addr_t end, + Cpm_jz4730_chip *cpm, + uint32_t frequency) +: _start(start), _end(end), _cpm(cpm), _frequency(frequency) +{ +} + +// Obtain a channel object. Only one channel is supported. + +I2c_jz4730_channel * +I2c_jz4730_chip::get_channel(uint8_t channel) +{ + if (channel == 0) + return new I2c_jz4730_channel(_start, _cpm, _frequency); + else + throw -L4_EINVAL; +} + + + +// C language interface functions. + +void *jz4730_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency) +{ + return (void *) new I2c_jz4730_chip(start, end, static_cast(cpm), frequency); +} + +void *jz4730_i2c_get_channel(void *i2c, uint8_t channel) +{ + return static_cast(i2c)->get_channel(channel); +} + +void jz4730_i2c_disable(void *i2c_channel) +{ + static_cast(i2c_channel)->disable(); +} + +void jz4730_i2c_enable(void *i2c_channel) +{ + static_cast(i2c_channel)->enable(); +} + +unsigned int jz4730_i2c_read(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length) +{ + return static_cast(i2c_channel)->read(address, buf, length); +} + +unsigned int jz4730_i2c_write(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length) +{ + return static_cast(i2c_channel)->write(address, buf, length); +} diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/devices/lib/i2c/src/jz4780.cc --- a/pkg/devices/lib/i2c/src/jz4780.cc Fri Jan 01 01:49:35 2021 +0100 +++ b/pkg/devices/lib/i2c/src/jz4780.cc Sun Jan 03 17:28:35 2021 +0100 @@ -1,4 +1,6 @@ /* + * I2C support for the JZ4780. + * * Copyright (C) 2017, 2018 Paul Boddie * * This program is free software; you can redistribute it and/or diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/landfall-examples/letux400_i2c/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/landfall-examples/letux400_i2c/Makefile Sun Jan 03 17:28:35 2021 +0100 @@ -0,0 +1,10 @@ +PKGDIR ?= .. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = ex_letux400_i2c + +SRC_CC = letux400_i2c.cc + +REQUIRES_LIBS = l4re_c-util libdrivers-cpm libdrivers-i2c libdevice-util + +include $(L4DIR)/mk/prog.mk diff -r 8fd418104ecd -r 4fb6ee3d4661 pkg/landfall-examples/letux400_i2c/letux400_i2c.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/landfall-examples/letux400_i2c/letux400_i2c.cc Sun Jan 03 17:28:35 2021 +0100 @@ -0,0 +1,157 @@ +/* + * Access peripherals on the I2C bus. + * + * Copyright (C) 2018, 2020 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 + + + +/* Scan the I2C bus by performing speculative reads from each device address. */ + +static void i2c_scan(void *i2c_channel) +{ + uint8_t buf[1]; + unsigned int address; + + for (address = 0; address < 0x20; address++) + printf("%02x ", address); + printf("\n"); + + for (address = 0; address < 0x20; address++) + printf("-- "); + + for (address = 0; address < 0x80; address++) + { + if ((address % 32) == 0) + printf("\n"); + + if (jz4730_i2c_read(i2c_channel, address, buf, 1)) + printf("%02x ", address); + else + printf("?? "); + } + + printf("\n"); + for (address = 0; address < 0x20; address++) + printf("-- "); + printf("\n\n"); +} + +/* Interpret RTC registers. */ + +static void rtc_datetime(uint8_t *rtcregs) +{ + const char *weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + + printf("%s %d%d-%d%d-%d%d %d%d:%d%d:%d%d\n", + weekdays[rtcregs[0x06] & 0x07], + (rtcregs[0x08] & 0xf0) >> 4, + rtcregs[0x08] & 0x0f, + (rtcregs[0x07] & 0x10) >> 4, + rtcregs[0x07] & 0x0f, + (rtcregs[0x05] & 0x30) >> 4, + rtcregs[0x05] & 0x0f, + (rtcregs[0x04] & 0x30) >> 4, + rtcregs[0x04] & 0x0f, + (rtcregs[0x03] & 0x70) >> 4, + rtcregs[0x03] & 0x0f, + (rtcregs[0x02] & 0x70) >> 4, + rtcregs[0x02] & 0x0f); +} + + + +int main(void) +{ + void *cpm; + void *i2c, *i2c0; + + /* Peripheral memory. */ + + l4_addr_t cpm_base = 0, cpm_base_end = 0; + l4_addr_t i2c_base = 0, i2c_base_end = 0; + + /* Obtain resource details describing I/O memory. */ + + printf("Access CPM...\n"); + + if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) + return 1; + + printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); + + printf("Access I2C...\n"); + + if (get_memory("jz4730-i2c", &i2c_base, &i2c_base_end) < 0) + return 1; + + printf("I2C at 0x%lx...0x%lx.\n", i2c_base, i2c_base_end); + + /* Obtain CPM and I2C references. */ + + cpm = jz4730_cpm_init(cpm_base); + i2c = jz4730_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */ + i2c0 = jz4730_i2c_get_channel(i2c, 0); + + /* Enable I2C. */ + + jz4730_i2c_enable(i2c0); + + uint8_t buf[32]; + unsigned int nwritten; + + /* Set the clock: 12:34:56 on Saturday 2nd January 2021. */ + + buf[0] = 2; buf[1] = 0x56; buf[2] = 0x34; buf[3] = 0x12; buf[4] = 0x02; buf[5] = 0x06; buf[6] = 0x01; buf[7] = 0x21; + nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 8); + + printf("Written: %d\n", nwritten); + + /* Issue selection of device register 0. */ + + buf[0] = 0; + nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 1); + + printf("Written: %d\n", nwritten); + + /* Read the contents of 16 registers. */ + + unsigned int nread = jz4730_i2c_read(i2c0, 0x51, buf, 16); + + printf("Read: %d\n", nread); + for (unsigned int i = 0; i < nread; i++) + printf("%02x ", buf[i]); + printf("\n"); + + /* Show the interpreted date and time. */ + + rtc_datetime(buf); + + /* Scan the bus. */ + + printf("Scan I2C0...\n"); + i2c_scan(i2c0); + + return 0; +}