x86: Add Intel Quark X1000 I2C support

This patch adds the i2c.c, i2c.h and i2c-registers.h files,
which support access to I2C controller configuration register
through a function interface.
This commit is contained in:
Ricardo de Almeida Gonzaga 2015-09-30 09:32:16 -03:00 committed by Jesus Sanchez-Palencia
parent cc51f89b31
commit a27af5b395
4 changed files with 736 additions and 1 deletions

View File

@ -2,7 +2,7 @@ include $(CONTIKI)/cpu/x86/Makefile.x86_common
CONTIKI_CPU_DIRS += drivers/legacy_pc drivers/quarkX1000 init/legacy_pc CONTIKI_CPU_DIRS += drivers/legacy_pc drivers/quarkX1000 init/legacy_pc
CONTIKI_SOURCEFILES += bootstrap_quarkX1000.S rtc.c pit.c pic.c irq.c nmi.c pci.c uart-16x50.c uart.c gpio.c CONTIKI_SOURCEFILES += bootstrap_quarkX1000.S rtc.c pit.c pic.c irq.c nmi.c pci.c uart-16x50.c uart.c gpio.c i2c.c
CFLAGS += -m32 -march=i586 -mtune=i586 CFLAGS += -m32 -march=i586 -mtune=i586
LDFLAGS += -m32 -Xlinker -T -Xlinker $(CONTIKI)/cpu/x86/quarkX1000.ld LDFLAGS += -m32 -Xlinker -T -Xlinker $(CONTIKI)/cpu/x86/quarkX1000.ld

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_
#define CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_
#define QUARKX1000_IC_CON 0x00
#define QUARKX1000_IC_TAR 0x04
#define QUARKX1000_IC_DATA_CMD 0x10
#define QUARKX1000_IC_SS_SCL_HCNT 0x14
#define QUARKX1000_IC_SS_SCL_LCNT 0x18
#define QUARKX1000_IC_FS_SCL_HCNT 0x1C
#define QUARKX1000_IC_FS_SCL_LCNT 0x20
#define QUARKX1000_IC_INTR_STAT 0x2C
#define QUARKX1000_IC_INTR_MASK 0x30
#define QUARKX1000_IC_RAW_INTR_STAT 0x34
#define QUARKX1000_IC_RX_TL 0x38
#define QUARKX1000_IC_TX_TL 0x3C
#define QUARKX1000_IC_CLR_INTR 0x40
#define QUARKX1000_IC_CLR_RX_UNDER 0x44
#define QUARKX1000_IC_CLR_RX_OVER 0x48
#define QUARKX1000_IC_CLR_TX_OVER 0x4C
#define QUARKX1000_IC_CLR_RD_REQ 0x50
#define QUARKX1000_IC_CLR_TX_ABRT 0x54
#define QUARKX1000_IC_CLR_ACTIVITY 0x5C
#define QUARKX1000_IC_CLR_STOP_DET 0x60
#define QUARKX1000_IC_CLR_START_DET 0x64
#define QUARKX1000_IC_ENABLE 0x6C
#define QUARKX1000_IC_STATUS 0x70
#define QUARKX1000_IC_TXFLR 0x74
#define QUARKX1000_IC_RXFLR 0x78
#define QUARKX1000_IC_SDA_HOLD 0x7C
#define QUARKX1000_IC_TX_ABRT_SOURCE 0x80
#define QUARKX1000_IC_ENABLE_STATUS 0x9C
#define QUARKX1000_IC_FS_SPKLEN 0xA0
/* IC_CON */
#define QUARKX1000_IC_CON_MASTER_MODE_SHIFT 0
#define QUARKX1000_IC_CON_MASTER_MODE_MASK 0x01
#define QUARKX1000_IC_CON_SPEED_SHIFT 1
#define QUARKX1000_IC_CON_SPEED_MASK 0x06
#define QUARKX1000_IC_CON_10BITADDR_MASTER_SHIFT 4
#define QUARKX1000_IC_CON_10BITADDR_MASTER_MASK 0x10
#define QUARKX1000_IC_CON_RESTART_EN_SHIFT 5
#define QUARKX1000_IC_CON_RESTART_EN_MASK 0x20
/* IC_TAR */
#define QUARKX1000_IC_TAR_SHIFT 0
#define QUARKX1000_IC_TAR_MASK 0x3FF
/* IC_DATA_CMD */
#define QUARKX1000_IC_DATA_CMD_DAT_SHIFT 0
#define QUARKX1000_IC_DATA_CMD_DAT_MASK 0x0FF
#define QUARKX1000_IC_DATA_CMD_CMD_SHIFT 8
#define QUARKX1000_IC_DATA_CMD_CMD_MASK 0x100
#define QUARKX1000_IC_DATA_CMD_STOP_SHIFT 9
#define QUARKX1000_IC_DATA_CMD_STOP_MASK 0x200
#define QUARKX1000_IC_DATA_CMD_RESTART_SHIFT 10
#define QUARKX1000_IC_DATA_CMD_RESTART_MASK 0x400
/* IC_SS_SCL_HCNT */
#define QUARKX1000_IC_SS_SCL_HCNT_SHIFT 0
#define QUARKX1000_IC_SS_SCL_HCNT_MASK 0xFFFF
/* IC_SS_SCL_LCNT */
#define QUARKX1000_IC_SS_SCL_LCNT_SHIFT 0
#define QUARKX1000_IC_SS_SCL_LCNT_MASK 0xFFFF
/* IC_FS_SCL_HCNT */
#define QUARKX1000_IC_FS_SCL_HCNT_SHIFT 0
#define QUARKX1000_IC_FS_SCL_HCNT_MASK 0xFFFF
/* IC_FS_SCL_LCNT */
#define QUARKX1000_IC_FS_SCL_LCNT_SHIFT 0
#define QUARKX1000_IC_FS_SCL_LCNT_MASK 0xFFFF
/* IC_INTR_STAT */
#define QUARKX1000_IC_INTR_STAT_RX_UNDER_SHIFT 0
#define QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK 0x001
#define QUARKX1000_IC_INTR_STAT_RX_OVER_SHIFT 1
#define QUARKX1000_IC_INTR_STAT_RX_OVER_MASK 0x002
#define QUARKX1000_IC_INTR_STAT_RX_FULL_SHIFT 2
#define QUARKX1000_IC_INTR_STAT_RX_FULL_MASK 0x004
#define QUARKX1000_IC_INTR_STAT_TX_OVER_SHIFT 3
#define QUARKX1000_IC_INTR_STAT_TX_OVER_MASK 0x008
#define QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT 4
#define QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK 0x010
#define QUARKX1000_IC_INTR_STAT_RD_REQ_SHIFT 5
#define QUARKX1000_IC_INTR_STAT_RD_REQ_MASK 0x020
#define QUARKX1000_IC_INTR_STAT_TX_ABRT_SHIFT 6
#define QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK 0x040
#define QUARKX1000_IC_INTR_STAT_ACTIVITY_SHIFT 8
#define QUARKX1000_IC_INTR_STAT_ACTIVITY_MASK 0x100
#define QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT 9
#define QUARKX1000_IC_INTR_STAT_STOP_DET_MASK 0x200
#define QUARKX1000_IC_INTR_STAT_START_DET_SHIFT 10
#define QUARKX1000_IC_INTR_STAT_START_DET_MASK 0x400
/* IC_ENABLE */
#define QUARKX1000_IC_ENABLE_SHIFT 0
#define QUARKX1000_IC_ENABLE_MASK 0x01
/* IC_STATUS */
#define QUARKX1000_IC_STATUS_ACTIVITY_SHIFT 0
#define QUARKX1000_IC_STATUS_ACTIVITY_MASK 0x01
#define QUARKX1000_IC_STATUS_TFNF_SHIFT 1
#define QUARKX1000_IC_STATUS_TFNF_MASK 0x02
#define QUARKX1000_IC_STATUS_TFE_SHIFT 2
#define QUARKX1000_IC_STATUS_TFE_MASK 0x04
#define QUARKX1000_IC_STATUS_RFNE_SHIFT 3
#define QUARKX1000_IC_STATUS_RFNE_MASK 0x08
#define QUARKX1000_IC_STATUS_RFF_SHIFT 4
#define QUARKX1000_IC_STATUS_RFF_MASK 0x10
#define QUARKX1000_IC_STATUS_MST_ACTIVITY_SHIFT 5
#define QUARKX1000_IC_STATUS_MST_ACTIVITY_MASK 0x20
/* IC_TXFLR */
#define QUARKX1000_IC_TXFLR_SHIFT 0
#define QUARKX1000_IC_TXFLR_MASK 0x1F
/* IC_RXFLR */
#define QUARKX1000_IC_RXFLR_SHIFT 0
#define QUARKX1000_IC_RXFLR_MASK 0x1F
/* IC_FS_SPKLEN */
#define QUARKX1000_IC_FS_SPKLEN_SHIFT 0
#define QUARKX1000_IC_FS_SPKLEN_MASK 0xFF
#endif /* CPU_X86_DRIVERS_QUARKX1000_I2C_REGISTERS_H_ */

View File

@ -0,0 +1,512 @@
/*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "contiki.h"
#include "i2c.h"
#include "i2c-registers.h"
#include "interrupt.h"
#include "pic.h"
#define I2C_CLOCK_SPEED 25 /* kHz */
#define I2C_FIFO_DEPTH 16
#define I2C_STD_HCNT (I2C_CLOCK_SPEED * 4)
#define I2C_STD_LCNT (I2C_CLOCK_SPEED * 5)
#define I2C_FS_HCNT (I2C_CLOCK_SPEED)
#define I2C_FS_LCNT (I2C_CLOCK_SPEED)
#define I2C_FS_SPKLEN_LCNT_OFFSET 8
#define I2C_FS_SPKLEN_HCNT_OFFSET 6
#define I2C_POLLING_TIMEOUT (CLOCK_SECOND / 10)
#define I2C_IRQ 9
#define I2C_INT PIC_INT(I2C_IRQ)
typedef enum {
I2C_DIRECTION_READ,
I2C_DIRECTION_WRITE
} I2C_DIRECTION;
struct i2c_internal_data {
struct quarkX1000_i2c_config config;
pci_driver_t pci;
I2C_DIRECTION direction;
uint8_t rx_len;
uint8_t *rx_buffer;
uint8_t tx_len;
uint8_t *tx_buffer;
uint8_t rx_tx_len;
uint32_t hcnt;
uint32_t lcnt;
};
static struct i2c_internal_data device;
static uint32_t
read(uint32_t base_addr, uint32_t offset)
{
return *(uint32_t*)(base_addr + offset);
}
static void
write(uint32_t base_addr, uint32_t offset, uint32_t val)
{
*(uint32_t*)(base_addr + offset) = val;
}
static uint32_t
get_value(uint32_t base_addr, uint32_t offset, uint32_t mask, uint32_t shift)
{
uint32_t register_value = *(uint32_t*)(base_addr + offset);
register_value &= ~(0xFFFFFFFF - mask);
return register_value >> shift;
}
static void
set_value(uint32_t base_addr, uint32_t offset, uint32_t mask, uint32_t shift, uint32_t value)
{
volatile uint32_t *register_value = (volatile uint32_t*)(base_addr + offset);
*register_value &= ~mask;
*register_value |= value << shift;
}
static void
i2c_data_read(void)
{
uint8_t i, rx_cnt;
if (device.rx_len == 0)
return;
rx_cnt = get_value(device.pci.mmio, QUARKX1000_IC_RXFLR,
QUARKX1000_IC_RXFLR_MASK, QUARKX1000_IC_RXFLR_SHIFT);
if (rx_cnt > device.rx_len)
rx_cnt = device.rx_len;
for (i = 0; i < rx_cnt; i++) {
device.rx_buffer[i] = get_value(device.pci.mmio, QUARKX1000_IC_DATA_CMD,
QUARKX1000_IC_DATA_CMD_DAT_MASK, QUARKX1000_IC_DATA_CMD_DAT_SHIFT);
}
device.rx_buffer += i;
device.rx_len -= i;
}
static void
i2c_data_send(void)
{
uint32_t data = 0;
uint8_t i, tx_cnt;
if (device.rx_tx_len == 0)
return;
tx_cnt = I2C_FIFO_DEPTH - get_value(device.pci.mmio, QUARKX1000_IC_TXFLR,
QUARKX1000_IC_TXFLR_MASK, QUARKX1000_IC_TXFLR_SHIFT);
if (tx_cnt > device.rx_tx_len)
tx_cnt = device.rx_tx_len;
for (i = 0; i < tx_cnt; i++) {
if (device.tx_len > 0) {
data = device.tx_buffer[i];
if (device.tx_len == 1)
data |= (device.rx_len > 0) ? QUARKX1000_IC_DATA_CMD_RESTART_MASK : QUARKX1000_IC_DATA_CMD_STOP_MASK;
device.tx_len -= 1;
} else {
data = QUARKX1000_IC_DATA_CMD_CMD_MASK;
if (device.rx_tx_len == 1)
data |= QUARKX1000_IC_DATA_CMD_STOP_MASK;
}
write(device.pci.mmio, QUARKX1000_IC_DATA_CMD, data);
device.rx_tx_len -= 1;
}
device.tx_buffer += i;
}
static void
i2c_isr(void)
{
if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK) {
i2c_data_read();
write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
if (device.direction == I2C_DIRECTION_WRITE) {
if (device.config.cb_tx)
device.config.cb_tx();
} else {
if (device.config.cb_rx)
device.config.cb_rx();
}
}
if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK) {
i2c_data_send();
if (device.rx_tx_len <= 0) {
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 0);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
}
}
if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_RX_FULL_MASK)
i2c_data_read();
if (read(device.pci.mmio, QUARKX1000_IC_INTR_STAT) & (QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK
| QUARKX1000_IC_INTR_STAT_TX_OVER_MASK | QUARKX1000_IC_INTR_STAT_RX_OVER_MASK
| QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK)) {
write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
if (device.config.cb_err)
device.config.cb_err();
}
}
int
quarkX1000_i2c_configure(struct quarkX1000_i2c_config *config)
{
uint32_t hcnt, lcnt;
uint8_t ic_fs_spklen;
device.config.speed = config->speed;
device.config.addressing_mode = config->addressing_mode;
device.config.cb_rx = config->cb_rx;
device.config.cb_tx = config->cb_tx;
device.config.cb_err = config->cb_err;
if (device.config.speed == QUARKX1000_I2C_SPEED_STANDARD) {
lcnt = I2C_STD_LCNT;
hcnt = I2C_STD_HCNT;
} else {
lcnt = I2C_FS_LCNT;
hcnt = I2C_FS_HCNT;
}
ic_fs_spklen = get_value(device.pci.mmio, QUARKX1000_IC_FS_SPKLEN,
QUARKX1000_IC_FS_SPKLEN_MASK, QUARKX1000_IC_FS_SPKLEN_SHIFT);
/* We adjust the Low Count and High Count based on the Spike Suppression Limit */
device.lcnt = (lcnt < (ic_fs_spklen + I2C_FS_SPKLEN_LCNT_OFFSET)) ? ic_fs_spklen + I2C_FS_SPKLEN_LCNT_OFFSET : lcnt;
device.hcnt = (hcnt < (ic_fs_spklen + I2C_FS_SPKLEN_HCNT_OFFSET)) ? ic_fs_spklen + I2C_FS_SPKLEN_HCNT_OFFSET : hcnt;
/* Clear interrupts. */
read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
return 0;
}
static int
i2c_setup(void)
{
/* Clear all values */
write(device.pci.mmio, QUARKX1000_IC_CON, 0);
/* Clear interrupts */
read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
/* Quark X1000 SoC I2C only supports master mode. */
set_value(device.pci.mmio, QUARKX1000_IC_CON,
QUARKX1000_IC_CON_MASTER_MODE_MASK, QUARKX1000_IC_CON_MASTER_MODE_SHIFT, 1);
/* Set restart enable */
set_value(device.pci.mmio, QUARKX1000_IC_CON,
QUARKX1000_IC_CON_RESTART_EN_MASK, QUARKX1000_IC_CON_RESTART_EN_SHIFT, 1);
/* Set addressing mode */
if (device.config.addressing_mode == QUARKX1000_I2C_ADDR_MODE_10BIT) {
set_value(device.pci.mmio, QUARKX1000_IC_CON,
QUARKX1000_IC_CON_10BITADDR_MASTER_MASK, QUARKX1000_IC_CON_10BITADDR_MASTER_SHIFT, 1);
}
if (device.config.speed == QUARKX1000_I2C_SPEED_STANDARD) {
set_value(device.pci.mmio, QUARKX1000_IC_SS_SCL_LCNT,
QUARKX1000_IC_SS_SCL_LCNT_MASK, QUARKX1000_IC_SS_SCL_LCNT_SHIFT, device.lcnt);
set_value(device.pci.mmio, QUARKX1000_IC_SS_SCL_HCNT,
QUARKX1000_IC_SS_SCL_HCNT_MASK, QUARKX1000_IC_SS_SCL_HCNT_SHIFT, device.hcnt);
set_value(device.pci.mmio, QUARKX1000_IC_CON,
QUARKX1000_IC_CON_SPEED_MASK, QUARKX1000_IC_CON_SPEED_SHIFT, 0x1);
} else {
set_value(device.pci.mmio, QUARKX1000_IC_FS_SCL_LCNT,
QUARKX1000_IC_FS_SCL_LCNT_MASK, QUARKX1000_IC_FS_SCL_LCNT_SHIFT, device.lcnt);
set_value(device.pci.mmio, QUARKX1000_IC_FS_SCL_HCNT,
QUARKX1000_IC_FS_SCL_HCNT_MASK, QUARKX1000_IC_FS_SCL_HCNT_SHIFT, device.hcnt);
set_value(device.pci.mmio, QUARKX1000_IC_CON,
QUARKX1000_IC_CON_SPEED_MASK, QUARKX1000_IC_CON_SPEED_SHIFT, 0x2);
}
return 0;
}
static void
i2c_operation_setup(uint8_t *write_buf, uint8_t write_len,
uint8_t *read_buf, uint8_t read_len, uint16_t addr)
{
device.rx_len = read_len;
device.rx_buffer = read_buf;
device.tx_len = write_len;
device.tx_buffer = write_buf;
device.rx_tx_len = device.rx_len + device.tx_len;
/* Disable controller */
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
i2c_setup();
/* Disable interrupts */
write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, 0);
/* Clear interrupts */
read(device.pci.mmio, QUARKX1000_IC_CLR_INTR);
/* Set address of target slave */
set_value(device.pci.mmio, QUARKX1000_IC_TAR,
QUARKX1000_IC_TAR_MASK, QUARKX1000_IC_TAR_SHIFT, addr);
}
/* This is an interrupt based operation */
static int
i2c_operation(uint8_t *write_buf, uint8_t write_len,
uint8_t *read_buf, uint8_t read_len, uint16_t addr)
{
if (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK)
return -1;
i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
/* Enable master TX and RX interrupts */
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_TX_OVER_MASK, QUARKX1000_IC_INTR_STAT_TX_OVER_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK, QUARKX1000_IC_INTR_STAT_TX_ABRT_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK, QUARKX1000_IC_INTR_STAT_RX_UNDER_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_RX_OVER_MASK, QUARKX1000_IC_INTR_STAT_RX_OVER_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_RX_FULL_MASK, QUARKX1000_IC_INTR_STAT_RX_FULL_SHIFT, 1);
set_value(device.pci.mmio, QUARKX1000_IC_INTR_MASK,
QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
/* Enable controller */
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 1);
return 0;
}
/* This is an interrupt based write */
int
quarkX1000_i2c_write(uint8_t *buf, uint8_t len, uint16_t addr)
{
device.direction = I2C_DIRECTION_WRITE;
return i2c_operation(buf, len, 0, 0, addr);
}
/* This is an interrupt based read */
int
quarkX1000_i2c_read(uint8_t *buf, uint8_t len, uint16_t addr)
{
device.direction = I2C_DIRECTION_READ;
return i2c_operation(0, 0, buf, len, addr);
}
static int
i2c_polling_operation(uint8_t *write_buf, uint8_t write_len,
uint8_t *read_buf, uint8_t read_len, uint16_t addr)
{
uint32_t start_time, intr_mask_stat;
if (!(read(device.pci.mmio, QUARKX1000_IC_CON) & QUARKX1000_IC_CON_MASTER_MODE_MASK))
return -1;
/* Wait i2c idle */
start_time = clock_seconds();
while (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
return -1;
}
}
/* Get interrupt mask to restore in the end of polling operation */
intr_mask_stat = read(device.pci.mmio, QUARKX1000_IC_INTR_MASK);
i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
/* Enable controller */
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 1);
/* Transmit */
if (device.tx_len != 0) {
while (device.tx_len > 0) {
start_time = clock_seconds();
while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFNF_MASK)) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
return -1;
}
}
i2c_data_send();
}
start_time = clock_seconds();
while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFE_MASK)) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
return -1;
}
}
}
i2c_data_send();
/* Receive */
if (device.rx_len != 0) {
while (device.rx_len > 0) {
start_time = clock_seconds();
while (!(read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_RFNE_MASK)) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
return -1;
}
}
i2c_data_read();
}
}
/* Stop Det */
start_time = clock_seconds();
while (!(read(device.pci.mmio, QUARKX1000_IC_RAW_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK)) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
return -1;
}
}
read(device.pci.mmio, QUARKX1000_IC_CLR_STOP_DET);
/* Wait i2c idle */
start_time = clock_seconds();
while (read(device.pci.mmio, QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
return -1;
}
}
/* Disable controller */
set_value(device.pci.mmio, QUARKX1000_IC_ENABLE,
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
/* Restore interrupt mask */
write(device.pci.mmio, QUARKX1000_IC_INTR_MASK, intr_mask_stat);
return 0;
}
int
quarkX1000_i2c_polling_write(uint8_t *buf, uint8_t len, uint16_t addr)
{
device.direction = I2C_DIRECTION_WRITE;
return i2c_polling_operation(buf, len, 0, 0, addr);
}
int
quarkX1000_i2c_polling_read(uint8_t *buf, uint8_t len, uint16_t addr)
{
device.direction = I2C_DIRECTION_READ;
return i2c_polling_operation(0, 0, buf, len ,addr);
}
int
quarkX1000_i2c_is_available(void)
{
return device.pci.mmio ? 1 : 0;
}
static void
i2c_handler()
{
i2c_isr();
pic_eoi(I2C_IRQ);
}
int
quarkX1000_i2c_init(void)
{
pci_config_addr_t pci_addr;
pci_addr.raw = 0;
pci_addr.bus = 0;
pci_addr.dev = 21;
pci_addr.func = 2;
pci_addr.reg_off = PCI_CONFIG_REG_BAR0;
pci_command_enable(pci_addr, PCI_CMD_1_MEM_SPACE_EN);
SET_INTERRUPT_HANDLER(I2C_INT, 0, i2c_handler);
if (pci_irq_agent_set_pirq(IRQAGENT3, INTC, PIRQC) < 0)
return -1;
pci_pirq_set_irq(PIRQC, I2C_IRQ, 1);
pci_init(&device.pci, pci_addr, 0);
pic_unmask_irq(I2C_IRQ);
return 0;
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CPU_X86_DRIVERS_QUARKX1000_I2C_H_
#define CPU_X86_DRIVERS_QUARKX1000_I2C_H_
#include "pci.h"
typedef enum {
QUARKX1000_I2C_SPEED_STANDARD,
QUARKX1000_I2C_SPEED_FAST
} QUARKX1000_I2C_SPEED;
typedef enum {
QUARKX1000_I2C_ADDR_MODE_7BIT,
QUARKX1000_I2C_ADDR_MODE_10BIT
} QUARKX1000_I2C_ADDR_MODE;
typedef void (*quarkX1000_i2c_callback)(void);
struct quarkX1000_i2c_config {
QUARKX1000_I2C_SPEED speed;
QUARKX1000_I2C_ADDR_MODE addressing_mode;
quarkX1000_i2c_callback cb_rx;
quarkX1000_i2c_callback cb_tx;
quarkX1000_i2c_callback cb_err;
};
int quarkX1000_i2c_init(void);
int quarkX1000_i2c_configure(struct quarkX1000_i2c_config *config);
int quarkX1000_i2c_is_available(void);
int quarkX1000_i2c_read(uint8_t *buf, uint8_t len, uint16_t addr);
int quarkX1000_i2c_write(uint8_t *buf, uint8_t len, uint16_t addr);
int quarkX1000_i2c_polling_read(uint8_t *buf, uint8_t len, uint16_t addr);
int quarkX1000_i2c_polling_write(uint8_t *buf, uint8_t len, uint16_t addr);
#endif /* CPU_X86_DRIVERS_QUARKX1000_I2C_H_ */