/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #define I2C_TIMEOUT_US (1000 * USECS_PER_MSEC) struct __packed i2c_regs { uint8_t con; uint8_t _1[3]; uint8_t stat; uint8_t _2[3]; uint8_t add; uint8_t _3[3]; uint8_t ds; uint8_t _4[3]; uint8_t lc; uint8_t _5[3]; }; struct s3c24x0_i2c_bus { int bus_num; struct i2c_regs *regs; enum periph_id periph_id; }; enum { I2cConIntPending = 0x1 << 4, I2cConIntEn = 0x1 << 5, I2cConAckGen = 0x1 << 7 }; enum { I2cStatAck = 0x1 << 0, I2cStatAddrZero = 0x1 << 1, I2cStatAddrSlave = 0x1 << 2, I2cStatArb = 0x1 << 3, I2cStatEnable = 0x1 << 4, I2cStatStartStop = 0x1 << 5, I2cStatBusy = 0x1 << 5, I2cStatModeMask = 0x3 << 6, I2cStatSlaveRecv = 0x0 << 6, I2cStatSlaveXmit = 0x1 << 6, I2cStatMasterRecv = 0x2 << 6, I2cStatMasterXmit = 0x3 << 6 }; static struct s3c24x0_i2c_bus i2c_busses[] = { { .bus_num = 0, .regs = (void *)0x12c60000, .periph_id = PERIPH_ID_I2C0, }, { .bus_num = 1, .regs = (void *)0x12c70000, .periph_id = PERIPH_ID_I2C1, }, { .bus_num = 2, .regs = (void *)0x12c80000, .periph_id = PERIPH_ID_I2C2, }, { .bus_num = 3, .regs = (void *)0x12c90000, .periph_id = PERIPH_ID_I2C3, }, { .bus_num = 4, .regs = (void *)0x12ca0000, .periph_id = PERIPH_ID_I2C4, }, { .bus_num = 5, .regs = (void *)0x12cb0000, .periph_id = PERIPH_ID_I2C5, }, { .bus_num = 6, .regs = (void *)0x12cc0000, .periph_id = PERIPH_ID_I2C6, }, { .bus_num = 7, .regs = (void *)0x12cd0000, .periph_id = PERIPH_ID_I2C7, }, }; static int i2c_int_pending(struct i2c_regs *regs) { return read8(®s->con) & I2cConIntPending; } static void i2c_clear_int(struct i2c_regs *regs) { write8(®s->con, read8(®s->con) & ~I2cConIntPending); } static void i2c_ack_enable(struct i2c_regs *regs) { write8(®s->con, read8(®s->con) | I2cConAckGen); } static void i2c_ack_disable(struct i2c_regs *regs) { write8(®s->con, read8(®s->con) & ~I2cConAckGen); } static int i2c_got_ack(struct i2c_regs *regs) { return !(read8(®s->stat) & I2cStatAck); } static int i2c_wait_for_idle(struct i2c_regs *regs, int timeout_us) { int timeout = DIV_ROUND_UP(timeout_us, 10); while (timeout--) { if (!(read8(®s->stat) & I2cStatBusy)) return 0; udelay(10); } printk(BIOS_ERR, "I2C timeout waiting for idle.\n"); return 1; } static int i2c_wait_for_int(struct i2c_regs *regs, int timeout_us) { int timeout = DIV_ROUND_UP(timeout_us, 10); while (timeout--) { if (i2c_int_pending(regs)) return 0; udelay(10); } printk(BIOS_ERR, "I2C timeout waiting for I2C interrupt.\n"); return 1; } static int i2c_send_stop(struct i2c_regs *regs) { uint8_t mode = read8(®s->stat) & (I2cStatModeMask); write8(®s->stat, mode | I2cStatEnable); i2c_clear_int(regs); return i2c_wait_for_idle(regs, I2C_TIMEOUT_US); } static int i2c_send_start(struct i2c_regs *regs, int read, int chip) { write8(®s->ds, chip << 1); uint8_t mode = read ? I2cStatMasterRecv : I2cStatMasterXmit; write8(®s->stat, mode | I2cStatStartStop | I2cStatEnable); i2c_clear_int(regs); if (i2c_wait_for_int(regs, I2C_TIMEOUT_US)) return 1; if (!i2c_got_ack(regs)) { // Nobody home, but they may just be asleep. return 1; } return 0; } static int i2c_xmit_buf(struct i2c_regs *regs, uint8_t *data, int len) { ASSERT(len); i2c_ack_enable(regs); int i; for (i = 0; i < len; i++) { write8(®s->ds, data[i]); i2c_clear_int(regs); if (i2c_wait_for_int(regs, CONFIG_I2C_TRANSFER_TIMEOUT_US)) return 1; if (!i2c_got_ack(regs)) { printk(BIOS_INFO, "I2c nacked.\n"); return 1; } } return 0; } static int i2c_recv_buf(struct i2c_regs *regs, uint8_t *data, int len) { ASSERT(len); i2c_ack_enable(regs); int i; for (i = 0; i < len; i++) { if (i == len - 1) i2c_ack_disable(regs); i2c_clear_int(regs); if (i2c_wait_for_int(regs, CONFIG_I2C_TRANSFER_TIMEOUT_US)) return 1; data[i] = read8(®s->ds); } return 0; } int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments, int seg_count) { struct s3c24x0_i2c_bus *i2c = &i2c_busses[bus]; struct i2c_regs *regs = i2c->regs; int res = 0; if (!regs || i2c_wait_for_idle(regs, I2C_TIMEOUT_US)) return 1; write8(®s->stat, I2cStatMasterXmit | I2cStatEnable); int i; for (i = 0; i < seg_count; i++) { struct i2c_msg *seg = &segments[i]; res = i2c_send_start(regs, seg->flags & I2C_M_RD, seg->slave); if (res) break; if (seg->flags & I2C_M_RD) res = i2c_recv_buf(regs, seg->buf, seg->len); else res = i2c_xmit_buf(regs, seg->buf, seg->len); if (res) break; } return i2c_send_stop(regs) || res; } void i2c_init(unsigned int bus, int speed, int slaveadd) { struct s3c24x0_i2c_bus *i2c = &i2c_busses[bus]; unsigned long freq, pres = 16, div; unsigned long val; freq = clock_get_periph_rate(i2c->periph_id); // Calculate prescaler and divisor values. if ((freq / pres / (16 + 1)) > speed) /* set prescaler to 512 */ pres = 512; div = 0; while ((freq / pres / (div + 1)) > speed) div++; // Set prescaler, divisor according to freq, also set ACKGEN, IRQ. val = (div & 0x0f) | 0xa0 | ((pres == 512) ? 0x40 : 0); write32(&i2c->regs->con, val); // Init to SLAVE RECEIVE mode and clear I2CADDn. write32(&i2c->regs->stat, 0); write32(&i2c->regs->add, slaveadd); // program Master Transmit (and implicit STOP). write32(&i2c->regs->stat, I2cStatMasterXmit | I2cStatEnable); }