/* * This file implements support for i2c on the BeagleBone and BeagleBoard-xM */ /* kernel headers */ #include #include #include #include #include #include #include #include #include #include #include /* device headers */ #include /* system headers */ #include #include #include /* usr headers */ #include #include #include /* local headers */ #include "omap_i2c.h" /* * defines the set of register * * Warning: always use the 16-bit variants of read/write/set from mmio.h * to access these registers. The DM37XX TRM Section 17.6 warns that 32-bit * accesses can corrupt the register contents. */ typedef struct omap_i2c_registers { vir_bytes I2C_REVNB_LO; /* AM335X Only */ vir_bytes I2C_REVNB_HI; /* AM335X Only */ vir_bytes I2C_REV; /* DM37XX Only */ vir_bytes I2C_IE; /* DM37XX Only */ vir_bytes I2C_STAT; /* DM37XX Only */ vir_bytes I2C_SYSC; vir_bytes I2C_IRQSTATUS_RAW; /* AM335X Only */ vir_bytes I2C_IRQSTATUS; /* AM335X Only */ vir_bytes I2C_IRQENABLE_SET; /* AM335X Only */ vir_bytes I2C_IRQENABLE_CLR; /* AM335X Only */ vir_bytes I2C_WE; vir_bytes I2C_DMARXENABLE_SET; /* AM335X Only */ vir_bytes I2C_DMATXENABLE_SET; /* AM335X Only */ vir_bytes I2C_DMARXENABLE_CLR; /* AM335X Only */ vir_bytes I2C_DMATXENABLE_CLR; /* AM335X Only */ vir_bytes I2C_DMARXWAKE_EN; /* AM335X Only */ vir_bytes I2C_DMATXWAKE_EN; /* AM335X Only */ vir_bytes I2C_SYSS; vir_bytes I2C_BUF; vir_bytes I2C_CNT; vir_bytes I2C_DATA; vir_bytes I2C_CON; vir_bytes I2C_OA; /* AM335X Only */ vir_bytes I2C_OA0; /* DM37XX Only */ vir_bytes I2C_SA; vir_bytes I2C_PSC; vir_bytes I2C_SCLL; vir_bytes I2C_SCLH; vir_bytes I2C_SYSTEST; vir_bytes I2C_BUFSTAT; vir_bytes I2C_OA1; vir_bytes I2C_OA2; vir_bytes I2C_OA3; vir_bytes I2C_ACTOA; vir_bytes I2C_SBLOCK; } omap_i2c_regs_t; /* generic definition an i2c bus */ typedef struct omap_i2c_bus { enum bus_types { AM335X_I2C_BUS, DM37XX_I2C_BUS} bus_type; phys_bytes mr_base; phys_bytes mr_size; vir_bytes mapped_addr; omap_i2c_regs_t *regs; uint32_t functional_clock; uint32_t module_clock; uint32_t bus_speed; uint16_t major; uint16_t minor; int irq; int irq_hook_id; int irq_hook_kernel_id; } omap_i2c_bus_t; /* Define the registers for each chip */ static omap_i2c_regs_t am335x_i2c_regs = { .I2C_REVNB_LO = AM335X_I2C_REVNB_LO, .I2C_REVNB_HI = AM335X_I2C_REVNB_HI, .I2C_SYSC = AM335X_I2C_SYSC, .I2C_IRQSTATUS_RAW = AM335X_I2C_IRQSTATUS_RAW, .I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS, .I2C_IRQENABLE_SET = AM335X_I2C_IRQENABLE_SET, .I2C_IRQENABLE_CLR = AM335X_I2C_IRQENABLE_CLR, .I2C_WE = AM335X_I2C_WE, .I2C_DMARXENABLE_SET = AM335X_I2C_DMARXENABLE_SET, .I2C_DMATXENABLE_SET = AM335X_I2C_DMATXENABLE_SET, .I2C_DMARXENABLE_CLR = AM335X_I2C_DMARXENABLE_CLR, .I2C_DMATXENABLE_CLR = AM335X_I2C_DMATXENABLE_CLR, .I2C_DMARXWAKE_EN = AM335X_I2C_DMARXWAKE_EN, .I2C_DMATXWAKE_EN = AM335X_I2C_DMATXWAKE_EN, .I2C_SYSS = AM335X_I2C_SYSS, .I2C_BUF = AM335X_I2C_BUF, .I2C_CNT = AM335X_I2C_CNT, .I2C_DATA = AM335X_I2C_DATA, .I2C_CON = AM335X_I2C_CON, .I2C_OA = AM335X_I2C_OA, .I2C_SA = AM335X_I2C_SA, .I2C_PSC = AM335X_I2C_PSC, .I2C_SCLL = AM335X_I2C_SCLL, .I2C_SCLH = AM335X_I2C_SCLH, .I2C_SYSTEST = AM335X_I2C_SYSTEST, .I2C_BUFSTAT = AM335X_I2C_BUFSTAT, .I2C_OA1 = AM335X_I2C_OA1, .I2C_OA2 = AM335X_I2C_OA2, .I2C_OA3 = AM335X_I2C_OA3, .I2C_ACTOA = AM335X_I2C_ACTOA, .I2C_SBLOCK = AM335X_I2C_SBLOCK }; static omap_i2c_regs_t dm37xx_i2c_regs = { .I2C_REV = DM37XX_I2C_REV, .I2C_IE = DM37XX_I2C_IE, .I2C_STAT = DM37XX_I2C_STAT, .I2C_WE = DM37XX_I2C_WE, .I2C_SYSS = DM37XX_I2C_SYSS, .I2C_BUF = DM37XX_I2C_BUF, .I2C_CNT = DM37XX_I2C_CNT, .I2C_DATA = DM37XX_I2C_DATA, .I2C_SYSC = DM37XX_I2C_SYSC, .I2C_CON = DM37XX_I2C_CON, .I2C_OA0 = DM37XX_I2C_OA0, .I2C_SA = DM37XX_I2C_SA, .I2C_PSC = DM37XX_I2C_PSC, .I2C_SCLL = DM37XX_I2C_SCLL, .I2C_SCLH = DM37XX_I2C_SCLH, .I2C_SYSTEST = DM37XX_I2C_SYSTEST, .I2C_BUFSTAT = DM37XX_I2C_BUFSTAT, .I2C_OA1 = DM37XX_I2C_OA1, .I2C_OA2 = DM37XX_I2C_OA2, .I2C_OA3 = DM37XX_I2C_OA3, .I2C_ACTOA = DM37XX_I2C_ACTOA, .I2C_SBLOCK = DM37XX_I2C_SBLOCK }; /* Define the buses available on each chip */ static omap_i2c_bus_t am335x_i2c_buses[] = { {AM335X_I2C_BUS, AM335X_I2C0_BASE, AM335X_I2C0_SIZE, 0, &am335x_i2c_regs, AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, BUS_SPEED_400KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, AM335X_I2C0_IRQ, 1, 1}, {AM335X_I2C_BUS, AM335X_I2C1_BASE, AM335X_I2C1_SIZE, 0, &am335x_i2c_regs, AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, AM335X_I2C1_IRQ, 2, 3}, {AM335X_I2C_BUS, AM335X_I2C2_BASE, AM335X_I2C2_SIZE, 0, &am335x_i2c_regs, AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, AM335X_I2C2_IRQ, 3, 3} }; #define AM335X_OMAP_NBUSES (sizeof(am335x_i2c_buses) / sizeof(omap_i2c_bus_t)) static omap_i2c_bus_t dm37xx_i2c_buses[] = { {DM37XX_I2C_BUS, DM37XX_I2C0_BASE, DM37XX_I2C0_SIZE, 0, &dm37xx_i2c_regs, DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, DM37XX_I2C0_IRQ, 1, 1}, {DM37XX_I2C_BUS, DM37XX_I2C1_BASE, DM37XX_I2C1_SIZE, 0, &dm37xx_i2c_regs, DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, DM37XX_I2C1_IRQ, 2, 2}, {DM37XX_I2C_BUS, DM37XX_I2C2_BASE, DM37XX_I2C2_SIZE, 0, &dm37xx_i2c_regs, DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, DM37XX_I2C2_IRQ, 3, 3} }; #define DM37XX_OMAP_NBUSES (sizeof(dm37xx_i2c_buses) / sizeof(omap_i2c_bus_t)) /* Globals */ static omap_i2c_bus_t *omap_i2c_buses; /* all available buses for this SoC */ static omap_i2c_bus_t *omap_i2c_bus; /* the bus selected at start-up */ static int omap_i2c_nbuses; /* number of buses supported by SoC */ /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ static struct log log = { .name = "i2c", .log_level = LEVEL_INFO, .log_func = default_log }; /* Local Function Prototypes */ /* Implementation of Generic I2C Interface using Bus Specific Code */ static int omap_i2c_process(minix_i2c_ioctl_exec_t * m); /* Bus Specific Code */ static void omap_i2c_flush(void); static uint16_t omap_i2c_poll(uint16_t mask); static int omap_i2c_bus_is_free(void); static int omap_i2c_soft_reset(void); static void omap_i2c_bus_init(void); static void omap_i2c_padconf(int i2c_bus_id); static void omap_i2c_clkconf(int i2c_bus_id); static void omap_i2c_intr_enable(void); static uint16_t omap_i2c_read_status(void); static void omap_i2c_write_status(uint16_t mask); static int omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, int dostop); static int omap_i2c_write(i2c_addr_t addr, const uint8_t * buf, size_t buflen, int dostop); /* * Performs the action in minix_i2c_ioctl_exec_t. */ static int omap_i2c_process(minix_i2c_ioctl_exec_t * ioctl_exec) { int r; /* * Zero data bytes transfers are not allowed. The controller treats * I2C_CNT register value of 0x0 as 65536. This is true for both the * am335x and dm37xx. Full details in the TRM on the I2C_CNT page. */ if (ioctl_exec->iie_buflen == 0) { return EINVAL; } omap_i2c_flush(); /* clear any garbage in the fifo */ /* Check bus busy flag before using the bus */ r = omap_i2c_bus_is_free(); if (r == 0) { log_warn(&log, "Bus is busy\n"); return EBUSY; } if (ioctl_exec->iie_cmdlen > 0) { r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_cmd, ioctl_exec->iie_cmdlen, !(I2C_OP_READ_P(ioctl_exec->iie_op))); if (r != OK) { omap_i2c_soft_reset(); omap_i2c_bus_init(); return r; } } if (I2C_OP_READ_P(ioctl_exec->iie_op)) { r = omap_i2c_read(ioctl_exec->iie_addr, ioctl_exec->iie_buf, ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); } else { r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_buf, ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); } if (r != OK) { omap_i2c_soft_reset(); omap_i2c_bus_init(); return r; } return OK; } /* * Drain the incoming FIFO. * * Usually called to clear any garbage that may be in the buffer before * doing a read. */ static void omap_i2c_flush(void) { int tries; int status; for (tries = 0; tries < 1000; tries++) { status = omap_i2c_poll(1 << RRDY); if ((status & (1 << RRDY)) != 0) { /* bytes available for reading */ /* consume the byte and throw it away */ (void) read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA); /* clear the read ready flag */ omap_i2c_write_status(1 << RRDY); } else { break; /* buffer drained */ } } } /* * Poll the status register checking the bits set in 'mask'. * Returns the status if any bits set or 0x0000 when the timeout is reached. */ static uint16_t omap_i2c_poll(uint16_t mask) { spin_t spin; uint16_t status; /* poll for up to 1 s */ spin_init(&spin, 1000000); do { status = omap_i2c_read_status(); if ((status & mask) != 0) { /* any bits in mask set */ return status; } } while (spin_check(&spin)); return status; /* timeout reached, abort */ } /* * Poll Bus Busy Flag until the bus becomes free (return 1) or the timeout * expires (return 0). */ static int omap_i2c_bus_is_free(void) { spin_t spin; uint16_t status; /* wait for up to 1 second for the bus to become free */ spin_init(&spin, 1000000); do { status = omap_i2c_read_status(); if ((status & (1 << BB)) == 0) { return 1; /* bus is free */ } } while (spin_check(&spin)); return 0; /* timeout expired */ } static void omap_i2c_clkconf(int i2c_bus_id) { clkconf_init(); if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { clkconf_set(CM_ICLKEN1_CORE, BIT((15 + i2c_bus_id)), 0xffffffff); clkconf_set(CM_FCLKEN1_CORE, BIT((15 + i2c_bus_id)), 0xffffffff); } else if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { switch (i2c_bus_id) { case 0: clkconf_set(CM_WKUP_I2C0_CLKCTRL, BIT(1), 0xffffffff); break; case 1: clkconf_set(CM_PER_I2C1_CLKCTRL, BIT(1), 0xffffffff); break; case 2: clkconf_set(CM_PER_I2C2_CLKCTRL, BIT(1), 0xffffffff); break; default: log_warn(&log, "Invalid i2c_bus_id\n"); break; } } clkconf_release(); } static void omap_i2c_padconf(int i2c_bus_id) { int r; u32_t pinopts; if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { /* use the options suggested in starterware driver */ pinopts = CONTROL_CONF_SLEWCTRL | CONTROL_CONF_RXACTIVE | CONTROL_CONF_PUTYPESEL; switch (i2c_bus_id) { case 0: pinopts |= CONTROL_CONF_MUXMODE(0); r = sys_padconf(CONTROL_CONF_I2C0_SDA, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } r = sys_padconf(CONTROL_CONF_I2C0_SCL, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } log_debug(&log, "pinopts=0x%x\n", pinopts); break; case 1: pinopts |= CONTROL_CONF_MUXMODE(2); r = sys_padconf(CONTROL_CONF_SPI0_CS0, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } r = sys_padconf(CONTROL_CONF_SPI0_D1, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } log_debug(&log, "pinopts=0x%x\n", pinopts); break; case 2: pinopts |= CONTROL_CONF_MUXMODE(3); r = sys_padconf(CONTROL_CONF_UART1_CTSN, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } r = sys_padconf(CONTROL_CONF_UART1_RTSN, 0xffffffff, pinopts); if (r != OK) { log_warn(&log, "padconf failed (r=%d)\n", r); } log_debug(&log, "pinopts=0x%x\n", pinopts); break; default: log_warn(&log, "Invalid i2c_bus_id\n"); break; } } /* nothing to do for the DM37XX */ } static int omap_i2c_soft_reset(void) { spin_t spin; /* Disable to do soft reset */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); micro_delay(50000); /* Do a soft reset */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1 << SRST)); /* Have to temporarily enable I2C to read RDONE */ set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<mapped_addr + omap_i2c_bus->regs->I2C_SYSS) & (1 << RDONE)) { return OK; } } while (spin_check(&spin)); log_warn(&log, "Tried soft reset, but bus never came back.\n"); return EIO; } static void omap_i2c_intr_enable(void) { int r; uint16_t intmask; static int policy_set = 0; static int enabled = 0; if (!policy_set) { r = sys_irqsetpolicy(omap_i2c_bus->irq, 0, &omap_i2c_bus->irq_hook_kernel_id); if (r == OK) { policy_set = 1; } else { log_warn(&log, "Couldn't set irq policy\n"); } } if (policy_set && !enabled) { r = sys_irqenable(&omap_i2c_bus->irq_hook_kernel_id); if (r == OK) { enabled = 1; } else { log_warn(&log, "Couldn't enable irq %d (hooked)\n", omap_i2c_bus->irq); } } /* According to NetBSD driver and u-boot, these are needed even * if just using polling (i.e. non-interrupt driver programming). */ intmask = 0; intmask |= (1 << ROVR); intmask |= (1 << AERR); intmask |= (1 << XRDY); intmask |= (1 << RRDY); intmask |= (1 << ARDY); intmask |= (1 << NACK); intmask |= (1 << AL); if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQENABLE_SET, intmask); } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IE, intmask); } else { log_warn(&log, "Don't know how to enable interrupts.\n"); } } static void omap_i2c_bus_init(void) { /* Ensure i2c module is disabled before setting prescalar & bus speed */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); micro_delay(50000); /* Disable autoidle */ set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1<mapped_addr + omap_i2c_bus->regs->I2C_PSC, ((omap_i2c_bus->functional_clock / omap_i2c_bus->module_clock) - 1)); /* Set the bus speed */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SCLL, ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - 7)); write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SCLH, ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - 5)); /* Set own I2C address */ if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_OA, I2C_OWN_ADDRESS); } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_OA0, I2C_OWN_ADDRESS); } else { log_warn(&log, "Don't know how to set own address.\n"); } /* Set TX/RX Threshold to 1 and disable I2C DMA */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_BUF, 0x0000); /* Bring the i2c module out of reset */ set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<bus_type == AM335X_I2C_BUS) { /* TRM says to use RAW for polling for events */ status = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS_RAW); } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { status = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_STAT); } else { log_warn(&log, "Don't know how to read i2c bus status.\n"); } return status; } static void omap_i2c_write_status(uint16_t mask) { if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { /* write 1's to IRQSTATUS (not RAW) to clear the bits */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS, mask); } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_STAT, mask); } else { log_warn(&log, "Don't know how to clear i2c bus status.\n"); } } static int omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, int dostop) { int r, i; uint16_t conopts; uint16_t pollmask; uint16_t errmask; /* Set address of slave device */ conopts = 0; addr &= MAX_I2C_SA_MASK; /* sanitize address (10-bit max) */ if (addr > 0x7f) { /* 10-bit extended address in use, need to set XSA */ conopts |= (1 << XSA); } errmask = 0; errmask |= (1 << ROVR); errmask |= (1 << AERR); errmask |= (1 << NACK); errmask |= (1 << AL); pollmask = 0; pollmask |= (1 << RRDY); /* Set bytes to read and slave address */ write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SA, addr); /* Set control register */ conopts |= (1 << I2C_EN); /* enabled */ conopts |= (1 << MST); /* master mode */ conopts |= (1 << STT); /* start condition */ if (dostop != 0) { conopts |= (1 << STP); /* stop condition */ } write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, conopts); for (i = 0; i < buflen; i++) { /* Data to read? */ r = omap_i2c_poll(pollmask | errmask); if ((r & errmask) != 0) { /* only debug log level because i2cscan trigers this */ log_debug(&log, "Read Error! Status=%x\n", r); return EIO; } else if ((r & pollmask) == 0) { log_warn(&log, "No RRDY Interrupt. Status=%x\n", r); log_warn(&log, "Likely cause: bad pinmux or no devices on bus\n"); return EBUSY; } /* read a byte */ buf[i] = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA) & 0xff; /* clear the read ready flag */ omap_i2c_write_status(pollmask); } r = omap_i2c_read_status(); if ((r & (1 << NACK)) != 0) { log_warn(&log, "NACK\n"); return EIO; } /* Wait for operation to complete */ pollmask = (1< 0x7f) { /* 10-bit extended address in use, need to set XSA */ conopts |= (1 << XSA); } pollmask = 0; pollmask |= (1 << XRDY); errmask = 0; errmask |= (1 << ROVR); errmask |= (1 << AERR); errmask |= (1 << NACK); errmask |= (1 << AL); write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SA, addr); /* Set control register */ conopts |= (1 << I2C_EN); /* enabled */ conopts |= (1 << MST); /* master mode */ conopts |= (1 << TRX); /* TRX mode */ conopts |= (1 << STT); /* start condition */ if (dostop != 0) { conopts |= (1 << STP); /* stop condition */ } omap_i2c_write_status(0x7fff); write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, conopts); for (i = 0; i < buflen; i++) { /* Ready to write? */ r = omap_i2c_poll(pollmask | errmask); if ((r & errmask) != 0) { log_warn(&log, "Write Error! Status=%x\n", r); return EIO; } else if ((r & pollmask) == 0) { log_warn(&log, "Not ready for write? Status=%x\n", r); return EBUSY; } write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA, buf[i]); /* clear the write ready flag */ omap_i2c_write_status(pollmask); } r = omap_i2c_read_status(); if ((r & (1 << NACK)) != 0) { log_warn(&log, "NACK\n"); return EIO; } /* Wait for operation to complete */ pollmask = (1<= omap_i2c_nbuses) { return EINVAL; } /* select the bus to operate on */ omap_i2c_bus = &omap_i2c_buses[i2c_bus_id]; /* Configure Pins */ omap_i2c_padconf(i2c_bus_id); /* * Map I2C Registers */ /* Configure memory access */ mr.mr_base = omap_i2c_bus->mr_base; /* start addr */ mr.mr_limit = mr.mr_base + omap_i2c_bus->mr_size; /* end addr */ /* ask for privileges to access the I2C memory range */ if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) { panic("Unable to obtain i2c memory range privileges"); } /* map the memory into this process */ omap_i2c_bus->mapped_addr = (vir_bytes) vm_map_phys(SELF, (void *) omap_i2c_bus->mr_base, omap_i2c_bus->mr_size); if (omap_i2c_bus->mapped_addr == (vir_bytes) MAP_FAILED) { panic("Unable to map i2c registers"); } /* Enable Clocks */ omap_i2c_clkconf(i2c_bus_id); /* Perform a soft reset of the I2C module to ensure a fresh start */ r = omap_i2c_soft_reset(); if (r != OK) { /* module didn't come back up :( */ return r; } /* Bring up I2C module */ omap_i2c_bus_init(); /* Get I2C Revision */ if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { /* I2C_REVLO revision: major (bits 10-8), minor (bits 5-0) */ i2c_rev = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_REVNB_LO); major = (i2c_rev >> 8) & 0x07; minor = i2c_rev & 0x3f; } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { /* I2C_REV revision: major (bits 7-4), minor (bits 3-0) */ i2c_rev = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_REV); major = (i2c_rev >> 4) & 0x0f; minor = i2c_rev & 0x0f; } else { panic("Don't know how to read i2c revision."); } if (major != omap_i2c_bus->major || minor != omap_i2c_bus->minor) { log_warn(&log, "Unrecognized value in I2C_REV register.\n"); log_warn(&log, "Read: 0x%x.0x%x | Expected: 0x%x.0x%x\n", major, minor, omap_i2c_bus->major, omap_i2c_bus->minor); } /* display i2c revision information for debugging purposes */ log_debug(&log, "i2c_%d: I2C rev 0x%x.0x%x\n", (i2c_bus_id + 1), major, minor); return OK; }