1 /* 2 * Access peripherals on the I2C bus. 3 * 4 * Copyright (C) 2018, 2020 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/cpm-jz4730.h> 23 #include <l4/devices/i2c-jz4730.h> 24 #include <l4/devices/memory.h> 25 26 #include <l4/re/c/util/cap_alloc.h> 27 #include <l4/sys/factory.h> 28 #include <l4/sys/icu.h> 29 #include <l4/sys/irq.h> 30 #include <l4/sys/rcv_endpoint.h> 31 32 #include <stdio.h> 33 #include <unistd.h> 34 35 36 37 /* Device and resource discovery. */ 38 39 static long item_in_range(long start, long end, long index) 40 { 41 if (start < end) 42 return start + index; 43 else 44 return start - index; 45 } 46 47 /* Scan the I2C bus by performing speculative reads from each device address. */ 48 49 static void i2c_scan(void *i2c_channel) 50 { 51 uint8_t buf[1]; 52 unsigned int address; 53 54 for (address = 0; address < 0x20; address++) 55 printf("%02x ", address); 56 printf("\n"); 57 58 for (address = 0; address < 0x20; address++) 59 printf("-- "); 60 61 for (address = 0; address < 0x80; address++) 62 { 63 if ((address % 32) == 0) 64 printf("\n"); 65 66 if (jz4730_i2c_read(i2c_channel, address, buf, 1)) 67 printf("%02x ", address); 68 else 69 printf("?? "); 70 } 71 72 printf("\n"); 73 for (address = 0; address < 0x20; address++) 74 printf("-- "); 75 printf("\n\n"); 76 } 77 78 /* Interpret RTC registers. */ 79 80 static void rtc_datetime(uint8_t *rtcregs) 81 { 82 const char *weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 83 84 printf("%s %d%d-%d%d-%d%d %d%d:%d%d:%d%d\n", 85 weekdays[rtcregs[0x06] & 0x07], 86 (rtcregs[0x08] & 0xf0) >> 4, 87 rtcregs[0x08] & 0x0f, 88 (rtcregs[0x07] & 0x10) >> 4, 89 rtcregs[0x07] & 0x0f, 90 (rtcregs[0x05] & 0x30) >> 4, 91 rtcregs[0x05] & 0x0f, 92 (rtcregs[0x04] & 0x30) >> 4, 93 rtcregs[0x04] & 0x0f, 94 (rtcregs[0x03] & 0x70) >> 4, 95 rtcregs[0x03] & 0x0f, 96 (rtcregs[0x02] & 0x70) >> 4, 97 rtcregs[0x02] & 0x0f); 98 } 99 100 101 102 int main(void) 103 { 104 void *cpm; 105 void *i2c, *i2c0; 106 107 /* Interrupts. */ 108 109 l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0; 110 l4_cap_idx_t icucap, irq0cap; 111 112 /* Obtain resource details describing the interrupt for I2C channel 0. */ 113 114 printf("Access IRQ...\n"); 115 116 if (get_irq("jz4730-i2c", &i2c_irq_start, &i2c_irq_end) < 0) 117 return 1; 118 119 printf("IRQ range at %d...%d.\n", i2c_irq_start, i2c_irq_end); 120 121 /* Obtain capabilities for the interrupt controller and an interrupt. */ 122 123 irq0cap = l4re_util_cap_alloc(); 124 icucap = l4re_env_get_cap("icu"); 125 126 if (l4_is_invalid_cap(icucap)) 127 { 128 printf("No 'icu' capability available in the virtual bus.\n"); 129 return 1; 130 } 131 132 if (l4_is_invalid_cap(irq0cap)) 133 { 134 printf("Capabilities not available for interrupts.\n"); 135 return 1; 136 } 137 138 /* Create interrupt objects. */ 139 140 long err; 141 142 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); 143 144 if (err) 145 { 146 printf("Could not create IRQ object: %lx\n", err); 147 return 1; 148 } 149 150 /* Bind interrupt objects to IRQ numbers. */ 151 152 err = l4_error(l4_icu_bind(icucap, 153 item_in_range(i2c_irq_start, i2c_irq_end, 0), 154 irq0cap)); 155 156 if (err) 157 { 158 printf("Could not bind IRQ to the ICU: %ld\n", err); 159 return 1; 160 } 161 162 /* Attach ourselves to the interrupt handler. */ 163 164 err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); 165 166 if (err) 167 { 168 printf("Could not attach to IRQs: %ld\n", err); 169 return 1; 170 } 171 172 /* Peripheral memory. */ 173 174 l4_addr_t cpm_base = 0, cpm_base_end = 0; 175 l4_addr_t i2c_base = 0, i2c_base_end = 0; 176 177 /* Obtain resource details describing I/O memory. */ 178 179 printf("Access CPM...\n"); 180 181 if (get_memory("jz4730-cpm", &cpm_base, &cpm_base_end) < 0) 182 return 1; 183 184 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 185 186 printf("Access I2C...\n"); 187 188 if (get_memory("jz4730-i2c", &i2c_base, &i2c_base_end) < 0) 189 return 1; 190 191 printf("I2C at 0x%lx...0x%lx.\n", i2c_base, i2c_base_end); 192 193 /* Obtain CPM and I2C references. */ 194 195 cpm = jz4730_cpm_init(cpm_base); 196 i2c = jz4730_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */ 197 i2c0 = jz4730_i2c_get_channel(i2c, 0, irq0cap); 198 199 /* Enable I2C. */ 200 201 jz4730_i2c_enable(i2c0); 202 203 uint8_t buf[32]; 204 unsigned int nwritten; 205 206 /* Set the clock: 12:34:56 on Saturday 2nd January 2021. */ 207 208 buf[0] = 2; buf[1] = 0x56; buf[2] = 0x34; buf[3] = 0x12; buf[4] = 0x02; buf[5] = 0x06; buf[6] = 0x01; buf[7] = 0x21; 209 nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 8); 210 211 printf("Written: %d\n", nwritten); 212 213 for (int i = 0; i < 3; i++) 214 { 215 /* Issue selection of device register 0. */ 216 217 buf[0] = 0; 218 nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 1); 219 220 printf("Written: %d\n", nwritten); 221 222 /* Read the contents of 16 registers. */ 223 224 unsigned int nread = jz4730_i2c_read(i2c0, 0x51, buf, 16); 225 226 printf("Read: %d\n", nread); 227 for (unsigned int i = 0; i < nread; i++) 228 printf("%02x ", buf[i]); 229 printf("\n"); 230 231 /* Show the interpreted date and time. */ 232 233 rtc_datetime(buf); 234 235 /* Read from the power controller at 0x28. */ 236 237 buf[0] = 0xdb; 238 nwritten = jz4730_i2c_write(i2c0, 0x28, buf, 1); 239 printf("Written: %d\n", nwritten); 240 nread = jz4730_i2c_read(i2c0, 0x28, buf, 1); 241 printf("Read: %d\n", nread); 242 243 printf("Voltage level: %1.3f\n", buf[0] * 36235 / 1000000.0); 244 245 buf[0] = 0xd9; 246 nwritten = jz4730_i2c_write(i2c0, 0x28, buf, 1); 247 printf("Written: %d\n", nwritten); 248 nread = jz4730_i2c_read(i2c0, 0x28, buf, 1); 249 printf("Read: %d\n", nread); 250 251 printf("Charger connected: %s\n", buf[0] & 1 ? "yes" : "no"); 252 253 /* Scan the bus. */ 254 255 printf("Scan I2C0...\n"); 256 i2c_scan(i2c0); 257 258 sleep(5); 259 } 260 261 /* Issue a shutdown request. */ 262 263 buf[0] = 0xd8; buf[1] = 1; 264 nwritten = jz4730_i2c_write(i2c0, 0x28, buf, 2); 265 printf("Written: %d\n", nwritten); 266 267 /* Detach from the interrupt. */ 268 269 err = l4_error(l4_irq_detach(irq0cap)); 270 271 if (err) 272 printf("Error detaching from IRQ objects: %ld\n", err); 273 274 return 0; 275 }