1 /* 2 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de> 3 * economic rights: Technische Universit??t Dresden (Germany) 4 * Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This file is part of TUD:OS and distributed under the terms of the 7 * GNU General Public License 2. 8 * Please see the COPYING-GPL-2 file for details. 9 */ 10 /* 11 * Access the I2C peripherals on the MIPS Creator CI20 board. 12 */ 13 14 #include <l4/devices/cpm-jz4780.h> 15 #include <l4/devices/gpio-jz4780.h> 16 #include <l4/devices/i2c-jz4780.h> 17 #include <l4/io/io.h> 18 #include <l4/libedid/edid.h> 19 #include <l4/re/env.h> 20 #include <l4/re/c/util/cap_alloc.h> 21 #include <l4/sys/factory.h> 22 #include <l4/sys/icu.h> 23 #include <l4/sys/ipc.h> 24 #include <l4/sys/irq.h> 25 #include <l4/vbus/vbus.h> 26 #include <stdio.h> 27 #include <unistd.h> 28 #include <stdint.h> 29 30 31 32 enum { 33 DDCSCL = 24, /* via PORTF */ 34 DDCSDA = 25, /* via PORTF */ 35 }; 36 37 38 39 /* Device and resource discovery. */ 40 41 static char const *resource_type(enum l4io_resource_types_t type) 42 { 43 switch (type) 44 { 45 case L4VBUS_RESOURCE_INVALID: 46 return "INVALID"; 47 48 case L4VBUS_RESOURCE_IRQ: 49 return "IRQ"; 50 51 case L4VBUS_RESOURCE_MEM: 52 return "MEMORY"; 53 54 default: 55 return "OTHER"; 56 } 57 } 58 59 static int vbus_get_device(char const *hid, l4io_device_handle_t *dh, l4io_resource_handle_t *rh) 60 { 61 int result = l4io_lookup_device(hid, dh, 0, rh); 62 63 if (result < 0) 64 printf("Could not access '%s': %s\n", hid, result == -L4_ENOENT ? "no such device" : "no device"); 65 66 return result; 67 } 68 69 static int vbus_get_resource(l4io_device_handle_t dh, l4io_resource_t *res, 70 enum l4io_resource_types_t type) 71 { 72 int current = 0, result = 0; 73 l4_cap_idx_t vbus = l4re_env_get_cap("vbus"); 74 75 do 76 { 77 result = l4vbus_get_resource(vbus, dh, current, res); 78 79 if (result) 80 printf("Could not access resource of type %s.\n", resource_type(type)); 81 else 82 printf("Resource %d: type %s, start=%lx, end=%lx\n", res->id, 83 resource_type(res->type), res->start, res->end); 84 85 current++; 86 } 87 while ((!result) && (res->type != type)); 88 89 return result; 90 } 91 92 static int vbus_get_irq(char const *hid, l4_uint32_t *start, l4_uint32_t *end) 93 { 94 l4io_device_handle_t dh; 95 l4io_resource_handle_t rh; 96 l4io_resource_t res; 97 int result; 98 99 result = vbus_get_device(hid, &dh, &rh); 100 101 if (result < 0) 102 return result; 103 104 result = vbus_get_resource(dh, &res, L4IO_RESOURCE_IRQ); 105 106 if (result) 107 return result; 108 109 *start = res.start; 110 *end = res.end; 111 112 return result; 113 } 114 115 static int vbus_get_memory(char const *hid, l4_addr_t *start, l4_addr_t *end) 116 { 117 l4io_device_handle_t dh; 118 l4io_resource_handle_t rh; 119 l4io_resource_t res; 120 int result; 121 122 result = vbus_get_device(hid, &dh, &rh); 123 124 if (result < 0) 125 return result; 126 127 result = vbus_get_resource(dh, &res, L4IO_RESOURCE_MEM); 128 129 if (result) 130 return result; 131 132 if ((result = l4io_request_iomem(res.start, res.end - res.start + 1, 133 L4IO_MEM_NONCACHED, start))) 134 { 135 printf("Could not get address for '%s'.\n", hid); 136 return result; 137 } 138 139 printf("Resource at 0x%lx...0x%lx.\n", res.start, res.end); 140 141 *end = *start + (res.end - res.start + 1); 142 143 return 0; 144 } 145 146 static long item_in_range(long start, long end, long index) 147 { 148 if (start < end) 149 return start + index; 150 else 151 return start - index; 152 } 153 154 static void show_deviceid(uint8_t buf[]) 155 { 156 printf("Manufacturer %x part %x revision %x\n", 157 (buf[0] << 4) | (buf[1] >> 4), 158 ((buf[1] & 0xf) << 5) | (buf[2] >> 3), 159 buf[2] & 0x7); 160 } 161 162 static void show_data(uint8_t buf[], unsigned pos) 163 { 164 unsigned i; 165 166 if (pos) 167 { 168 printf("Read %d bytes from bus.\n", pos); 169 for (i = 0; i < pos; i++) 170 printf(" %02x", buf[i]); 171 printf("\n"); 172 } 173 else 174 printf("No reply from bus.\n"); 175 } 176 177 int main(void) 178 { 179 l4_addr_t gpio_base = 0, gpio_base_end = 0; 180 l4_addr_t port_f, port_f_end; 181 l4_addr_t i2c_base = 0, i2c_base_end = 0, cpm_base = 0, cpm_base_end; 182 void *gpio_port_f; 183 void *i2c, *i2c_channel, *cpm; 184 l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0, i2c_irq; 185 l4_cap_idx_t irqcap, icucap; 186 l4_msgtag_t tag; 187 long err; 188 int result = 0; 189 uint8_t buf[256]; 190 unsigned pos; 191 unsigned width = 0, height = 0; 192 193 /* Obtain capabilities for the interrupt controller and an interrupt. */ 194 195 irqcap = l4re_util_cap_alloc(); 196 icucap = l4re_env_get_cap("icu"); 197 198 if (l4_is_invalid_cap(icucap)) 199 { 200 printf("No 'icu' capability available in the virtual bus.\n"); 201 return 1; 202 } 203 204 if (l4_is_invalid_cap(irqcap)) 205 { 206 printf("No capability available for the interrupt.\n"); 207 return 1; 208 } 209 210 /* Obtain resource details describing the interrupt for I2C channel 4. */ 211 212 printf("Access IRQ...\n"); 213 214 if ((result = vbus_get_irq("jz4780-i2c", &i2c_irq_start, &i2c_irq_end)) < 0) 215 return 1; 216 217 i2c_irq = item_in_range(i2c_irq_start, i2c_irq_end, 4); 218 printf("IRQ range at %d...%d.\n", i2c_irq_start, i2c_irq_end); 219 printf("I2C IRQ at %d.\n", i2c_irq); 220 221 /* Obtain resource details describing I/O memory. */ 222 223 printf("Access GPIO...\n"); 224 225 if ((result = vbus_get_memory("jz4780-gpio", &gpio_base, &gpio_base_end)) < 0) 226 return 1; 227 228 printf("GPIO at 0x%lx...0x%lx.\n", gpio_base, gpio_base_end); 229 230 printf("Access CPM...\n"); 231 232 if ((result = vbus_get_memory("jz4780-cpm", &cpm_base, &cpm_base_end)) < 0) 233 return 1; 234 235 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 236 237 printf("Access I2C...\n"); 238 239 if ((result = vbus_get_memory("jz4780-i2c", &i2c_base, &i2c_base_end)) < 0) 240 return 1; 241 242 printf("I2C at 0x%lx...0x%lx.\n", i2c_base, i2c_base_end); 243 244 /* Create an interrupt object. */ 245 246 if ((err = l4_error(tag = l4_factory_create_irq(l4re_global_env->factory, irqcap)))) 247 { 248 printf("Could not create IRQ object: %lx\n", err); 249 return 1; 250 } 251 252 /* Bind the interrupt object to the IRQ number. */ 253 254 if ((err = l4_error(l4_icu_bind(icucap, i2c_irq, irqcap)))) 255 { 256 printf("Could not bind IRQ %d to the ICU: %ld\n", i2c_irq, err); 257 return 1; 258 } 259 260 /* Attach ourselves to the interrupt handler. */ 261 262 tag = l4_irq_attach(irqcap, 0xDEAD, l4re_env()->main_thread); 263 264 if ((err = l4_error(tag))) 265 { 266 printf("Could not attach to IRQ %d: %ld\n", i2c_irq, err); 267 return 1; 268 } 269 270 /* Configure pins. */ 271 272 port_f = gpio_base + 0x500; 273 port_f_end = port_f + 0x100; 274 275 printf("PORTF at 0x%lx...0x%lx.\n", port_f, port_f_end); 276 277 gpio_port_f = jz4780_gpio_init(port_f, port_f_end, 32, 0xffa7f00f, 0x00580ff0); 278 279 printf("Set up GPIO pins...\n"); 280 281 jz4780_gpio_config_pad(gpio_port_f, DDCSCL, Function_alt, 1); 282 jz4780_gpio_config_pad(gpio_port_f, DDCSDA, Function_alt, 1); 283 284 /* Obtain CPM and I2C objects. */ 285 286 cpm = jz4780_cpm_init(cpm_base); 287 288 /* Attempt to set the PCLK source to SCLK_A. */ 289 290 jz4780_cpm_set_pclock_source(cpm, 1); 291 printf("Peripheral clock: %d\n", jz4780_cpm_get_pclock_frequency(cpm)); 292 293 /* Obtain I2C reference. */ 294 295 i2c = jz4780_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */ 296 297 printf("Trying DDC on I2C4...\n"); 298 299 i2c_channel = jz4780_i2c_get_channel(i2c, 4); 300 301 /* Attempt to read from address 0x50 for DDC. 302 See: drivers/video/fbdev/core/fb_ddc.c */ 303 304 jz4780_i2c_set_target(i2c_channel, 0x50); 305 buf[0] = 0; 306 jz4780_i2c_write(i2c_channel, buf, 1); 307 jz4780_i2c_start_read(i2c_channel, buf, 128); 308 309 printf("Waiting\n"); 310 311 while (!jz4780_i2c_read_done(i2c_channel)) 312 { 313 if (jz4780_i2c_read_incomplete(i2c_channel)) 314 { 315 printf("Failed\n"); 316 break; 317 } 318 319 tag = l4_irq_receive(irqcap, L4_IPC_NEVER); 320 321 if ((err = l4_ipc_error(tag, l4_utcb()))) 322 { 323 printf("Error on IRQ receive: %ld\n", err); 324 continue; 325 } 326 327 jz4780_i2c_read(i2c_channel); 328 } 329 330 pos = jz4780_i2c_have_read(i2c_channel); 331 show_data(buf, pos); 332 333 /* Attempt to decode EDID information. */ 334 335 libedid_prefered_resolution(buf, &width, &height); 336 printf("Preferred resolution: %d x %d\n", width, height); 337 338 libedid_dump_standard_timings(buf); 339 340 /* Detach from the interrupt. */ 341 342 tag = l4_irq_detach(irqcap); 343 344 if ((err = l4_error(tag))) 345 printf("Error detaching from IRQ: %ld\n", err); 346 347 return 0; 348 }