You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
841 lines
20 KiB
841 lines
20 KiB
/* dw_i2c.c - I2C file for Design Ware */ |
|
|
|
/* |
|
* Copyright (c) 2015 Intel Corporation |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
#include <stddef.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
#include <stdbool.h> |
|
|
|
#include <i2c.h> |
|
#include <nanokernel.h> |
|
#include <init.h> |
|
#include <arch/cpu.h> |
|
#include <string.h> |
|
|
|
#include <board.h> |
|
#include <errno.h> |
|
#include <sys_io.h> |
|
|
|
#include <misc/util.h> |
|
|
|
#ifdef CONFIG_SHARED_IRQ |
|
#include <shared_irq.h> |
|
#endif |
|
|
|
#ifdef CONFIG_IOAPIC |
|
#include <drivers/ioapic.h> |
|
#endif |
|
|
|
#include "i2c_dw.h" |
|
#include "i2c_dw_registers.h" |
|
|
|
#ifndef CONFIG_I2C_DEBUG |
|
#define DBG(...) { ; } |
|
#else |
|
#if defined(CONFIG_STDOUT_CONSOLE) |
|
#include <stdio.h> |
|
#define DBG printf |
|
#else |
|
#define DBG printk |
|
#endif /* CONFIG_STDOUT_CONSOLE */ |
|
#endif /* CONFIG_I2C_DEBUG */ |
|
|
|
static inline void _i2c_dw_data_ask(struct device *dev) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
uint32_t data; |
|
uint8_t tx_empty; |
|
int8_t rx_empty; |
|
uint8_t cnt; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
/* No more bytes to request, so command queue is no longer needed */ |
|
if (dw->request_bytes == 0) { |
|
regs->ic_intr_mask.bits.tx_empty = 0; |
|
return; |
|
} |
|
|
|
/* How many bytes we can actually ask */ |
|
rx_empty = (I2C_DW_FIFO_DEPTH - regs->ic_rxflr) - dw->rx_pending; |
|
|
|
if (rx_empty < 0) { |
|
/* RX FIFO expected to be full. |
|
* So don't request any bytes, yet. |
|
*/ |
|
return; |
|
} |
|
|
|
/* How many empty slots in TX FIFO (as command queue) */ |
|
tx_empty = I2C_DW_FIFO_DEPTH - regs->ic_txflr; |
|
|
|
/* Figure out how many bytes we can request */ |
|
cnt = min(I2C_DW_FIFO_DEPTH, dw->request_bytes); |
|
cnt = min(min(tx_empty, rx_empty), cnt); |
|
|
|
while (cnt > 0) { |
|
/* Tell controller to get another byte */ |
|
data = IC_DATA_CMD_CMD; |
|
|
|
/* Send RESTART if needed */ |
|
if (dw->xfr_flags & I2C_MSG_RESTART) { |
|
data |= IC_DATA_CMD_RESTART; |
|
dw->xfr_flags &= ~(I2C_MSG_RESTART); |
|
} |
|
|
|
/* After receiving the last byte, send STOP if needed */ |
|
if ((dw->xfr_flags & I2C_MSG_STOP) |
|
&& (dw->request_bytes == 1)) { |
|
data |= IC_DATA_CMD_STOP; |
|
} |
|
|
|
regs->ic_data_cmd.raw = data; |
|
|
|
dw->rx_pending++; |
|
dw->request_bytes--; |
|
cnt--; |
|
} |
|
} |
|
|
|
static void _i2c_dw_data_read(struct device *dev) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
while (regs->ic_status.bits.rfne && (dw->xfr_len > 0)) { |
|
dw->xfr_buf[0] = regs->ic_data_cmd.raw; |
|
|
|
dw->xfr_buf++; |
|
dw->xfr_len--; |
|
dw->rx_pending--; |
|
|
|
if (dw->xfr_len == 0) { |
|
break; |
|
} |
|
} |
|
|
|
/* Nothing to receive anymore */ |
|
if (dw->xfr_len == 0) { |
|
dw->state &= ~I2C_DW_CMD_RECV; |
|
return; |
|
} |
|
} |
|
|
|
|
|
static int _i2c_dw_data_send(struct device *dev) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
uint32_t data = 0; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
/* Nothing to send anymore, mask the interrupt */ |
|
if (dw->xfr_len == 0) { |
|
regs->ic_intr_mask.bits.tx_empty = 0; |
|
|
|
dw->state &= ~I2C_DW_CMD_SEND; |
|
|
|
return DEV_OK; |
|
} |
|
|
|
while (regs->ic_status.bits.tfnf && (dw->xfr_len > 0)) { |
|
/* We have something to transmit to a specific host */ |
|
data = dw->xfr_buf[0]; |
|
|
|
/* Send RESTART if needed */ |
|
if (dw->xfr_flags & I2C_MSG_RESTART) { |
|
data |= IC_DATA_CMD_RESTART; |
|
dw->xfr_flags &= ~(I2C_MSG_RESTART); |
|
} |
|
|
|
/* Send STOP if needed */ |
|
if ((dw->xfr_len == 1) && (dw->xfr_flags & I2C_MSG_STOP)) { |
|
data |= IC_DATA_CMD_STOP; |
|
} |
|
|
|
regs->ic_data_cmd.raw = data; |
|
|
|
dw->xfr_len--; |
|
dw->xfr_buf++; |
|
|
|
if (regs->ic_intr_stat.bits.tx_abrt) { |
|
return DEV_FAIL; |
|
} |
|
} |
|
|
|
return DEV_OK; |
|
} |
|
|
|
static inline void _i2c_dw_transfer_complete(struct device *dev) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
uint32_t value; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
regs->ic_intr_mask.raw = DW_DISABLE_ALL_I2C_INT; |
|
value = regs->ic_clr_intr; |
|
|
|
device_sync_call_complete(&dw->sync); |
|
} |
|
|
|
void i2c_dw_isr(struct device *port) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = port->config->config_info; |
|
struct i2c_dw_dev_config * const dw = port->driver_data; |
|
union ic_interrupt_register intr_stat; |
|
uint32_t value; |
|
int ret = DEV_OK; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
/* Cache ic_intr_stat for processing, so there is no need to read |
|
* the register multiple times. |
|
*/ |
|
intr_stat.raw = regs->ic_intr_stat.raw; |
|
|
|
#if CONFIG_SHARED_IRQ |
|
/* If using with shared IRQ, this function will be called |
|
* by the shared IRQ driver. So check here if the interrupt |
|
* is coming from the I2C controller (or somewhere else). |
|
*/ |
|
if (!intr_stat.raw) { |
|
return; |
|
} |
|
#endif |
|
|
|
/* |
|
* Causes of an interrupt: |
|
* - STOP condition is detected |
|
* - Transfer is aborted |
|
* - Transmit FIFO is empy |
|
* - Transmit FIFO is overflowing |
|
* - Receive FIFO is full |
|
* - Receive FIFO overflow |
|
* - Received FIFO underrun |
|
* - Transmit data required (tx_req) |
|
* - Receive data available (rx_avail) |
|
*/ |
|
|
|
DBG("I2C: interrupt received\n"); |
|
|
|
/* Check if we are configured as a master device */ |
|
if (regs->ic_con.bits.master_mode) { |
|
/* Bail early if there is any error. */ |
|
if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER | |
|
DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_UNDER) & |
|
intr_stat.raw) { |
|
dw->state = I2C_DW_CMD_ERROR; |
|
goto done; |
|
} |
|
|
|
/* Check if the RX FIFO reached threshold */ |
|
if (intr_stat.bits.rx_full) { |
|
_i2c_dw_data_read(port); |
|
} |
|
|
|
/* Check if the TX FIFO is ready for commands. |
|
* TX FIFO also serves as command queue where read requests |
|
* are written to TX FIFO. |
|
*/ |
|
if (intr_stat.bits.tx_empty) { |
|
if ((dw->xfr_flags & I2C_MSG_RW_MASK) |
|
== I2C_MSG_WRITE) { |
|
ret = _i2c_dw_data_send(port); |
|
} else { |
|
_i2c_dw_data_ask(port); |
|
} |
|
|
|
/* If STOP is not expected, finish processing this |
|
* message if there is nothing left to do anymore. |
|
*/ |
|
if (((dw->xfr_len == 0) |
|
&& !(dw->xfr_flags & I2C_MSG_STOP)) |
|
|| (ret != DEV_OK)) { |
|
goto done; |
|
} |
|
} |
|
} else { /* we must be configured as a slave device */ |
|
|
|
/* We have a read requested by the master device */ |
|
if (intr_stat.bits.rd_req && |
|
(!dw->app_config.bits.is_slave_read)) { |
|
|
|
/* data is not ready to send */ |
|
if (intr_stat.bits.tx_abrt) { |
|
/* clear the TX_ABRT interrupt */ |
|
value = regs->ic_clr_tx_abrt; |
|
} |
|
|
|
_i2c_dw_data_send(port); |
|
value = regs->ic_clr_rd_req; |
|
} |
|
|
|
/* The slave device is ready to receive */ |
|
if (intr_stat.bits.rx_full && |
|
dw->app_config.bits.is_slave_read) { |
|
_i2c_dw_data_read(port); |
|
} |
|
} |
|
|
|
/* STOP detected: finish processing this message */ |
|
if (intr_stat.bits.stop_det) { |
|
value = regs->ic_clr_stop_det; |
|
goto done; |
|
} |
|
|
|
return; |
|
|
|
done: |
|
_i2c_dw_transfer_complete(port); |
|
} |
|
|
|
|
|
static int _i2c_dw_setup(struct device *dev, uint16_t slave_address) |
|
{ |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
uint32_t value; |
|
union ic_con_register ic_con; |
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
ic_con.raw = 0; |
|
|
|
/* Disable the device controller to be able set TAR */ |
|
regs->ic_enable.bits.enable = 0; |
|
|
|
/* Disable interrupts */ |
|
regs->ic_intr_mask.raw = 0; |
|
|
|
/* Clear interrupts */ |
|
value = regs->ic_clr_intr; |
|
|
|
/* Set master or slave mode - (initialization = slave) */ |
|
if (dw->app_config.bits.is_master_device) { |
|
/* |
|
* Make sure to set both the master_mode and slave_disable_bit |
|
* to both 0 or both 1 |
|
*/ |
|
DBG("I2C: host configured as Master Device\n"); |
|
ic_con.bits.master_mode = 1; |
|
ic_con.bits.slave_disable = 1; |
|
} |
|
|
|
ic_con.bits.restart_en = 1; |
|
|
|
/* Set addressing mode - (initialization = 7 bit) */ |
|
if (dw->app_config.bits.use_10_bit_addr) { |
|
DBG("I2C: using 10-bit address\n"); |
|
ic_con.bits.addr_master_10bit = 1; |
|
ic_con.bits.addr_slave_10bit = 1; |
|
} |
|
|
|
/* Setup the clock frequency and speed mode */ |
|
switch (dw->app_config.bits.speed) { |
|
case I2C_SPEED_STANDARD: |
|
DBG("I2C: speed set to STANDARD\n"); |
|
regs->ic_ss_scl_lcnt = dw->lcnt; |
|
regs->ic_ss_scl_hcnt = dw->hcnt; |
|
ic_con.bits.speed = I2C_DW_SPEED_STANDARD; |
|
|
|
break; |
|
case I2C_SPEED_FAST: |
|
/* fall through */ |
|
case I2C_SPEED_FAST_PLUS: |
|
DBG("I2C: speed set to FAST or FAST_PLUS\n"); |
|
regs->ic_fs_scl_lcnt = dw->lcnt; |
|
regs->ic_fs_scl_hcnt = dw->hcnt; |
|
ic_con.bits.speed = I2C_DW_SPEED_FAST; |
|
|
|
break; |
|
case I2C_SPEED_HIGH: |
|
if (!dw->support_hs_mode) { |
|
return DEV_INVALID_CONF; |
|
} |
|
|
|
DBG("I2C: speed set to HIGH\n"); |
|
regs->ic_hs_scl_lcnt = dw->lcnt; |
|
regs->ic_hs_scl_hcnt = dw->hcnt; |
|
ic_con.bits.speed = I2C_DW_SPEED_HIGH; |
|
|
|
break; |
|
default: |
|
DBG("I2C: invalid speed requested\n"); |
|
return DEV_INVALID_CONF; |
|
} |
|
|
|
DBG("I2C: lcnt = %d\n", dw->lcnt); |
|
DBG("I2C: hcnt = %d\n", dw->hcnt); |
|
|
|
/* Set the IC_CON register */ |
|
regs->ic_con = ic_con; |
|
|
|
/* Set RX fifo threshold level. |
|
* Setting it to zero automatically triggers interrupt |
|
* RX_FULL whenever there is data received. |
|
* |
|
* TODO: extend the threshold for multi-byte RX. |
|
*/ |
|
regs->ic_rx_tl = 0; |
|
|
|
/* Set TX fifo threshold level. |
|
* TX_EMPTY interrupt is triggered only when the |
|
* TX FIFO is truly empty. So that we can let |
|
* the controller do the transfers for longer period |
|
* before we need to fill the FIFO again. This may |
|
* cause some pauses during transfers, but this keeps |
|
* the device from interrupting often. |
|
*/ |
|
regs->ic_tx_tl = 0; |
|
|
|
if (regs->ic_con.bits.master_mode) { |
|
/* Set address of target slave */ |
|
regs->ic_tar.bits.ic_tar = slave_address; |
|
} else { |
|
/* Set slave address for device */ |
|
regs->ic_sar.bits.ic_sar = slave_address; |
|
} |
|
|
|
return DEV_OK; |
|
} |
|
|
|
static int i2c_dw_transfer(struct device *dev, |
|
struct i2c_msg *msgs, uint8_t num_msgs, |
|
uint16_t slave_address) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
struct i2c_msg *cur_msg = msgs; |
|
uint8_t msg_left = num_msgs; |
|
uint8_t pflags; |
|
int ret; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
/* Why bother processing no messages */ |
|
if (!msgs || !num_msgs) { |
|
return DEV_INVALID_OP; |
|
} |
|
|
|
/* First step, check if there is current activity */ |
|
if ((regs->ic_status.bits.activity) || (dw->state & I2C_DW_BUSY)) { |
|
return DEV_FAIL; |
|
} |
|
|
|
dw->state |= I2C_DW_BUSY; |
|
|
|
ret = _i2c_dw_setup(dev, slave_address); |
|
if (ret) { |
|
dw->state = I2C_DW_STATE_READY; |
|
return ret; |
|
} |
|
|
|
/* Enable controller */ |
|
regs->ic_enable.bits.enable = 1; |
|
|
|
/* Process all the messages */ |
|
while (msg_left > 0) { |
|
pflags = dw->xfr_flags; |
|
|
|
dw->xfr_buf = cur_msg->buf; |
|
dw->xfr_len = cur_msg->len; |
|
dw->xfr_flags = cur_msg->flags; |
|
dw->rx_pending = 0; |
|
|
|
/* Need to RESTART if changing transfer direction */ |
|
if ((pflags & I2C_MSG_RW_MASK) |
|
!= (dw->xfr_flags & I2C_MSG_RW_MASK)) { |
|
dw->xfr_flags |= I2C_MSG_RESTART; |
|
} |
|
|
|
/* Send STOP if this is the last message */ |
|
if (msg_left == 1) { |
|
dw->xfr_flags |= I2C_MSG_STOP; |
|
} |
|
|
|
dw->state &= ~(I2C_DW_CMD_SEND | I2C_DW_CMD_RECV); |
|
|
|
if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { |
|
dw->state |= I2C_DW_CMD_SEND; |
|
dw->request_bytes = 0; |
|
} else { |
|
dw->state |= I2C_DW_CMD_RECV; |
|
dw->request_bytes = dw->xfr_len; |
|
} |
|
|
|
/* Enable interrupts to trigger ISR */ |
|
if (regs->ic_con.bits.master_mode) { |
|
/* Enable necessary interrupts */ |
|
regs->ic_intr_mask.raw = (DW_ENABLE_TX_INT_I2C_MASTER | |
|
DW_ENABLE_RX_INT_I2C_MASTER); |
|
} else { |
|
/* Enable necessary interrupts */ |
|
regs->ic_intr_mask.raw = DW_ENABLE_TX_INT_I2C_SLAVE; |
|
} |
|
|
|
/* Wait for transfer to be done */ |
|
device_sync_call_wait(&dw->sync); |
|
|
|
if (dw->state & I2C_DW_CMD_ERROR) { |
|
ret = DEV_FAIL; |
|
break; |
|
} |
|
|
|
/* Something wrong if there is something left to do */ |
|
if (dw->xfr_len > 0) { |
|
ret = DEV_FAIL; |
|
break; |
|
} |
|
|
|
cur_msg++; |
|
msg_left--; |
|
} |
|
|
|
dw->state = I2C_DW_STATE_READY; |
|
|
|
return ret; |
|
} |
|
|
|
static int i2c_dw_runtime_configure(struct device *dev, uint32_t config) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = dev->config->config_info; |
|
struct i2c_dw_dev_config * const dw = dev->driver_data; |
|
uint32_t value = 0; |
|
uint32_t rc = DEV_OK; |
|
|
|
volatile struct i2c_dw_registers * const regs = |
|
(struct i2c_dw_registers *)rom->base_address; |
|
|
|
dw->app_config.raw = config; |
|
|
|
/* Make sure we have a supported speed for the DesignWare model */ |
|
/* and have setup the clock frequency and speed mode */ |
|
switch (dw->app_config.bits.speed) { |
|
case I2C_SPEED_STANDARD: |
|
/* Following the directions on DW spec page 59, IC_SS_SCL_LCNT |
|
* must have register values larger than IC_FS_SPKLEN + 7 |
|
*/ |
|
if (I2C_STD_LCNT <= (regs->ic_fs_spklen + 7)) { |
|
value = regs->ic_fs_spklen + 8; |
|
} else { |
|
value = I2C_STD_LCNT; |
|
} |
|
|
|
dw->lcnt = value; |
|
|
|
/* Following the directions on DW spec page 59, IC_SS_SCL_HCNT |
|
* must have register values larger than IC_FS_SPKLEN + 5 |
|
*/ |
|
if (I2C_STD_HCNT <= (regs->ic_fs_spklen + 5)) { |
|
value = regs->ic_fs_spklen + 6; |
|
} else { |
|
value = I2C_STD_HCNT; |
|
} |
|
|
|
dw->hcnt = value; |
|
break; |
|
case I2C_SPEED_FAST: |
|
/* fall through */ |
|
case I2C_SPEED_FAST_PLUS: |
|
/* |
|
* Following the directions on DW spec page 59, IC_FS_SCL_LCNT |
|
* must have register values larger than IC_FS_SPKLEN + 7 |
|
*/ |
|
if (I2C_FS_LCNT <= (regs->ic_fs_spklen + 7)) { |
|
value = regs->ic_fs_spklen + 8; |
|
} else { |
|
value = I2C_FS_LCNT; |
|
} |
|
|
|
dw->lcnt = value; |
|
|
|
/* |
|
* Following the directions on DW spec page 59, IC_FS_SCL_HCNT |
|
* must have register values larger than IC_FS_SPKLEN + 5 |
|
*/ |
|
if (I2C_FS_HCNT <= (regs->ic_fs_spklen + 5)) { |
|
value = regs->ic_fs_spklen + 6; |
|
} else { |
|
value = I2C_FS_HCNT; |
|
} |
|
|
|
dw->hcnt = value; |
|
break; |
|
case I2C_SPEED_HIGH: |
|
if (dw->support_hs_mode) { |
|
if (I2C_HS_LCNT <= (regs->ic_hs_spklen + 7)) { |
|
value = regs->ic_hs_spklen + 8; |
|
} else { |
|
value = I2C_HS_LCNT; |
|
} |
|
|
|
dw->lcnt = value; |
|
|
|
if (I2C_HS_HCNT <= (regs->ic_hs_spklen + 5)) { |
|
value = regs->ic_hs_spklen + 6; |
|
} else { |
|
value = I2C_HS_HCNT; |
|
} |
|
|
|
dw->hcnt = value; |
|
} else { |
|
rc = DEV_INVALID_CONF; |
|
} |
|
break; |
|
default: |
|
/* TODO change */ |
|
rc = DEV_INVALID_CONF; |
|
} |
|
|
|
/* |
|
* Clear any interrupts currently waiting in the controller |
|
*/ |
|
value = regs->ic_clr_intr; |
|
|
|
/* |
|
* TEMPORARY HACK - The I2C does not work in any mode other than Master |
|
* currently. This "hack" forces us to always be configured for master |
|
* mode, until we can verify that Slave mode works correctly. |
|
*/ |
|
dw->app_config.bits.is_master_device = 1; |
|
|
|
return rc; |
|
} |
|
|
|
static int i2c_dw_suspend(struct device *dev) |
|
{ |
|
DBG("I2C: suspend called - function not yet implemented\n"); |
|
/* TODO - add this code */ |
|
return DEV_OK; |
|
} |
|
|
|
|
|
static int i2c_dw_resume(struct device *dev) |
|
{ |
|
DBG("I2C: resume called - function not yet implemented\n"); |
|
/* TODO - add this code */ |
|
return DEV_OK; |
|
} |
|
|
|
|
|
static struct i2c_driver_api funcs = { |
|
.configure = i2c_dw_runtime_configure, |
|
.transfer = i2c_dw_transfer, |
|
.suspend = i2c_dw_suspend, |
|
.resume = i2c_dw_resume, |
|
}; |
|
|
|
|
|
#ifdef CONFIG_PCI |
|
static inline int i2c_dw_pci_setup(struct device *dev) |
|
{ |
|
struct i2c_dw_rom_config *rom = dev->config->config_info; |
|
|
|
pci_bus_scan_init(); |
|
|
|
if (!pci_bus_scan(&rom->pci_dev)) { |
|
DBG("Could not find device\n"); |
|
return 0; |
|
} |
|
|
|
#ifdef CONFIG_PCI_ENUMERATION |
|
rom->base_address = rom->pci_dev.addr; |
|
rom->irq_num = rom->pci_dev.irq; |
|
#endif |
|
pci_enable_regs(&rom->pci_dev); |
|
|
|
pci_show(&rom->pci_dev); |
|
|
|
return 1; |
|
} |
|
#else |
|
#define i2c_dw_pci_setup(_unused_) (1) |
|
#endif /* CONFIG_PCI */ |
|
|
|
int i2c_dw_initialize(struct device *port) |
|
{ |
|
struct i2c_dw_rom_config const * const rom = port->config->config_info; |
|
struct i2c_dw_dev_config * const dev = port->driver_data; |
|
|
|
volatile struct i2c_dw_registers *regs; |
|
|
|
if (!i2c_dw_pci_setup(port)) { |
|
return DEV_NOT_CONFIG; |
|
} |
|
|
|
device_sync_call_init(&dev->sync); |
|
|
|
regs = (struct i2c_dw_registers *) rom->base_address; |
|
|
|
/* verify that we have a valid DesignWare register first */ |
|
if (regs->ic_comp_type != I2C_DW_MAGIC_KEY) { |
|
port->driver_api = NULL; |
|
DBG("I2C: DesignWare magic key not found, check base address."); |
|
DBG(" Stopping initialization\n"); |
|
return DEV_NOT_CONFIG; |
|
} |
|
|
|
port->driver_api = &funcs; |
|
|
|
/* |
|
* grab the default value on initialization. This should be set to the |
|
* IC_MAX_SPEED_MODE in the hardware. If it does support high speed we |
|
* can move provide support for it |
|
*/ |
|
if (regs->ic_con.bits.speed == I2C_DW_SPEED_HIGH) { |
|
DBG("I2C: high speed supported\n"); |
|
dev->support_hs_mode = true; |
|
} else { |
|
DBG("I2C: high speed NOT supported\n"); |
|
dev->support_hs_mode = false; |
|
} |
|
|
|
rom->config_func(port); |
|
|
|
if (i2c_dw_runtime_configure(port, dev->app_config.raw) != DEV_OK) { |
|
DBG("I2C: Cannot set default configuration 0x%x\n", |
|
dev->app_config.raw); |
|
return DEV_NOT_CONFIG; |
|
} |
|
|
|
dev->state = I2C_DW_STATE_READY; |
|
|
|
return DEV_OK; |
|
} |
|
|
|
#if defined(CONFIG_IOAPIC) |
|
#if defined(CONFIG_I2C_DW_IRQ_FALLING_EDGE) |
|
#define I2C_DW_IRQ_FLAGS (IOAPIC_EDGE | IOAPIC_LOW) |
|
#elif defined(CONFIG_I2C_DW_IRQ_RISING_EDGE) |
|
#define I2C_DW_IRQ_FLAGS (IOAPIC_EDGE | IOAPIC_HIGH) |
|
#elif defined(CONFIG_I2C_DW_IRQ_LEVEL_HIGH) |
|
#define I2C_DW_IRQ_FLAGS (IOAPIC_LEVEL | IOAPIC_HIGH) |
|
#elif defined(CONFIG_I2C_DW_IRQ_LEVEL_LOW) |
|
#define I2C_DW_IRQ_FLAGS (IOAPIC_LEVEL | IOAPIC_LOW) |
|
#endif |
|
#else |
|
#define I2C_DW_IRQ_FLAGS 0 |
|
#endif /* CONFIG_IOAPIC */ |
|
|
|
/* system bindings */ |
|
#if CONFIG_I2C_DW_0 |
|
void i2c_config_0(struct device *port); |
|
|
|
struct i2c_dw_rom_config i2c_config_dw_0 = { |
|
.base_address = CONFIG_I2C_DW_0_BASE, |
|
#ifdef CONFIG_I2C_DW_0_IRQ_DIRECT |
|
.irq_num = CONFIG_I2C_DW_0_IRQ, |
|
#endif |
|
.config_func = i2c_config_0, |
|
|
|
#ifdef CONFIG_GPIO_DW_0_IRQ_SHARED |
|
.shared_irq_dev_name = CONFIG_I2C_DW_0_IRQ_SHARED_NAME, |
|
#endif |
|
|
|
#if CONFIG_PCI |
|
.pci_dev.class_type = CONFIG_I2C_DW_CLASS, |
|
.pci_dev.bus = CONFIG_I2C_DW_0_BUS, |
|
.pci_dev.dev = CONFIG_I2C_DW_0_DEV, |
|
.pci_dev.vendor_id = CONFIG_I2C_DW_VENDOR_ID, |
|
.pci_dev.device_id = CONFIG_I2C_DW_DEVICE_ID, |
|
.pci_dev.function = CONFIG_I2C_DW_0_FUNCTION, |
|
.pci_dev.bar = CONFIG_I2C_DW_0_BAR, |
|
#endif |
|
}; |
|
|
|
struct i2c_dw_dev_config i2c_0_runtime = { |
|
.app_config.raw = CONFIG_I2C_DW_0_DEFAULT_CFG, |
|
}; |
|
|
|
DEVICE_INIT(i2c_0, CONFIG_I2C_DW_0_NAME, &i2c_dw_initialize, |
|
&i2c_0_runtime, &i2c_config_dw_0, |
|
SECONDARY, CONFIG_I2C_INIT_PRIORITY); |
|
|
|
void i2c_config_0(struct device *port) |
|
{ |
|
struct i2c_dw_rom_config * const config = port->config->config_info; |
|
struct device *shared_irq_dev; |
|
|
|
#if defined(CONFIG_I2C_DW_0_IRQ_DIRECT) |
|
ARG_UNUSED(shared_irq_dev); |
|
irq_connect(CONFIG_I2C_DW_0_IRQ, CONFIG_I2C_DW_0_INT_PRIORITY, |
|
i2c_dw_isr, DEVICE_GET(i2c_0), I2C_DW_IRQ_FLAGS); |
|
irq_enable(config->irq_num); |
|
#elif defined(CONFIG_I2C_DW_0_IRQ_SHARED) |
|
ARG_UNUSED(config); |
|
shared_irq_dev = device_get_binding(config->shared_irq_dev_name); |
|
shared_irq_isr_register(shared_irq_dev, (isr_t)i2c_dw_isr, port); |
|
shared_irq_enable(shared_irq_dev, port); |
|
#endif |
|
} |
|
#endif /* CONFIG_I2C_DW_0 */ |
|
|
|
|
|
/* |
|
* Adding in I2C1 |
|
*/ |
|
#if CONFIG_I2C_DW_1 |
|
void i2c_config_1(struct device *port); |
|
|
|
struct i2c_dw_rom_config i2c_config_dw_1 = { |
|
.base_address = CONFIG_I2C_DW_1_BASE, |
|
.irq_num = CONFIG_I2C_DW_1_IRQ, |
|
.config_func = i2c_config_1, |
|
|
|
#if CONFIG_PCI |
|
.pci_dev.class_type = CONFIG_I2C_DW_CLASS, |
|
.pci_dev.bus = CONFIG_I2C_DW_1_BUS, |
|
.pci_dev.dev = CONFIG_I2C_DW_1_DEV, |
|
.pci_dev.vendor_id = CONFIG_I2C_DW_VENDOR_ID, |
|
.pci_dev.device_id = CONFIG_I2C_DW_DEVICE_ID, |
|
.pci_dev.function = CONFIG_I2C_DW_1_FUNCTION, |
|
.pci_dev.bar = CONFIG_I2C_DW_1_BAR, |
|
#endif |
|
}; |
|
|
|
struct i2c_dw_dev_config i2c_1_runtime = { |
|
.app_config.raw = CONFIG_I2C_DW_1_DEFAULT_CFG, |
|
}; |
|
|
|
DEVICE_INIT(i2c_1, CONFIG_I2C_DW_1_NAME, &i2c_dw_initialize, |
|
&i2c_1_runtime, &i2c_config_dw_1, |
|
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |
|
|
|
void i2c_config_1(struct device *port) |
|
{ |
|
struct i2c_dw_rom_config * const config = port->config->config_info; |
|
struct device *shared_irq_dev; |
|
|
|
ARG_UNUSED(shared_irq_dev); |
|
irq_connect(CONFIG_I2C_DW_1_IRQ, CONFIG_I2C_DW_1_INT_PRIORITY, |
|
i2c_dw_isr, DEVICE_GET(i2c_1), I2C_DW_IRQ_FLAGS); |
|
irq_enable(config->irq_num); |
|
} |
|
|
|
#endif /* CONFIG_I2C_DW_1 */
|
|
|