2015-09-30 14:32:16 +02:00
|
|
|
/*
|
2016-01-07 02:00:45 +01:00
|
|
|
* Copyright (C) 2015-2016, Intel Corporation. All rights reserved.
|
2015-09-30 14:32:16 +02:00
|
|
|
*
|
|
|
|
* 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"
|
2016-01-07 02:00:45 +01:00
|
|
|
|
2015-09-30 14:32:16 +02:00
|
|
|
#include "i2c-registers.h"
|
2015-08-10 17:34:02 +02:00
|
|
|
#include "paging.h"
|
2016-01-07 02:00:45 +01:00
|
|
|
#include "shared-isr.h"
|
2015-08-10 17:34:02 +02:00
|
|
|
#include "syscalls.h"
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2015-08-08 00:43:10 +02:00
|
|
|
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__PAGING
|
2015-08-10 17:34:02 +02:00
|
|
|
#define MMIO_SZ MIN_PAGE_SIZE
|
2015-08-08 00:43:10 +02:00
|
|
|
#else
|
|
|
|
#define MMIO_SZ (QUARKX1000_IC_HIGHEST + 4)
|
|
|
|
#endif
|
2015-08-10 17:34:02 +02:00
|
|
|
|
2015-09-30 14:32:16 +02:00
|
|
|
typedef enum {
|
|
|
|
I2C_DIRECTION_READ,
|
|
|
|
I2C_DIRECTION_WRITE
|
|
|
|
} I2C_DIRECTION;
|
|
|
|
|
2015-08-10 17:34:02 +02:00
|
|
|
PROT_DOMAINS_ALLOC(pci_driver_t, drv);
|
|
|
|
|
2016-01-06 08:05:09 +01:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2015-09-30 14:32:16 +02:00
|
|
|
struct i2c_internal_data {
|
|
|
|
struct quarkX1000_i2c_config config;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-08-10 17:34:02 +02:00
|
|
|
static int inited = 0;
|
|
|
|
|
|
|
|
void quarkX1000_i2c_mmin(uint32_t offset, uint32_t *res);
|
|
|
|
SYSCALLS_DEFINE_SINGLETON(quarkX1000_i2c_mmin, drv,
|
|
|
|
uint32_t offset, uint32_t *res)
|
|
|
|
{
|
|
|
|
uint32_t *loc_res;
|
|
|
|
|
|
|
|
PROT_DOMAINS_VALIDATE_PTR(loc_res, res, sizeof(*res));
|
|
|
|
if(QUARKX1000_IC_HIGHEST < offset) {
|
|
|
|
halt();
|
|
|
|
}
|
|
|
|
|
2015-08-08 00:43:10 +02:00
|
|
|
prot_domains_enable_mmio();
|
2015-08-10 17:34:02 +02:00
|
|
|
PCI_MMIO_READL(drv, *loc_res, offset);
|
2015-08-08 00:43:10 +02:00
|
|
|
prot_domains_disable_mmio();
|
2015-08-10 17:34:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t
|
2015-10-23 18:34:17 +02:00
|
|
|
read(uint32_t offset)
|
2015-09-30 14:32:16 +02:00
|
|
|
{
|
2015-10-23 18:34:17 +02:00
|
|
|
uint32_t res;
|
2015-08-10 17:34:02 +02:00
|
|
|
quarkX1000_i2c_mmin(offset, &res);
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
return res;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
2015-08-10 17:34:02 +02:00
|
|
|
void quarkX1000_i2c_mmout(uint32_t offset, uint32_t val);
|
|
|
|
SYSCALLS_DEFINE_SINGLETON(quarkX1000_i2c_mmout, drv,
|
|
|
|
uint32_t offset, uint32_t val)
|
|
|
|
{
|
|
|
|
if(QUARKX1000_IC_HIGHEST < offset) {
|
|
|
|
halt();
|
|
|
|
}
|
|
|
|
|
2015-08-08 00:43:10 +02:00
|
|
|
prot_domains_enable_mmio();
|
2015-08-10 17:34:02 +02:00
|
|
|
PCI_MMIO_WRITEL(drv, offset, val);
|
2015-08-08 00:43:10 +02:00
|
|
|
prot_domains_disable_mmio();
|
2015-08-10 17:34:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2015-10-23 18:34:17 +02:00
|
|
|
write(uint32_t offset, uint32_t val)
|
2015-09-30 14:32:16 +02:00
|
|
|
{
|
2015-08-10 17:34:02 +02:00
|
|
|
quarkX1000_i2c_mmout(offset, val);
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
2015-10-23 18:34:17 +02:00
|
|
|
get_value(uint32_t offset, uint32_t mask, uint32_t shift)
|
2015-09-30 14:32:16 +02:00
|
|
|
{
|
2015-10-23 18:34:17 +02:00
|
|
|
uint32_t register_value = read(offset);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
register_value &= ~(0xFFFFFFFF - mask);
|
|
|
|
|
|
|
|
return register_value >> shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(uint32_t offset, uint32_t mask, uint32_t shift, uint32_t value)
|
2015-09-30 14:32:16 +02:00
|
|
|
{
|
2015-10-23 18:34:17 +02:00
|
|
|
uint32_t register_value = read(offset);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
register_value &= ~mask;
|
|
|
|
register_value |= value << shift;
|
|
|
|
|
|
|
|
write(offset, register_value);
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
i2c_data_read(void)
|
|
|
|
{
|
|
|
|
uint8_t i, rx_cnt;
|
|
|
|
|
|
|
|
if (device.rx_len == 0)
|
|
|
|
return;
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
rx_cnt = get_value(QUARKX1000_IC_RXFLR,
|
2015-09-30 14:32:16 +02:00
|
|
|
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++) {
|
2015-10-23 18:34:17 +02:00
|
|
|
device.rx_buffer[i] = get_value(QUARKX1000_IC_DATA_CMD,
|
2015-09-30 14:32:16 +02:00
|
|
|
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;
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
tx_cnt = I2C_FIFO_DEPTH - get_value(QUARKX1000_IC_TXFLR,
|
2015-09-30 14:32:16 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_DATA_CMD, data);
|
2015-09-30 14:32:16 +02:00
|
|
|
device.rx_tx_len -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
device.tx_buffer += i;
|
|
|
|
}
|
|
|
|
|
2016-01-07 02:00:45 +01:00
|
|
|
static bool
|
2015-09-30 14:32:16 +02:00
|
|
|
i2c_isr(void)
|
|
|
|
{
|
2016-01-07 02:00:45 +01:00
|
|
|
bool handled = false;
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
if (read(QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK) {
|
2015-09-30 14:32:16 +02:00
|
|
|
i2c_data_read();
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_INTR_MASK, 0);
|
|
|
|
read(QUARKX1000_IC_CLR_INTR);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2016-01-07 02:00:45 +01:00
|
|
|
|
|
|
|
handled = true;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
if (read(QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK) {
|
2015-09-30 14:32:16 +02:00
|
|
|
i2c_data_send();
|
|
|
|
if (device.rx_tx_len <= 0) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 0);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
|
|
|
|
}
|
2016-01-07 02:00:45 +01:00
|
|
|
|
|
|
|
handled = true;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
2016-01-07 02:00:45 +01:00
|
|
|
if(read(QUARKX1000_IC_INTR_STAT) & QUARKX1000_IC_INTR_STAT_RX_FULL_MASK) {
|
2015-09-30 14:32:16 +02:00
|
|
|
i2c_data_read();
|
|
|
|
|
2016-01-07 02:00:45 +01:00
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
if (read(QUARKX1000_IC_INTR_STAT) & (QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK
|
2015-09-30 14:32:16 +02:00
|
|
|
| QUARKX1000_IC_INTR_STAT_TX_OVER_MASK | QUARKX1000_IC_INTR_STAT_RX_OVER_MASK
|
|
|
|
| QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK)) {
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_INTR_MASK, 0);
|
|
|
|
read(QUARKX1000_IC_CLR_INTR);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
if (device.config.cb_err)
|
|
|
|
device.config.cb_err();
|
2016-01-07 02:00:45 +01:00
|
|
|
|
|
|
|
handled = true;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
2016-01-07 02:00:45 +01:00
|
|
|
|
|
|
|
return handled;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
2016-01-06 08:05:09 +01:00
|
|
|
void
|
|
|
|
quarkX1000_i2c_configure(QUARKX1000_I2C_SPEED speed,
|
|
|
|
QUARKX1000_I2C_ADDR_MODE addressing_mode)
|
2015-09-30 14:32:16 +02:00
|
|
|
{
|
|
|
|
uint32_t hcnt, lcnt;
|
|
|
|
uint8_t ic_fs_spklen;
|
|
|
|
|
2016-01-06 08:05:09 +01:00
|
|
|
device.config.speed = speed;
|
|
|
|
device.config.addressing_mode = addressing_mode;
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
ic_fs_spklen = get_value(QUARKX1000_IC_FS_SPKLEN,
|
2015-09-30 14:32:16 +02:00
|
|
|
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. */
|
2015-10-23 18:34:17 +02:00
|
|
|
read(QUARKX1000_IC_CLR_INTR);
|
2016-01-06 08:05:09 +01:00
|
|
|
}
|
2015-09-30 14:32:16 +02:00
|
|
|
|
2016-01-06 08:05:09 +01:00
|
|
|
void
|
|
|
|
quarkX1000_i2c_set_callbacks(quarkX1000_i2c_callback rx,
|
|
|
|
quarkX1000_i2c_callback tx,
|
|
|
|
quarkX1000_i2c_callback err)
|
|
|
|
{
|
|
|
|
device.config.cb_rx = rx;
|
|
|
|
device.config.cb_tx = tx;
|
|
|
|
device.config.cb_err = err;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
i2c_setup(void)
|
|
|
|
{
|
|
|
|
/* Clear all values */
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_CON, 0);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
/* Clear interrupts */
|
2015-10-23 18:34:17 +02:00
|
|
|
read(QUARKX1000_IC_CLR_INTR);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
/* Quark X1000 SoC I2C only supports master mode. */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_CON,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_CON_MASTER_MODE_MASK, QUARKX1000_IC_CON_MASTER_MODE_SHIFT, 1);
|
|
|
|
|
|
|
|
/* Set restart enable */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_CON,
|
2015-09-30 14:32:16 +02:00
|
|
|
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) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_CON,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_CON_10BITADDR_MASTER_MASK, QUARKX1000_IC_CON_10BITADDR_MASTER_SHIFT, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device.config.speed == QUARKX1000_I2C_SPEED_STANDARD) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_SS_SCL_LCNT,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_SS_SCL_LCNT_MASK, QUARKX1000_IC_SS_SCL_LCNT_SHIFT, device.lcnt);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_SS_SCL_HCNT,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_SS_SCL_HCNT_MASK, QUARKX1000_IC_SS_SCL_HCNT_SHIFT, device.hcnt);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_CON,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_CON_SPEED_MASK, QUARKX1000_IC_CON_SPEED_SHIFT, 0x1);
|
|
|
|
} else {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_FS_SCL_LCNT,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_FS_SCL_LCNT_MASK, QUARKX1000_IC_FS_SCL_LCNT_SHIFT, device.lcnt);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_FS_SCL_HCNT,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_FS_SCL_HCNT_MASK, QUARKX1000_IC_FS_SCL_HCNT_SHIFT, device.hcnt);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_CON,
|
2015-09-30 14:32:16 +02:00
|
|
|
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 */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
|
|
|
|
i2c_setup();
|
|
|
|
|
|
|
|
/* Disable interrupts */
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_INTR_MASK, 0);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
/* Clear interrupts */
|
2015-10-23 18:34:17 +02:00
|
|
|
read(QUARKX1000_IC_CLR_INTR);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
/* Set address of target slave */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_TAR,
|
2015-09-30 14:32:16 +02:00
|
|
|
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)
|
|
|
|
{
|
2015-10-23 18:34:17 +02:00
|
|
|
if (read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK)
|
2015-09-30 14:32:16 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
|
|
|
|
|
|
|
|
/* Enable master TX and RX interrupts */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_TX_OVER_MASK, QUARKX1000_IC_INTR_STAT_TX_OVER_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_TX_EMPTY_MASK, QUARKX1000_IC_INTR_STAT_TX_EMPTY_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_TX_ABRT_MASK, QUARKX1000_IC_INTR_STAT_TX_ABRT_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_RX_UNDER_MASK, QUARKX1000_IC_INTR_STAT_RX_UNDER_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_RX_OVER_MASK, QUARKX1000_IC_INTR_STAT_RX_OVER_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_RX_FULL_MASK, QUARKX1000_IC_INTR_STAT_RX_FULL_SHIFT, 1);
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_INTR_MASK,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_INTR_STAT_STOP_DET_MASK, QUARKX1000_IC_INTR_STAT_STOP_DET_SHIFT, 1);
|
|
|
|
|
|
|
|
/* Enable controller */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
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;
|
|
|
|
|
2015-10-23 18:34:17 +02:00
|
|
|
if (!(read(QUARKX1000_IC_CON) & QUARKX1000_IC_CON_MASTER_MODE_MASK))
|
2015-09-30 14:32:16 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Wait i2c idle */
|
|
|
|
start_time = clock_seconds();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get interrupt mask to restore in the end of polling operation */
|
2015-10-23 18:34:17 +02:00
|
|
|
intr_mask_stat = read(QUARKX1000_IC_INTR_MASK);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
i2c_operation_setup(write_buf, write_len, read_buf, read_len, addr);
|
|
|
|
|
|
|
|
/* Enable controller */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 1);
|
|
|
|
|
|
|
|
/* Transmit */
|
|
|
|
if (device.tx_len != 0) {
|
|
|
|
while (device.tx_len > 0) {
|
|
|
|
start_time = clock_seconds();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (!(read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFNF_MASK)) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i2c_data_send();
|
|
|
|
}
|
|
|
|
|
|
|
|
start_time = clock_seconds();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (!(read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_TFE_MASK)) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
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();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (!(read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_RFNE_MASK)) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i2c_data_read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop Det */
|
|
|
|
start_time = clock_seconds();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (!(read(QUARKX1000_IC_RAW_INTR_STAT) & QUARKX1000_IC_INTR_STAT_STOP_DET_MASK)) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2015-10-23 18:34:17 +02:00
|
|
|
read(QUARKX1000_IC_CLR_STOP_DET);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
/* Wait i2c idle */
|
|
|
|
start_time = clock_seconds();
|
2015-10-23 18:34:17 +02:00
|
|
|
while (read(QUARKX1000_IC_STATUS) & QUARKX1000_IC_STATUS_ACTIVITY_MASK) {
|
2015-09-30 14:32:16 +02:00
|
|
|
if ((clock_seconds() - start_time) > I2C_POLLING_TIMEOUT) {
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable controller */
|
2015-10-23 18:34:17 +02:00
|
|
|
set_value(QUARKX1000_IC_ENABLE,
|
2015-09-30 14:32:16 +02:00
|
|
|
QUARKX1000_IC_ENABLE_MASK, QUARKX1000_IC_ENABLE_SHIFT, 0);
|
|
|
|
|
|
|
|
/* Restore interrupt mask */
|
2015-10-23 18:34:17 +02:00
|
|
|
write(QUARKX1000_IC_INTR_MASK, intr_mask_stat);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2015-08-10 17:34:02 +02:00
|
|
|
return inited;
|
2015-09-30 14:32:16 +02:00
|
|
|
}
|
|
|
|
|
2016-01-07 02:00:45 +01:00
|
|
|
DEFINE_SHARED_IRQ(I2C_IRQ, IRQAGENT3, INTC, PIRQC, i2c_isr);
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-08-10 17:34:02 +02:00
|
|
|
PROT_DOMAINS_INIT_ID(drv);
|
|
|
|
pci_init(&drv, pci_addr, MMIO_SZ, 0, 0);
|
|
|
|
SYSCALLS_INIT(quarkX1000_i2c_mmin);
|
|
|
|
SYSCALLS_AUTHZ(quarkX1000_i2c_mmin, drv);
|
|
|
|
SYSCALLS_INIT(quarkX1000_i2c_mmout);
|
|
|
|
SYSCALLS_AUTHZ(quarkX1000_i2c_mmout, drv);
|
|
|
|
|
|
|
|
inited = 1;
|
2015-09-30 14:32:16 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|