Landfall

Annotated pkg/landfall-examples/ci20_i2c/ci20_i2c.c

248:a993c7344862
9 months ago Paul Boddie Fixed TCU clock source. cpm-library-improvements
paul@0 1
/*
paul@0 2
 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>
paul@0 3
 *     economic rights: Technische Universit??t Dresden (Germany)
paul@211 4
 * Copyright (C) 2017, 2018, 2019, 2020, 2023 Paul Boddie <paul@boddie.org.uk>
paul@0 5
 *
paul@0 6
 * This file is part of TUD:OS and distributed under the terms of the
paul@0 7
 * GNU General Public License 2.
paul@0 8
 * Please see the COPYING-GPL-2 file for details.
paul@0 9
 */
paul@0 10
/*
paul@0 11
 * Access the I2C peripherals on the MIPS Creator CI20 board.
paul@0 12
 */
paul@0 13
paul@0 14
#include <l4/devices/cpm-jz4780.h>
paul@0 15
#include <l4/devices/gpio-jz4780.h>
paul@0 16
#include <l4/devices/i2c-jz4780.h>
paul@54 17
#include <l4/devices/memory.h>
paul@0 18
#include <l4/io/io.h>
paul@0 19
#include <l4/re/env.h>
paul@0 20
#include <l4/re/c/util/cap_alloc.h>
paul@0 21
#include <l4/sys/factory.h>
paul@0 22
#include <l4/sys/icu.h>
paul@0 23
#include <l4/sys/ipc.h>
paul@0 24
#include <l4/sys/irq.h>
paul@54 25
#include <l4/sys/rcv_endpoint.h>
paul@0 26
#include <l4/vbus/vbus.h>
paul@0 27
#include <stdio.h>
paul@0 28
#include <unistd.h>
paul@0 29
#include <stdint.h>
paul@56 30
#include <string.h>
paul@0 31
paul@62 32
#define REG(x) *((volatile uint32_t *) (x))
paul@62 33
paul@62 34
#define RTC_RTCCR   0x00
paul@62 35
#define RTC_HCR     0x20
paul@62 36
#define RTC_HSPR    0x34
paul@62 37
#define RTC_WENR    0x3c
paul@62 38
#define RTC_PWRONCR 0x3c
paul@62 39
paul@0 40
paul@0 41
paul@0 42
enum {
paul@54 43
  PMSDA  = 30,  /* via PORTD */
paul@54 44
  PMSCL  = 31,  /* via PORTD */
paul@54 45
  PWM3   = 3,   /* via PORTE */
paul@54 46
  PWM4   = 4,   /* via PORTE */
paul@54 47
  RTCSDA = 12,  /* via PORTE */
paul@54 48
  RTCSCL = 13,  /* via PORTE */
paul@0 49
  DDCSCL = 24,  /* via PORTF */
paul@0 50
  DDCSDA = 25,  /* via PORTF */
paul@0 51
};
paul@0 52
paul@0 53
paul@0 54
paul@0 55
/* Device and resource discovery. */
paul@0 56
paul@0 57
static long item_in_range(long start, long end, long index)
paul@0 58
{
paul@0 59
  if (start < end)
paul@0 60
    return start + index;
paul@0 61
  else
paul@0 62
    return start - index;
paul@0 63
}
paul@0 64
paul@216 65
static int i2c_read(void *i2c_channel, uint8_t *buf, unsigned length,
paul@214 66
                     int stop, l4_cap_idx_t irqcap)
paul@54 67
{
paul@54 68
  l4_msgtag_t tag;
paul@54 69
paul@214 70
  jz4780_i2c_start_read(i2c_channel, buf, length, stop);
paul@54 71
paul@54 72
  while (!jz4780_i2c_read_done(i2c_channel))
paul@54 73
  {
paul@214 74
    tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_from_us(1000)));
paul@54 75
paul@214 76
    if (l4_ipc_error(tag, l4_utcb()))
paul@216 77
      break;
paul@56 78
paul@56 79
    if (jz4780_i2c_failed(i2c_channel))
paul@216 80
      break;
paul@54 81
paul@54 82
    jz4780_i2c_read(i2c_channel);
paul@54 83
  }
paul@54 84
paul@216 85
  if (stop)
paul@216 86
    jz4780_i2c_stop(i2c_channel);
paul@216 87
paul@54 88
  return jz4780_i2c_have_read(i2c_channel);
paul@54 89
}
paul@54 90
paul@216 91
static int i2c_write(void *i2c_channel, uint8_t *buf, unsigned length,
paul@214 92
                      int stop, l4_cap_idx_t irqcap)
paul@56 93
{
paul@56 94
  l4_msgtag_t tag;
paul@56 95
  long err;
paul@56 96
paul@214 97
  jz4780_i2c_start_write(i2c_channel, buf, length, stop);
paul@56 98
paul@56 99
  while (!jz4780_i2c_write_done(i2c_channel))
paul@56 100
  {
paul@214 101
    tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_from_us(1000)));
paul@56 102
paul@56 103
    if ((err = l4_ipc_error(tag, l4_utcb())))
paul@216 104
      break;
paul@56 105
paul@56 106
    if (jz4780_i2c_failed(i2c_channel))
paul@216 107
      break;
paul@56 108
paul@56 109
    jz4780_i2c_write(i2c_channel);
paul@56 110
  }
paul@56 111
paul@216 112
  if (stop)
paul@216 113
    jz4780_i2c_stop(i2c_channel);
paul@216 114
paul@56 115
  return jz4780_i2c_have_written(i2c_channel);
paul@56 116
}
paul@56 117
paul@61 118
static long i2c_get(void *i2c_channel, uint8_t *buffer, long length, l4_cap_idx_t irqcap)
paul@58 119
{
paul@58 120
  long pos;
paul@58 121
paul@61 122
  memset(buffer, 0, length);
paul@214 123
  pos = i2c_read(i2c_channel, buffer, length, 1, irqcap);
paul@61 124
paul@61 125
  return pos;
paul@61 126
}
paul@61 127
paul@61 128
static long i2c_get_reg(void *i2c_channel, uint8_t reg, uint8_t *buffer, long length, l4_cap_idx_t irqcap)
paul@61 129
{
paul@58 130
  buffer[0] = reg;
paul@214 131
  i2c_write(i2c_channel, buffer, 1, 0, irqcap);
paul@58 132
paul@61 133
  return i2c_get(i2c_channel, buffer, length, irqcap);
paul@61 134
}
paul@61 135
paul@61 136
static long i2c_set_reg(void *i2c_channel, uint8_t reg, uint8_t *buffer, long length, l4_cap_idx_t irqcap)
paul@61 137
{
paul@61 138
  uint8_t buf[length + 1];
paul@61 139
  long pos;
paul@61 140
paul@61 141
  buf[0] = reg;
paul@61 142
  memcpy(buf + 1, buffer, length);
paul@214 143
  pos = i2c_write(i2c_channel, buf, length + 1, 1, irqcap);
paul@58 144
paul@58 145
  return pos;
paul@58 146
}
paul@58 147
paul@58 148
static void i2c_report(void *i2c_channel, uint8_t *buffer, long length)
paul@58 149
{
paul@58 150
  if (!jz4780_i2c_failed(i2c_channel))
paul@58 151
  {
paul@58 152
    printf("Read: ");
paul@58 153
    while (length--)
paul@58 154
      printf("%02x", *(buffer++));
paul@58 155
    printf("\n");
paul@58 156
  }
paul@58 157
  else
paul@58 158
    printf("Read failed.\n");
paul@58 159
}
paul@58 160
paul@54 161
static void i2c_scan(void *i2c_channel, l4_cap_idx_t irqcap)
paul@54 162
{
paul@54 163
  uint8_t buf[1];
paul@54 164
  unsigned int address;
paul@54 165
paul@54 166
  for (address = 0; address < 0x20; address++)
paul@54 167
    printf("%02x ", address);
paul@54 168
  printf("\n");
paul@54 169
paul@54 170
  for (address = 0; address < 0x20; address++)
paul@54 171
    printf("-- ");
paul@54 172
paul@54 173
  for (address = 0; address < 0x80; address++)
paul@54 174
  {
paul@54 175
    if ((address % 32) == 0)
paul@54 176
      printf("\n");
paul@54 177
paul@54 178
    jz4780_i2c_set_target(i2c_channel, address);
paul@61 179
    i2c_get(i2c_channel, buf, 1, irqcap);
paul@54 180
paul@56 181
    if (!jz4780_i2c_failed(i2c_channel))
paul@54 182
      printf("%02x ", address);
paul@54 183
    else
paul@54 184
      printf("?? ");
paul@54 185
  }
paul@54 186
paul@54 187
  printf("\n");
paul@54 188
  for (address = 0; address < 0x20; address++)
paul@54 189
    printf("-- ");
paul@54 190
  printf("\n\n");
paul@54 191
}
paul@54 192
paul@61 193
static void pmic_dump(void *i2c_channel, l4_cap_idx_t irqcap)
paul@54 194
{
paul@61 195
  uint8_t regs[] = {0x00, 0x01, 0x10, 0x12, 0x20, 0x22, 0x30, 0x32,
paul@61 196
                    0x40, 0x41, 0x50, 0x51, 0x60, 0x61, 0x70, 0x71,
paul@61 197
                    0x80, 0x81, 0x91};
paul@61 198
  uint8_t buf[1];
paul@56 199
  long pos;
paul@61 200
  unsigned i;
paul@54 201
paul@61 202
  for (i = 0; i < sizeof(regs); i++)
paul@56 203
  {
paul@61 204
    printf("PMIC %02x:\n", regs[i]);
paul@61 205
    jz4780_i2c_set_target(i2c_channel, 0x5a);
paul@61 206
    pos = i2c_get_reg(i2c_channel, regs[i], buf, 1, irqcap);
paul@61 207
    i2c_report(i2c_channel, buf, pos);
paul@56 208
  }
paul@54 209
}
paul@54 210
paul@61 211
static void rtc_dump(void *i2c_channel, l4_cap_idx_t irqcap, uint8_t *rtcregs)
paul@54 212
{
paul@58 213
  /* Attempt to read from address 0x51 for RTC. */
paul@58 214
paul@58 215
  jz4780_i2c_set_target(i2c_channel, 0x51);
paul@61 216
  i2c_get_reg(i2c_channel, 0x00, rtcregs, 16, irqcap);
paul@61 217
  i2c_report(i2c_channel, rtcregs, 16);
paul@61 218
}
paul@61 219
paul@61 220
static void rtc_datetime(uint8_t *rtcregs)
paul@61 221
{
paul@61 222
  char *weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
paul@61 223
  printf("%s %d%d-%d%d-%d%d %d%d:%d%d:%d%d\n",
paul@61 224
    weekdays[rtcregs[0x06] & 0x07],
paul@61 225
    (rtcregs[0x08] & 0xf0) >> 4,
paul@61 226
    rtcregs[0x08] & 0x0f,
paul@61 227
    (rtcregs[0x07] & 0x10) >> 4,
paul@61 228
    rtcregs[0x07] & 0x0f,
paul@61 229
    (rtcregs[0x05] & 0x30) >> 4,
paul@61 230
    rtcregs[0x05] & 0x0f,
paul@61 231
    (rtcregs[0x04] & 0x30) >> 4,
paul@61 232
    rtcregs[0x04] & 0x0f,
paul@61 233
    (rtcregs[0x03] & 0x70) >> 4,
paul@61 234
    rtcregs[0x03] & 0x0f,
paul@61 235
    (rtcregs[0x02] & 0x70) >> 4,
paul@61 236
    rtcregs[0x02] & 0x0f);
paul@61 237
}
paul@61 238
paul@61 239
static int rtc_update(void *i2c_channel, l4_cap_idx_t irqcap, uint8_t *rtcregs)
paul@61 240
{
paul@61 241
  jz4780_i2c_set_target(i2c_channel, 0x51);
paul@61 242
  return i2c_set_reg(i2c_channel, 0x02, rtcregs + 2, 7, irqcap);
paul@54 243
}
paul@54 244
paul@62 245
static void rtc_write_ready(l4_addr_t rtc_base)
paul@62 246
{
paul@62 247
  while (!(REG(rtc_base + RTC_RTCCR) & 0x80));
paul@62 248
}
paul@62 249
paul@62 250
static void rtc_write_enable(l4_addr_t rtc_base)
paul@62 251
{
paul@62 252
  rtc_write_ready(rtc_base);
paul@62 253
  REG(rtc_base + RTC_WENR) = 0xa55a;
paul@62 254
  while (!(REG(rtc_base + RTC_WENR) & 0x80000000));
paul@62 255
}
paul@62 256
paul@0 257
int main(void)
paul@0 258
{
paul@54 259
  long err;
paul@54 260
paul@54 261
  /* Peripheral memory. */
paul@54 262
paul@0 263
  l4_addr_t gpio_base = 0, gpio_base_end = 0;
paul@54 264
  l4_addr_t i2c_base = 0, i2c_base_end = 0;
paul@54 265
  l4_addr_t cpm_base = 0, cpm_base_end = 0;
paul@62 266
  l4_addr_t rtc_base = 0, rtc_base_end = 0;
paul@54 267
  l4_addr_t port_d, port_d_end;
paul@54 268
  l4_addr_t port_e, port_e_end;
paul@54 269
paul@54 270
  /* Peripheral abstractions. */
paul@54 271
paul@61 272
  void *gpio_port_d, *gpio_port_e;
paul@54 273
  void *i2c, *cpm;
paul@54 274
  void *i2c0, *i2c4;
paul@54 275
paul@54 276
  /* IRQ resources. */
paul@54 277
paul@54 278
  l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0;
paul@54 279
  l4_cap_idx_t icucap, irq0cap, irq4cap;
paul@0 280
paul@61 281
  /* RTC registers. */
paul@61 282
paul@61 283
  uint8_t rtcregs[16];
paul@61 284
  int i;
paul@61 285
paul@0 286
  /* Obtain capabilities for the interrupt controller and an interrupt. */
paul@0 287
paul@54 288
  irq0cap = l4re_util_cap_alloc();
paul@54 289
  irq4cap = l4re_util_cap_alloc();
paul@0 290
  icucap = l4re_env_get_cap("icu");
paul@0 291
paul@0 292
  if (l4_is_invalid_cap(icucap))
paul@0 293
  {
paul@0 294
    printf("No 'icu' capability available in the virtual bus.\n");
paul@0 295
    return 1;
paul@0 296
  }
paul@0 297
paul@54 298
  if (l4_is_invalid_cap(irq0cap) || l4_is_invalid_cap(irq4cap))
paul@0 299
  {
paul@54 300
    printf("Capabilities not available for interrupts.\n");
paul@0 301
    return 1;
paul@0 302
  }
paul@0 303
paul@0 304
  /* Obtain resource details describing the interrupt for I2C channel 4. */
paul@0 305
paul@0 306
  printf("Access IRQ...\n");
paul@0 307
paul@54 308
  if (get_irq("jz4780-i2c", &i2c_irq_start, &i2c_irq_end) < 0)
paul@0 309
    return 1;
paul@0 310
paul@0 311
  printf("IRQ range at %d...%d.\n", i2c_irq_start, i2c_irq_end);
paul@0 312
paul@0 313
  /* Obtain resource details describing I/O memory. */
paul@0 314
paul@0 315
  printf("Access GPIO...\n");
paul@0 316
paul@54 317
  if (get_memory("jz4780-gpio", &gpio_base, &gpio_base_end) < 0)
paul@0 318
    return 1;
paul@0 319
paul@0 320
  printf("GPIO at 0x%lx...0x%lx.\n", gpio_base, gpio_base_end);
paul@0 321
paul@0 322
  printf("Access CPM...\n");
paul@0 323
paul@54 324
  if (get_memory("jz4780-cpm", &cpm_base, &cpm_base_end) < 0)
paul@0 325
    return 1;
paul@0 326
paul@0 327
  printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end);
paul@0 328
paul@0 329
  printf("Access I2C...\n");
paul@0 330
paul@54 331
  if (get_memory("jz4780-i2c", &i2c_base, &i2c_base_end) < 0)
paul@0 332
    return 1;
paul@0 333
paul@0 334
  printf("I2C at 0x%lx...0x%lx.\n", i2c_base, i2c_base_end);
paul@0 335
paul@62 336
  printf("Access RTC...\n");
paul@62 337
paul@62 338
  if (get_memory("jz4780-rtc", &rtc_base, &rtc_base_end) < 0)
paul@62 339
    return 1;
paul@62 340
paul@62 341
  printf("RTC at 0x%lx...0x%lx.\n", rtc_base, rtc_base_end);
paul@62 342
paul@54 343
  /* Create interrupt objects. */
paul@0 344
paul@54 345
  err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)) ||
paul@54 346
        l4_error(l4_factory_create_irq(l4re_global_env->factory, irq4cap));
paul@54 347
paul@54 348
  if (err)
paul@0 349
  {
paul@0 350
    printf("Could not create IRQ object: %lx\n", err);
paul@0 351
    return 1;
paul@0 352
  }
paul@0 353
paul@54 354
  /* Bind interrupt objects to IRQ numbers. */
paul@0 355
paul@54 356
  err = l4_error(l4_icu_bind(icucap,
paul@54 357
                             item_in_range(i2c_irq_start, i2c_irq_end, 0),
paul@54 358
                             irq0cap)) ||
paul@54 359
        l4_error(l4_icu_bind(icucap,
paul@54 360
                             item_in_range(i2c_irq_start, i2c_irq_end, 4),
paul@54 361
                             irq4cap));
paul@54 362
paul@54 363
  if (err)
paul@0 364
  {
paul@54 365
    printf("Could not bind IRQ to the ICU: %ld\n", err);
paul@0 366
    return 1;
paul@0 367
  }
paul@0 368
paul@0 369
  /* Attach ourselves to the interrupt handler. */
paul@0 370
paul@54 371
  err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)) ||
paul@54 372
        l4_error(l4_rcv_ep_bind_thread(irq4cap, l4re_env()->main_thread, 4));
paul@0 373
paul@54 374
  if (err)
paul@0 375
  {
paul@54 376
    printf("Could not attach to IRQs: %ld\n", err);
paul@0 377
    return 1;
paul@0 378
  }
paul@0 379
paul@0 380
  /* Configure pins. */
paul@0 381
paul@54 382
  port_d = gpio_base + 0x300;
paul@54 383
  port_d_end = port_d + 0x100;
paul@54 384
  port_e = gpio_base + 0x400;
paul@54 385
  port_e_end = port_e + 0x100;
paul@0 386
paul@54 387
  printf("PORTD at 0x%lx...0x%lx.\n", port_d, port_d_end);
paul@54 388
  printf("PORTE at 0x%lx...0x%lx.\n", port_e, port_e_end);
paul@0 389
paul@54 390
  gpio_port_d = jz4780_gpio_init(port_d, port_d_end, 32, 0xffff4fff, 0x0000b000);
paul@54 391
  gpio_port_e = jz4780_gpio_init(port_e, port_e_end, 32, 0xfffff37c, 0x00000483);
paul@0 392
paul@0 393
  printf("Set up GPIO pins...\n");
paul@0 394
paul@54 395
  jz4780_gpio_config_pad(gpio_port_d, PMSCL, Function_alt, 0);
paul@54 396
  jz4780_gpio_config_pad(gpio_port_d, PMSDA, Function_alt, 0);
paul@54 397
paul@61 398
  jz4780_gpio_config_pad(gpio_port_e, RTCSCL, Function_alt, 1);
paul@61 399
  jz4780_gpio_config_pad(gpio_port_e, RTCSDA, Function_alt, 1);
paul@0 400
paul@0 401
  /* Obtain CPM and I2C objects. */
paul@0 402
paul@0 403
  cpm = jz4780_cpm_init(cpm_base);
paul@0 404
paul@0 405
  /* Attempt to set the PCLK source to SCLK_A. */
paul@0 406
paul@211 407
  jz4780_cpm_set_source_clock(cpm, Clock_pclock, Clock_main);
paul@213 408
  printf("Peripheral clock: %lld\n", jz4780_cpm_get_frequency(cpm, Clock_pclock));
paul@0 409
paul@0 410
  /* Obtain I2C reference. */
paul@0 411
paul@0 412
  i2c = jz4780_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */
paul@54 413
  i2c0 = jz4780_i2c_get_channel(i2c, 0);
paul@54 414
  i2c4 = jz4780_i2c_get_channel(i2c, 4);
paul@0 415
paul@54 416
  printf("Scan I2C0...\n");
paul@54 417
  i2c_scan(i2c0, irq0cap);
paul@0 418
paul@54 419
  printf("Scan I2C4...\n");
paul@54 420
  i2c_scan(i2c4, irq4cap);
paul@54 421
paul@61 422
  printf("PMIC...\n");
paul@61 423
  pmic_dump(i2c0, irq0cap);
paul@61 424
paul@61 425
  printf("RTC...\n");
paul@61 426
  rtc_dump(i2c4, irq4cap, rtcregs);
paul@0 427
paul@61 428
  rtcregs[0x02] = 0x56;
paul@61 429
  rtcregs[0x03] = 0x34;
paul@61 430
  rtcregs[0x04] = 0x12;
paul@61 431
  rtcregs[0x05] = 0x25; /* 25th */
paul@61 432
  rtcregs[0x06] = 0x06; /* Saturday */
paul@61 433
  rtcregs[0x07] = 0x01; /* January */
paul@61 434
  rtcregs[0x08] = 0x20; /* 2020 */
paul@0 435
paul@61 436
  printf("Updated %d registers.\n", rtc_update(i2c4, irq4cap, rtcregs));
paul@61 437
paul@62 438
  for (i = 0; i < 3; i++)
paul@61 439
  {
paul@61 440
    rtc_dump(i2c4, irq4cap, rtcregs);
paul@61 441
    rtc_datetime(rtcregs);
paul@61 442
    sleep(1);
paul@61 443
  }
paul@58 444
paul@62 445
  /* Investigate the internal RTC registers. */
paul@62 446
paul@62 447
  printf("Control: %08x\n", REG(rtc_base + RTC_RTCCR));
paul@62 448
  printf("Hibernate: %08x\n", REG(rtc_base + RTC_HCR));
paul@62 449
  printf("Scratchpad: %08x\n", REG(rtc_base + RTC_HSPR));
paul@62 450
  printf("Power on control: %08x\n", REG(rtc_base + RTC_PWRONCR));
paul@62 451
  printf("Write enable: %08x\n", REG(rtc_base + RTC_WENR));
paul@62 452
paul@62 453
  rtc_write_enable(rtc_base);
paul@62 454
  rtc_write_ready(rtc_base);
paul@62 455
  REG(rtc_base + RTC_HSPR) = 0x12345678;
paul@62 456
paul@62 457
  for (i = 0; i < 3; i++)
paul@62 458
  {
paul@62 459
    printf("Scratchpad: %08x\n", REG(rtc_base + RTC_HSPR));
paul@62 460
    sleep(1);
paul@62 461
  }
paul@62 462
paul@214 463
  //jz4780_i2c_disable(i2c0);
paul@214 464
  //jz4780_i2c_disable(i2c4);
paul@0 465
paul@0 466
  /* Detach from the interrupt. */
paul@0 467
paul@54 468
  err = l4_error(l4_irq_detach(irq0cap)) || l4_error(l4_irq_detach(irq4cap));
paul@0 469
paul@54 470
  if (err)
paul@54 471
    printf("Error detaching from IRQ objects: %ld\n", err);
paul@54 472
paul@54 473
  printf("Done.\n");
paul@0 474
paul@0 475
  return 0;
paul@0 476
}