ref: 5e2071caecc32e4c5ff3ee9e0c7ac3cd13bd43c6
parent: ae146ac10b5ae6ed6739add4834862b406debd07
author: theowner <theowner@files>
date: Thu Sep 21 13:57:13 EDT 2023
bcm64: add i2c for pi3
--- /dev/null
+++ b/sys/src/9/bcm64/i2cbcm.c
@@ -1,0 +1,228 @@
+/*
+ * bcm2835 i2c controller
+ *
+ * Only i2c1 is supported.
+ * i2c2 is reserved for HDMI.
+ * i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards)
+ *
+ * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/i2c.h"
+
+#define I2CREGS (VIRTIO+0x804000)
+#define SDA0Pin 2
+#define SCL0Pin 3
+#define Alt0 0x4
+
+typedef struct Ctlr Ctlr;
+typedef struct Bsc Bsc;
+
+/*
+ * Registers for Broadcom Serial Controller (i2c compatible)
+ */
+struct Bsc {
+ u32int ctrl;
+ u32int stat;
+ u32int dlen;
+ u32int addr;
+ u32int fifo;
+ u32int clkdiv; /* default 1500 => 100 KHz assuming 150Mhz input clock */
+ u32int delay; /* default (48<<16)|48 falling:rising edge */
+ u32int clktimeout; /* default 64 */
+};
+
+/*
+ * Per-controller info
+ */
+struct Ctlr {
+ QLock lock;
+ Bsc *regs;
+ Rendez r;
+};
+
+static Ctlr ctlr;
+
+enum {
+ /* ctrl */
+ I2cen = 1<<15, /* I2c enable */
+ Intr = 1<<10, /* interrupt on reception */
+ Intt = 1<<9, /* interrupt on transmission */
+ Intd = 1<<8, /* interrupt on done */
+ Start = 1<<7, /* aka ST, start a transfer */
+ Clear = 1<<4, /* clear fifo */
+ Read = 1<<0, /* read transfer */
+ Write = 0<<0, /* write transfer */
+
+ /* stat */
+ Clkt = 1<<9, /* clock stretch timeout */
+ Err = 1<<8, /* NAK */
+ Rxf = 1<<7, /* RX fifo full */
+ Txe = 1<<6, /* TX fifo full */
+ Rxd = 1<<5, /* RX fifo has data */
+ Txd = 1<<4, /* TX fifo has space */
+ Rxr = 1<<3, /* RX fiio needs reading */
+ Txw = 1<<2, /* TX fifo needs writing */
+ Done = 1<<1, /* transfer done */
+ Ta = 1<<0, /* Transfer active */
+};
+
+static void
+i2cinterrupt(Ureg*, void*)
+{
+ Bsc *r;
+ int st;
+
+ r = ctlr.regs;
+ st = 0;
+ if((r->ctrl & Intr) && (r->stat & Rxd))
+ st |= Intr;
+ if((r->ctrl & Intt) && (r->stat & Txd))
+ st |= Intt;
+ if(r->stat & Done)
+ st |= Intd;
+ if(st){
+ r->ctrl &= ~st;
+ wakeup(&ctlr.r);
+ }
+}
+
+static int
+i2cready(void *st)
+{
+ return (ctlr.regs->stat & (uintptr)st);
+}
+
+static int
+i2cinit(I2Cbus*)
+{
+ ctlr.regs = (Bsc*)I2CREGS;
+ ctlr.regs->clkdiv = 2500;
+
+ gpiosel(SDA0Pin, Alt0);
+ gpiosel(SCL0Pin, Alt0);
+ gpiopullup(SDA0Pin);
+ gpiopullup(SCL0Pin);
+
+ intrenable(IRQi2c, i2cinterrupt, nil, BUSUNKNOWN, "i2c");
+
+ return 0;
+}
+
+/*
+ * Basic I²C driver for Raspberry Pi
+ * subaddressing wasn't reliable, so it is just not allowed
+ *
+ * 10 bit addressing is also disabled.
+ */
+static int
+i2cio(I2Cdev *dev, uchar *pkt, int olen, int ilen)
+{
+ Bsc *r;
+ uchar *p;
+ int st;
+ int o;
+//print("enter io\n");
+// r->ctrl = 0; /* Shutdown ctrl incase it was left in bad state */
+
+/* arguements from Miller's i2cio() */
+ int rw, len;
+ uint addr;
+ o = 0;
+
+ if(dev->subaddr > 0){
+//print("subaddr\n");
+ return -1;
+ }
+
+ if((pkt[0] & 0xF8) == 0xF0){ /* b11110xxx reserved for 10bit addressing*/
+//print("10bit\n");
+ r->ctrl = 0;
+ return -1;
+ }
+
+ rw = pkt[0] & 1; /* rw bit is first bit of pkt[0], read == 1 */
+ addr = dev->addr;
+ pkt++; /* move past device addr packet */
+ o++; /* have to atleast return processing the dev addr */
+
+//print("addr=%ux rw=%d olen=%d ilen=%d\n", addr, rw, olen, ilen);
+/*
+ * If 9Front is just running a probe
+ * return 1,
+ * else the controller throws an NAK error
+ * when doing a write with just the dev addr
+ */
+
+ if((olen == 1) && (ilen == 0)){
+ return 1;
+ }
+
+ qlock(&ctlr.lock);
+ r = ctlr.regs;
+ r->ctrl = I2cen | Clear;
+ r->addr = addr;
+ r->stat = Clkt|Err|Done;
+
+
+ len = (olen - 1) + ilen;
+ r->dlen = len;
+ r->ctrl = I2cen | Start | Intd | rw;
+
+//print("len=%d\n", len);
+
+ p = pkt;
+ st = rw == Read? Rxd : Txd;
+ while(len > 0){
+ while((r->stat & (st|Done)) == 0){
+ r->ctrl |= rw == Read? Intr : Intt;
+ sleep(&ctlr.r, i2cready, (void*)(st|Done));
+ }
+ if(r->stat & (Err|Clkt)){
+ qunlock(&ctlr.lock);
+ //print("error1\n");
+ r->ctrl = 0;
+ return -1;
+ }
+ if(rw == Read){
+ do{
+ *p++ = r->fifo;
+ len--;
+ o++;
+ }while ((r->stat & Rxd) && len > 0);
+ }else{
+ do{
+ r->fifo = *p++;
+ len--;
+ o++;
+ }while((r->stat & Txd) && len > 0);
+ }
+ }
+
+ while((r->stat & Done) == 0)
+ sleep(&ctlr.r, i2cready, (void*)Done);
+ if(r->stat & (Err|Clkt)){
+ qunlock(&ctlr.lock);
+ //print("error2 %ux\n", r->stat);
+ r->ctrl = 0;
+ return -1;
+ }
+ r->ctrl = 0;
+ qunlock(&ctlr.lock);
+ return o;
+}
+
+
+void
+i2cbcmlink(void)
+{
+ static I2Cbus i2c = {"i2c", 400000, &ctlr, i2cinit, i2cio};
+ addi2cbus(&i2c);
+}