# HG changeset patch # User Paul Boddie # Date 1563736476 -7200 # Node ID a8b4e1a31b12e46dcb00fe132a24f468ccab4b78 # Parent ccb30374bad23b26ab81e2236199ad0a95938362 Employed utility functions instead of duplicating them in the example program. Added functionality for scanning I2C bus channels. Introduced a common I2C reading function along with specialised functions for investigating the DDC and regulator bus devices. diff -r ccb30374bad2 -r a8b4e1a31b12 pkg/landfall-examples/ci20_i2c/Makefile --- a/pkg/landfall-examples/ci20_i2c/Makefile Sun Jul 21 21:10:24 2019 +0200 +++ b/pkg/landfall-examples/ci20_i2c/Makefile Sun Jul 21 21:14:36 2019 +0200 @@ -3,6 +3,6 @@ TARGET = ex_ci20_i2c SRC_C = ci20_i2c.c -REQUIRES_LIBS = libio l4re_c-util libdrivers-i2c libdrivers-cpm libdrivers-gpio libedid +REQUIRES_LIBS = libio l4re_c-util libdevice-util libdrivers-i2c libdrivers-cpm libdrivers-gpio libedid include $(L4DIR)/mk/prog.mk diff -r ccb30374bad2 -r a8b4e1a31b12 pkg/landfall-examples/ci20_i2c/ci20_i2c.c --- a/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Sun Jul 21 21:10:24 2019 +0200 +++ b/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Sun Jul 21 21:14:36 2019 +0200 @@ -1,7 +1,7 @@ /* * (c) 2008-2009 Adam Lackorzynski * economic rights: Technische Universität Dresden (Germany) - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2019 Paul Boddie * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +32,12 @@ enum { + PMSDA = 30, /* via PORTD */ + PMSCL = 31, /* via PORTD */ + PWM3 = 3, /* via PORTE */ + PWM4 = 4, /* via PORTE */ + RTCSDA = 12, /* via PORTE */ + RTCSCL = 13, /* via PORTE */ DDCSCL = 24, /* via PORTF */ DDCSDA = 25, /* via PORTF */ }; @@ -38,111 +46,6 @@ /* Device and resource discovery. */ -static char const *resource_type(enum l4io_resource_types_t type) -{ - switch (type) - { - case L4VBUS_RESOURCE_INVALID: - return "INVALID"; - - case L4VBUS_RESOURCE_IRQ: - return "IRQ"; - - case L4VBUS_RESOURCE_MEM: - return "MEMORY"; - - default: - return "OTHER"; - } -} - -static int vbus_get_device(char const *hid, l4io_device_handle_t *dh, l4io_resource_handle_t *rh) -{ - int result = l4io_lookup_device(hid, dh, 0, rh); - - if (result < 0) - printf("Could not access '%s': %s\n", hid, result == -L4_ENOENT ? "no such device" : "no device"); - - return result; -} - -static int vbus_get_resource(l4io_device_handle_t dh, l4io_resource_t *res, - enum l4io_resource_types_t type) -{ - int current = 0, result = 0; - l4_cap_idx_t vbus = l4re_env_get_cap("vbus"); - - do - { - result = l4vbus_get_resource(vbus, dh, current, res); - - if (result) - printf("Could not access resource of type %s.\n", resource_type(type)); - else - printf("Resource %d: type %s, start=%lx, end=%lx\n", res->id, - resource_type(res->type), res->start, res->end); - - current++; - } - while ((!result) && (res->type != type)); - - return result; -} - -static int vbus_get_irq(char const *hid, l4_uint32_t *start, l4_uint32_t *end) -{ - l4io_device_handle_t dh; - l4io_resource_handle_t rh; - l4io_resource_t res; - int result; - - result = vbus_get_device(hid, &dh, &rh); - - if (result < 0) - return result; - - result = vbus_get_resource(dh, &res, L4IO_RESOURCE_IRQ); - - if (result) - return result; - - *start = res.start; - *end = res.end; - - return result; -} - -static int vbus_get_memory(char const *hid, l4_addr_t *start, l4_addr_t *end) -{ - l4io_device_handle_t dh; - l4io_resource_handle_t rh; - l4io_resource_t res; - int result; - - result = vbus_get_device(hid, &dh, &rh); - - if (result < 0) - return result; - - result = vbus_get_resource(dh, &res, L4IO_RESOURCE_MEM); - - if (result) - return result; - - if ((result = l4io_request_iomem(res.start, res.end - res.start + 1, - L4IO_MEM_NONCACHED, start))) - { - printf("Could not get address for '%s'.\n", hid); - return result; - } - - printf("Resource at 0x%lx...0x%lx.\n", res.start, res.end); - - *end = *start + (res.end - res.start + 1); - - return 0; -} - static long item_in_range(long start, long end, long index) { if (start < end) @@ -174,25 +77,180 @@ printf("No reply from bus.\n"); } +static unsigned int i2c_read(void *i2c_channel, uint8_t *buf, unsigned length, + l4_cap_idx_t irqcap) +{ + l4_msgtag_t tag; + long err; + + jz4780_i2c_start_read(i2c_channel, buf, length); + + while (!jz4780_i2c_read_done(i2c_channel)) + { + if (jz4780_i2c_read_incomplete(i2c_channel)) + { + printf("Failed\n"); + break; + } + + tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(1, 20))); + + if ((err = l4_ipc_error(tag, l4_utcb()))) + { + printf("Error on IRQ receive: %ld\n", err); + break; + } + + jz4780_i2c_read(i2c_channel); + } + + jz4780_i2c_stop(i2c_channel); + return jz4780_i2c_have_read(i2c_channel); +} + +static void i2c_scan(void *i2c_channel, l4_cap_idx_t irqcap) +{ + l4_msgtag_t tag; + 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"); + + jz4780_i2c_set_target(i2c_channel, address); + buf[0] = 0; + jz4780_i2c_write(i2c_channel, buf, 1); + jz4780_i2c_start_read(i2c_channel, buf, 1); + + while (!jz4780_i2c_read_done(i2c_channel)) + { + if (jz4780_i2c_read_incomplete(i2c_channel)) + break; + + tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(2, 10))); + + if (l4_ipc_error(tag, l4_utcb())) + break; + + jz4780_i2c_read(i2c_channel); + } + + jz4780_i2c_stop(i2c_channel); + + if (jz4780_i2c_have_read(i2c_channel)) + printf("%02x ", address); + else + printf("?? "); + } + + printf("\n"); + for (address = 0; address < 0x20; address++) + printf("-- "); + printf("\n\n"); +} + +static void ddc_read(void *i2c_channel, l4_cap_idx_t irqcap) +{ + /* Buffer for reading. */ + + uint8_t buf[128]; + unsigned int pos; + + /* DDC EDID details. */ + + unsigned width = 0, height = 0; + + printf("Trying DDC on I2C4...\n"); + + /* Attempt to read from address 0x50 for DDC. + See: drivers/video/fbdev/core/fb_ddc.c */ + + jz4780_i2c_set_target(i2c_channel, 0x50); + buf[0] = 0; + jz4780_i2c_write(i2c_channel, buf, 1); + + printf("Waiting...\n"); + + pos = i2c_read(i2c_channel, buf, 128, irqcap); + show_data(buf, pos); + + /* Attempt to decode EDID information. */ + + libedid_prefered_resolution(buf, &width, &height); + printf("Preferred resolution: %d x %d\n", width, height); + + libedid_dump_standard_timings(buf); +} + +static void pmic_out3_control(void *i2c_channel, l4_cap_idx_t irqcap) +{ + /* Buffer for communicating. */ + + uint8_t buf[2]; + + /* Attempt to read from address 0x5a for PMIC OUT3. */ + + jz4780_i2c_set_target(i2c_channel, 0x5a); + buf[0] = 0x32; + jz4780_i2c_write(i2c_channel, buf, 1); + i2c_read(i2c_channel, buf, 1, irqcap); + printf("Read %02x\n", buf[0]); + +#if 0 + /* Disable the regulator. */ + + printf("Updating...\n"); + buf[1] = buf[0] & ~0x80; + buf[0] = 0x32; + jz4780_i2c_write(i2c_channel, buf, 2); + + /* Read back from the register. Seemed to give 0xff, which makes no real + sense, although the regulator did bring the voltage level low. */ + + buf[0] = 0x32; + jz4780_i2c_write(i2c_channel, buf, 1); + i2c_read(i2c_channel, buf, 1, irqcap); + printf("Read %02x\n", buf[0]); +#endif +} + int main(void) { + long err; + + /* Peripheral memory. */ + l4_addr_t gpio_base = 0, gpio_base_end = 0; + l4_addr_t i2c_base = 0, i2c_base_end = 0; + l4_addr_t cpm_base = 0, cpm_base_end = 0; + l4_addr_t port_d, port_d_end; + l4_addr_t port_e, port_e_end; l4_addr_t port_f, port_f_end; - l4_addr_t i2c_base = 0, i2c_base_end = 0, cpm_base = 0, cpm_base_end; - void *gpio_port_f; - void *i2c, *i2c_channel, *cpm; - l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0, i2c_irq; - l4_cap_idx_t irqcap, icucap; - l4_msgtag_t tag; - long err; - int result = 0; - uint8_t buf[256]; - unsigned pos; - unsigned width = 0, height = 0; + + /* Peripheral abstractions. */ + + void *gpio_port_d, *gpio_port_e, *gpio_port_f; + void *i2c, *cpm; + void *i2c0, *i2c4; + + /* IRQ resources. */ + + l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0; + l4_cap_idx_t icucap, irq0cap, irq4cap; /* Obtain capabilities for the interrupt controller and an interrupt. */ - irqcap = l4re_util_cap_alloc(); + irq0cap = l4re_util_cap_alloc(); + irq4cap = l4re_util_cap_alloc(); icucap = l4re_env_get_cap("icu"); if (l4_is_invalid_cap(icucap)) @@ -201,9 +259,9 @@ return 1; } - if (l4_is_invalid_cap(irqcap)) + if (l4_is_invalid_cap(irq0cap) || l4_is_invalid_cap(irq4cap)) { - printf("No capability available for the interrupt.\n"); + printf("Capabilities not available for interrupts.\n"); return 1; } @@ -211,73 +269,96 @@ printf("Access IRQ...\n"); - if ((result = vbus_get_irq("jz4780-i2c", &i2c_irq_start, &i2c_irq_end)) < 0) + if (get_irq("jz4780-i2c", &i2c_irq_start, &i2c_irq_end) < 0) return 1; - i2c_irq = item_in_range(i2c_irq_start, i2c_irq_end, 4); printf("IRQ range at %d...%d.\n", i2c_irq_start, i2c_irq_end); - printf("I2C IRQ at %d.\n", i2c_irq); /* Obtain resource details describing I/O memory. */ printf("Access GPIO...\n"); - if ((result = vbus_get_memory("jz4780-gpio", &gpio_base, &gpio_base_end)) < 0) + if (get_memory("jz4780-gpio", &gpio_base, &gpio_base_end) < 0) return 1; printf("GPIO at 0x%lx...0x%lx.\n", gpio_base, gpio_base_end); printf("Access CPM...\n"); - if ((result = vbus_get_memory("jz4780-cpm", &cpm_base, &cpm_base_end)) < 0) + if (get_memory("jz4780-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 ((result = vbus_get_memory("jz4780-i2c", &i2c_base, &i2c_base_end)) < 0) + if (get_memory("jz4780-i2c", &i2c_base, &i2c_base_end) < 0) return 1; printf("I2C at 0x%lx...0x%lx.\n", i2c_base, i2c_base_end); - /* Create an interrupt object. */ + /* Create interrupt objects. */ - if ((err = l4_error(tag = l4_factory_create_irq(l4re_global_env->factory, irqcap)))) + err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)) || + l4_error(l4_factory_create_irq(l4re_global_env->factory, irq4cap)); + + if (err) { printf("Could not create IRQ object: %lx\n", err); return 1; } - /* Bind the interrupt object to the IRQ number. */ + /* Bind interrupt objects to IRQ numbers. */ - if ((err = l4_error(l4_icu_bind(icucap, i2c_irq, irqcap)))) + err = l4_error(l4_icu_bind(icucap, + item_in_range(i2c_irq_start, i2c_irq_end, 0), + irq0cap)) || + l4_error(l4_icu_bind(icucap, + item_in_range(i2c_irq_start, i2c_irq_end, 4), + irq4cap)); + + if (err) { - printf("Could not bind IRQ %d to the ICU: %ld\n", i2c_irq, err); + printf("Could not bind IRQ to the ICU: %ld\n", err); return 1; } /* Attach ourselves to the interrupt handler. */ - tag = l4_irq_attach(irqcap, 0xDEAD, l4re_env()->main_thread); + err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)) || + l4_error(l4_rcv_ep_bind_thread(irq4cap, l4re_env()->main_thread, 4)); - if ((err = l4_error(tag))) + if (err) { - printf("Could not attach to IRQ %d: %ld\n", i2c_irq, err); + printf("Could not attach to IRQs: %ld\n", err); return 1; } /* Configure pins. */ + port_d = gpio_base + 0x300; + port_d_end = port_d + 0x100; + port_e = gpio_base + 0x400; + port_e_end = port_e + 0x100; port_f = gpio_base + 0x500; port_f_end = port_f + 0x100; + printf("PORTD at 0x%lx...0x%lx.\n", port_d, port_d_end); + printf("PORTE at 0x%lx...0x%lx.\n", port_e, port_e_end); printf("PORTF at 0x%lx...0x%lx.\n", port_f, port_f_end); - gpio_port_f = jz4780_gpio_init(port_f, port_f_end, 32, 0xffa7f00f, 0x00580ff0); + gpio_port_d = jz4780_gpio_init(port_d, port_d_end, 32, 0xffff4fff, 0x0000b000); + gpio_port_e = jz4780_gpio_init(port_e, port_e_end, 32, 0xfffff37c, 0x00000483); + gpio_port_f = jz4780_gpio_init(port_f, port_f_end, 32, 0x7fa7f00f, 0x00580ff0); printf("Set up GPIO pins...\n"); + jz4780_gpio_config_pad(gpio_port_d, PMSCL, Function_alt, 0); + jz4780_gpio_config_pad(gpio_port_d, PMSDA, Function_alt, 0); + + jz4780_gpio_config_pad(gpio_port_e, RTCSCL, Function_alt, 1); + jz4780_gpio_config_pad(gpio_port_e, RTCSDA, Function_alt, 1); + jz4780_gpio_config_pad(gpio_port_f, DDCSCL, Function_alt, 1); jz4780_gpio_config_pad(gpio_port_f, DDCSDA, Function_alt, 1); @@ -293,56 +374,30 @@ /* Obtain I2C reference. */ i2c = jz4780_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */ - - printf("Trying DDC on I2C4...\n"); - - i2c_channel = jz4780_i2c_get_channel(i2c, 4); + i2c0 = jz4780_i2c_get_channel(i2c, 0); + i2c4 = jz4780_i2c_get_channel(i2c, 4); - /* Attempt to read from address 0x50 for DDC. - See: drivers/video/fbdev/core/fb_ddc.c */ - - jz4780_i2c_set_target(i2c_channel, 0x50); - buf[0] = 0; - jz4780_i2c_write(i2c_channel, buf, 1); - jz4780_i2c_start_read(i2c_channel, buf, 128); - - printf("Waiting\n"); + printf("Scan I2C0...\n"); + i2c_scan(i2c0, irq0cap); - while (!jz4780_i2c_read_done(i2c_channel)) - { - if (jz4780_i2c_read_incomplete(i2c_channel)) - { - printf("Failed\n"); - break; - } + printf("Scan I2C4...\n"); + i2c_scan(i2c4, irq4cap); + + pmic_out3_control(i2c0, irq0cap); - tag = l4_irq_receive(irqcap, L4_IPC_NEVER); - - if ((err = l4_ipc_error(tag, l4_utcb()))) - { - printf("Error on IRQ receive: %ld\n", err); - continue; - } + ddc_read(i2c4, irq4cap); - jz4780_i2c_read(i2c_channel); - } - - pos = jz4780_i2c_have_read(i2c_channel); - show_data(buf, pos); - - /* Attempt to decode EDID information. */ - - libedid_prefered_resolution(buf, &width, &height); - printf("Preferred resolution: %d x %d\n", width, height); - - libedid_dump_standard_timings(buf); + jz4780_i2c_disable(i2c0); + jz4780_i2c_disable(i2c4); /* Detach from the interrupt. */ - tag = l4_irq_detach(irqcap); + err = l4_error(l4_irq_detach(irq0cap)) || l4_error(l4_irq_detach(irq4cap)); - if ((err = l4_error(tag))) - printf("Error detaching from IRQ: %ld\n", err); + if (err) + printf("Error detaching from IRQ objects: %ld\n", err); + + printf("Done.\n"); return 0; }