Landfall

Annotated pkg/landfall-examples/letux400_i2c/letux400_i2c.cc

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