Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14914
-gerrit
commit cc69a01c6a3b23ccbd122cdcb749a1b4d9dd8b11 Author: Akshay Saraswat akshay.s@samsung.com Date: Thu Aug 7 16:29:56 2014 +0530
Exynos7: Add I2C initialization
Adding initial I2C setup for Exynos7 in this patch.
Change-Id: I96970189a97d6446215c309f64d590a284756213 Signed-off-by: Akshay Saraswat akshay.s@samsung.com Signed-off-by: Stefan Reinauer stefan.reinauer@coreboot.org --- src/soc/samsung/exynos7/Makefile.inc | 2 + src/soc/samsung/exynos7/i2c.c | 517 ++++++++++++++++++++++++++++++ src/soc/samsung/exynos7/include/soc/i2c.h | 80 +++++ 3 files changed, 599 insertions(+)
diff --git a/src/soc/samsung/exynos7/Makefile.inc b/src/soc/samsung/exynos7/Makefile.inc index f954bd3..700c0a1 100644 --- a/src/soc/samsung/exynos7/Makefile.inc +++ b/src/soc/samsung/exynos7/Makefile.inc @@ -39,6 +39,7 @@ endif romstage-y += alternate_cbfs.c romstage-y += clock.c romstage-y += gpio.c +romstage-y += i2c.c romstage-y += mct.c romstage-y += monotonic_timer.c romstage-y += pinmux.c @@ -53,6 +54,7 @@ ramstage-y += alternate_cbfs.c ramstage-y += clock.c ramstage-y += cpu.c ramstage-y += gpio.c +ramstage-y += i2c.c ramstage-y += mct.c ramstage-y += monotonic_timer.c ramstage-y += pinmux.c diff --git a/src/soc/samsung/exynos7/i2c.c b/src/soc/samsung/exynos7/i2c.c new file mode 100644 index 0000000..e44928e --- /dev/null +++ b/src/soc/samsung/exynos7/i2c.c @@ -0,0 +1,517 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <console/console.h> +#include <delay.h> +#include <timer.h> +#include <arch/io.h> +#include <soc/clock.h> +#include <soc/i2c.h> +#include <soc/pinmux.h> + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +/* HSI2C specific register description */ + +/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_TX_FIFO_LEVEL (0x7f << 0) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_RX_FIFO_LEVEL (0x7f << 16) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_RX_FIFO_EMPTY (1u << 24) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff) + +/* I2C_INT_EN Register bits */ +#define HSI2C_INT_TX_ALMOSTEMPTY_EN (1u << 0) +#define HSI2C_INT_RX_ALMOSTFULL_EN (1u << 1) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_TIMEOUT_AUTO (1u << 11) +/* In INT_STATUS for Exynos7 */ +#define HSI2C_NO_DEV (1u << 10) +#define HSI2C_NO_DEV_ACK (1u << 9) +#define HSI2C_TRANS_ABORT (1u << 8) +#define HSI2C_TRANS_DONE (1u << 7) +#define HSI2C_INT_STATUS_BITS (HSI2C_TIMEOUT_AUTO | HSI2C_NO_DEV \ + | HSI2C_NO_DEV_ACK | HSI2C_TRANS_ABORT \ + | HSI2C_TRANS_DONE) +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) + +#define HSI2C_TIMEOUT 100 + +/* The timeouts we live by */ +enum { + I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */ + I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */ + I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */ + I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */ +}; + +static struct exynos_i2c_bus i2c_buses[] = { + { + .bus_num = 0, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C0_BASE, + .periph_id = PERIPH_ID_I2C0, + .is_highspeed = 1, + }, + { + .bus_num = 1, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C1_BASE, + .periph_id = PERIPH_ID_I2C1, + .is_highspeed = 1, + }, + { + .bus_num = 2, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C2_BASE, + .periph_id = PERIPH_ID_I2C2, + .is_highspeed = 1, + }, + { + .bus_num = 3, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C3_BASE, + .periph_id = PERIPH_ID_I2C3, + .is_highspeed = 1, + }, + { + .bus_num = 4, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C4_BASE, + .periph_id = PERIPH_ID_I2C4, + .is_highspeed = 1, + }, + { + .bus_num = 5, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C5_BASE, + .periph_id = PERIPH_ID_I2C5, + .is_highspeed = 1, + }, + { + .bus_num = 6, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C6_BASE, + .periph_id = PERIPH_ID_I2C6, + .is_highspeed = 1, + }, + { + .bus_num = 7, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C7_BASE, + .periph_id = PERIPH_ID_I2C7, + .is_highspeed = 1, + }, + { + .bus_num = 8, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C8_BASE, + .periph_id = PERIPH_ID_I2C8, + .is_highspeed = 1, + }, + { + .bus_num = 9, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C9_BASE, + .periph_id = PERIPH_ID_I2C9, + .is_highspeed = 1, + }, + { + .bus_num = 10, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C10_BASE, + .periph_id = PERIPH_ID_I2C10, + .is_highspeed = 1, + }, + { + .bus_num = 11, + .hsregs = (struct exynos7_hsi2c *)EXYNOS7_HSI2C11_BASE, + .periph_id = PERIPH_ID_I2C11, + .is_highspeed = 1, + }, +}; + +static int hsi2c_get_clk_details(struct exynos_i2c_bus *i2c_bus, + unsigned int bus_freq_hz) +{ + struct exynos7_hsi2c *hsregs = i2c_bus->hsregs; + unsigned long clkin = get_i2c_clk(); + unsigned int i = 0, utemp0 = 0, utemp1 = 0; + unsigned int t_ftl_cycle; + + /* + * FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE + * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + * uTemp1 = (TSCLK_L + TSCLK_H + 2) + * uTemp2 = TSCLK_L + TSCLK_H + */ + t_ftl_cycle = (read32(&hsregs->usi_conf) >> 16) & 0x7; + utemp0 = (clkin / bus_freq_hz) - 8 - t_ftl_cycle; + + /* CLK_DIV max is 256 */ + for (i = 0; i < 256; i++) { + utemp1 = utemp0 / (i + 1); + if ((utemp1 < 512) && (utemp1 > 4)) { + i2c_bus->clk_cycle = utemp1 - 2; + i2c_bus->clk_div = i; + return 0; + } + } + printk(BIOS_ERR, "%s: failed?\n", __func__); + return -1; +} + +static void hsi2c_ch_init(struct exynos_i2c_bus *i2c_bus, + unsigned int bus_freq_hz) +{ + struct exynos7_hsi2c *hsregs = i2c_bus->hsregs; + unsigned int t_sr_release; + unsigned int n_clkdiv; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + + hsi2c_get_clk_details(i2c_bus, bus_freq_hz); + + n_clkdiv = i2c_bus->clk_div; + t_scl_l = i2c_bus->clk_cycle / 2; + t_scl_h = i2c_bus->clk_cycle / 2; + t_start_su = t_scl_l; + t_start_hd = t_scl_l; + t_stop_su = t_scl_l; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = i2c_bus->clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + write32(&hsregs->usi_trailing_ctl, HSI2C_TRAILING_COUNT); + + /* Clear to enable Timeout */ + clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0); + + write32(&hsregs->usi_conf, read32(&hsregs->usi_conf) | HSI2C_AUTO_MODE); + + /* Currently operating in Fast speed mode. */ + write32(&hsregs->usi_timing_fs1, i2c_timing_s1); + write32(&hsregs->usi_timing_fs2, i2c_timing_s2); + write32(&hsregs->usi_timing_fs3, i2c_timing_s3); + write32(&hsregs->usi_timing_sla, i2c_timing_sla); + + /* Enable TXFIFO and RXFIFO */ + write32(&hsregs->usi_fifo_ctl, HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN); + + /* i2c_conf configure */ + write32(&hsregs->usi_conf, read32(&hsregs->usi_conf) | HSI2C_AUTO_MODE); +} + +/* SW reset for the high speed bus */ +static void i2c_reset(struct exynos_i2c_bus *i2c_bus) +{ + struct exynos7_hsi2c *i2c = i2c_bus->hsregs; + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = read32(&i2c->usi_ctl); + i2c_ctl |= HSI2C_SW_RST; + write32(&i2c->usi_ctl, i2c_ctl); + + i2c_ctl = read32(&i2c->usi_ctl); + i2c_ctl &= ~HSI2C_SW_RST; + write32(&i2c->usi_ctl, i2c_ctl); + + /* Initialize the configure registers */ + hsi2c_ch_init(i2c_bus, 100000); /* 100 KHz*/ +} + +void i2c_init(unsigned bus_num, int speed, int slaveadd) +{ + struct exynos_i2c_bus *i2c; + + i2c = &i2c_buses[bus_num]; + + i2c_reset(i2c); + + hsi2c_ch_init(i2c, speed); +} + +/* + * Check whether the transfer is complete. + * Return values: + * 0 - transfer not done + * 1 - transfer finished successfully + * -1 - transfer failed + */ +static int hsi2c_check_transfer(struct exynos7_hsi2c *i2c) +{ + uint32_t status = read32(&i2c->usi_int_stat); + uint32_t trans_status = read32(&i2c->usi_trans_status); + + if (status & (HSI2C_TRANS_ABORT | HSI2C_NO_DEV_ACK | + HSI2C_NO_DEV | HSI2C_TIMEOUT_AUTO)) { + if (status & HSI2C_TRANS_ABORT) + printk(BIOS_ERR, + "%s: Transaction aborted.\n", __func__); + if (status & HSI2C_NO_DEV_ACK) + printk(BIOS_ERR, + "%s: No ack from device.\n", __func__); + if (status & HSI2C_NO_DEV) + printk(BIOS_ERR, + "%s: No response from device.\n", __func__); + if (status & HSI2C_TIMEOUT_AUTO) + printk(BIOS_ERR, + "%s: Transaction time out.\n", __func__); + return -1; + } + return !(trans_status & HSI2C_MASTER_BUSY); +} + +/* + * Wait for the transfer to finish. + * Return values: + * 0 - transfer not done + * 1 - transfer finished successfully + * -1 - transfer failed + */ +static int hsi2c_wait_for_transfer(struct exynos7_hsi2c *i2c) +{ + struct mono_time current, end; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_usecs(&end, HSI2C_TIMEOUT * 1000); + while (mono_time_before(¤t, &end)) { + int ret = hsi2c_check_transfer(i2c); + if (ret) + return ret; + udelay(5); + timer_monotonic_get(¤t); + } + return 0; +} + +static int hsi2c_senddata(struct exynos7_hsi2c *i2c, const uint8_t *data, + int len) +{ + while (!hsi2c_check_transfer(i2c) && len) { + if (!(read32(&i2c->usi_fifo_stat) & HSI2C_TX_FIFO_FULL)) { + write32(&i2c->usi_txdata, *data++); + len--; + } + } + return len ? -1 : 0; +} + +static int hsi2c_recvdata(struct exynos7_hsi2c *i2c, uint8_t *data, int len) +{ + while (!hsi2c_check_transfer(i2c) && len) { + if (!(read32(&i2c->usi_fifo_stat) & HSI2C_RX_FIFO_EMPTY)) { + *data++ = read32(&i2c->usi_rxdata); + len--; + } + } + return len ? -1 : 0; +} + +static int hsi2c_write(struct exynos7_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + const uint8_t data[], + unsigned short len) +{ + uint32_t i2c_auto_conf; + + write32(&i2c->usi_int_en, HSI2C_INT_STATUS_BITS); + if (hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + /* chip address */ + write32(&i2c->i2c_addr, HSI2C_SLV_ADDR_MAS(chip)); + + /* usi_ctl enable i2c func, master write configure */ + write32(&i2c->usi_ctl, + (HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER)); + + /* auto_conf for write length and stop configure */ + i2c_auto_conf = ((len + alen) | HSI2C_STOP_AFTER_TRANS); + i2c_auto_conf &= ~HSI2C_READ_WRITE; + /* Master run, start xfer */ + i2c_auto_conf |= HSI2C_MASTER_RUN; + write32(&i2c->usi_auto_conf, i2c_auto_conf); + + if (hsi2c_senddata(i2c, addr, alen) || + hsi2c_senddata(i2c, data, len) || + hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + write32(&i2c->usi_ctl, HSI2C_FUNC_MODE_I2C); + return 0; +} + +static int hsi2c_read(struct exynos7_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + uint8_t data[], + unsigned short len, + int check) +{ + uint32_t i2c_auto_conf; + + write32(&i2c->usi_int_en, HSI2C_INT_STATUS_BITS); + /* start read */ + if (hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + /* chip address */ + write32(&i2c->i2c_addr, HSI2C_SLV_ADDR_MAS(chip)); + + if (alen) { + /* usi_ctl enable i2c func, master write configure */ + write32(&i2c->usi_ctl, + HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER); + + /* auto_conf */ + write32(&i2c->usi_auto_conf, + alen | HSI2C_MASTER_RUN | HSI2C_STOP_AFTER_TRANS); + + if (hsi2c_senddata(i2c, addr, alen) || + hsi2c_wait_for_transfer(i2c) != 1) + return -1; + } + + /* usi_ctl enable i2c func, master WRITE configure */ + write32(&i2c->usi_ctl, + (HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER)); + + /* auto_conf, length and stop configure */ + i2c_auto_conf = (len | HSI2C_STOP_AFTER_TRANS | HSI2C_READ_WRITE); + i2c_auto_conf |= HSI2C_MASTER_RUN; + /* Master run, start xfer */ + write32(&i2c->usi_auto_conf, i2c_auto_conf); + + if (hsi2c_recvdata(i2c, data, len) || + hsi2c_wait_for_transfer(i2c) != 1) + return -1; + + write32(&i2c->usi_ctl, HSI2C_FUNC_MODE_I2C); + return 0; +} + +int i2c_read(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, uint8_t *buf, unsigned len) +{ + struct exynos_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + ret = hsi2c_read(i2c->hsregs, chip, &xaddr[4 - alen], + alen, buf, len, 0); + if (ret) { + i2c_reset(i2c); + printk(BIOS_ERR, + "I2C read (bus %02x, chip addr %02x) failed: %d\n", + bus, chip, ret); + return 1; + } + return 0; +} + +int i2c_write(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, const uint8_t *buf, unsigned len) +{ + struct exynos_i2c_bus *i2c; + unsigned char xaddr[4]; + int ret; + + if (alen > 4) { + printk(BIOS_ERR, "I2C write: addr len %d not supported\n", + alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + i2c = &i2c_buses[bus]; + ret = hsi2c_write(i2c->hsregs, chip, &xaddr[4 - alen], + alen, buf, len); + if (ret != 0) { + i2c_reset(i2c); + printk(BIOS_ERR, + "I2C write (bus %02x, chip addr %02x) failed: %d\n", + bus, chip, ret); + return 1; + } + return 0; +} diff --git a/src/soc/samsung/exynos7/include/soc/i2c.h b/src/soc/samsung/exynos7/include/soc/i2c.h new file mode 100644 index 0000000..6931e21 --- /dev/null +++ b/src/soc/samsung/exynos7/include/soc/i2c.h @@ -0,0 +1,80 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CPU_SAMSUNG_EXYNOS7_I2C_H_ +#define _CPU_SAMSUNG_EXYNOS7_I2C_H_ + +#include <stddef.h> +#include <soc/periph.h> + +struct exynos_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +} __attribute__ ((packed)); + +struct exynos7_hsi2c { + u32 usi_ctl; + u32 usi_fifo_ctl; + u32 usi_trailing_ctl; + u32 usi_clk_ctl; + u32 usi_clk_slot; + u32 spi_ctl; + u32 uart_ctl; + u32 res1; + u32 usi_int_en; + u32 usi_int_stat; + u32 usi_modem_stat; + u32 usi_error_stat; + u32 usi_fifo_stat; + u32 usi_txdata; + u32 usi_rxdata; + u32 res2; + u32 usi_conf; + u32 usi_auto_conf; + u32 usi_timeout; + u32 usi_manual_cmd; + u32 usi_trans_status; + u32 usi_timing_hs1; + u32 usi_timing_hs2; + u32 usi_timing_hs3; + u32 usi_timing_fs1; + u32 usi_timing_fs2; + u32 usi_timing_fs3; + u32 usi_timing_sla; + u32 i2c_addr; +} __attribute__ ((packed)); +check_member(exynos7_hsi2c, i2c_addr, 0x70); + +struct exynos_i2c_bus { + int bus_num; + struct exynos_i2c *regs; + enum periph_id periph_id; + struct exynos7_hsi2c *hsregs; + int is_highspeed; /* High speed type, rather than I2C */ + int id; + unsigned clk_cycle; + unsigned clk_div; +}; + +void i2c_init(unsigned bus, int speed, int slaveadd); + +int i2c_read(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, uint8_t *buf, unsigned len); +int i2c_write(unsigned bus, unsigned chip, unsigned addr, + unsigned alen, const uint8_t *buf, unsigned len); +#endif /* _CPU_SAMSUNG_EXYNOS7_I2C_H_ */