Landfall

pkg/landfall-examples/ci20_i2c/ci20_i2c.c

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