avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
159
u-boot/drivers/i2c/Kconfig
Normal file
159
u-boot/drivers/i2c/Kconfig
Normal file
@@ -0,0 +1,159 @@
|
||||
#
|
||||
# I2C subsystem configuration
|
||||
#
|
||||
|
||||
menu "I2C support"
|
||||
|
||||
config DM_I2C
|
||||
bool "Enable Driver Model for I2C drivers"
|
||||
depends on DM
|
||||
help
|
||||
Enable driver model for I2C. The I2C uclass interface: probe, read,
|
||||
write and speed, is implemented with the bus drivers operations,
|
||||
which provide methods for bus setting and data transfer. Each chip
|
||||
device (bus child) info is kept as parent platdata. The interface
|
||||
is defined in include/i2c.h. When i2c bus driver supports the i2c
|
||||
uclass, but the device drivers not, then DM_I2C_COMPAT config can
|
||||
be used as compatibility layer.
|
||||
|
||||
config DM_I2C_COMPAT
|
||||
bool "Enable I2C compatibility layer"
|
||||
depends on DM
|
||||
help
|
||||
Enable old-style I2C functions for compatibility with existing code.
|
||||
This option can be enabled as a temporary measure to avoid needing
|
||||
to convert all code for a board in a single commit. It should not
|
||||
be enabled for any board in an official release.
|
||||
|
||||
config I2C_CROS_EC_TUNNEL
|
||||
tristate "Chrome OS EC tunnel I2C bus"
|
||||
depends on CROS_EC
|
||||
help
|
||||
This provides an I2C bus that will tunnel i2c commands through to
|
||||
the other side of the Chrome OS EC to the I2C bus connected there.
|
||||
This will work whatever the interface used to talk to the EC (SPI,
|
||||
I2C or LPC). Some Chromebooks use this when the hardware design
|
||||
does not allow direct access to the main PMIC from the AP.
|
||||
|
||||
config I2C_CROS_EC_LDO
|
||||
bool "Provide access to LDOs on the Chrome OS EC"
|
||||
depends on CROS_EC
|
||||
---help---
|
||||
On many Chromebooks the main PMIC is inaccessible to the AP. This is
|
||||
often dealt with by using an I2C pass-through interface provided by
|
||||
the EC. On some unfortunate models (e.g. Spring) the pass-through
|
||||
is not available, and an LDO message is available instead. This
|
||||
option enables a driver which provides very basic access to those
|
||||
regulators, via the EC. We implement this as an I2C bus which
|
||||
emulates just the TPS65090 messages we know about. This is done to
|
||||
avoid duplicating the logic in the TPS65090 regulator driver for
|
||||
enabling/disabling an LDO.
|
||||
|
||||
config DM_I2C_GPIO
|
||||
bool "Enable Driver Model for software emulated I2C bus driver"
|
||||
depends on DM_I2C && DM_GPIO
|
||||
help
|
||||
Enable the i2c bus driver emulation by using the GPIOs. The bus GPIO
|
||||
configuration is given by the device tree. Kernel-style device tree
|
||||
bindings are supported.
|
||||
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
|
||||
|
||||
config SYS_I2C_FSL
|
||||
bool "Freescale I2C bus driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Add support for Freescale I2C busses as used on MPC8240, MPC8245, and
|
||||
MPC85xx processors.
|
||||
|
||||
config SYS_I2C_CADENCE
|
||||
tristate "Cadence I2C Controller"
|
||||
depends on DM_I2C && (ARCH_ZYNQ || ARM64)
|
||||
help
|
||||
Say yes here to select Cadence I2C Host Controller. This controller is
|
||||
e.g. used by Xilinx Zynq.
|
||||
|
||||
config SYS_I2C_DW
|
||||
bool "Designware I2C Controller"
|
||||
default n
|
||||
help
|
||||
Say yes here to select the Designware I2C Host Controller. This
|
||||
controller is used in various SoCs, e.g. the ST SPEAr, Altera
|
||||
SoCFPGA, Synopsys ARC700 and some Intel x86 SoCs.
|
||||
|
||||
config SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
|
||||
bool "DW I2C Enable Status Register not supported"
|
||||
depends on SYS_I2C_DW && (TARGET_SPEAR300 || TARGET_SPEAR310 || \
|
||||
TARGET_SPEAR320 || TARGET_SPEAR600 || TARGET_X600)
|
||||
default y
|
||||
help
|
||||
Some versions of the Designware I2C controller do not support the
|
||||
enable status register. This config option can be enabled in such
|
||||
cases.
|
||||
|
||||
config SYS_I2C_INTEL
|
||||
bool "Intel I2C/SMBUS driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Add support for the Intel SMBUS driver. So far this driver is just
|
||||
a stub which perhaps some basic init. There is no implementation of
|
||||
the I2C API meaning that any I2C operations will immediately fail
|
||||
for now.
|
||||
|
||||
config SYS_I2C_ROCKCHIP
|
||||
bool "Rockchip I2C driver"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Add support for the Rockchip I2C driver. This is used with various
|
||||
Rockchip parts such as RK3126, RK3128, RK3036 and RK3288. All chips
|
||||
have several I2C ports and all are provided, controled by the
|
||||
device tree.
|
||||
|
||||
config SYS_I2C_SANDBOX
|
||||
bool "Sandbox I2C driver"
|
||||
depends on SANDBOX && DM_I2C
|
||||
help
|
||||
Enable I2C support for sandbox. This is an emulation of a real I2C
|
||||
bus. Devices can be attached to the bus using the device tree
|
||||
which specifies the driver to use. As an example, see this device
|
||||
tree fragment from sandbox.dts. It shows that the I2C bus has a
|
||||
single EEPROM at address 0x2c (7-bit address) which is emulated by
|
||||
the driver for "sandbox,i2c-eeprom", which is in
|
||||
drivers/misc/i2c_eeprom_emul.c.
|
||||
|
||||
i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
compatible = "sandbox,i2c";
|
||||
clock-frequency = <400000>;
|
||||
eeprom@2c {
|
||||
reg = <0x2c>;
|
||||
compatible = "i2c-eeprom";
|
||||
emul {
|
||||
compatible = "sandbox,i2c-eeprom";
|
||||
sandbox,filename = "i2c.bin";
|
||||
sandbox,size = <128>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
config SYS_I2C_UNIPHIER
|
||||
bool "UniPhier I2C driver"
|
||||
depends on ARCH_UNIPHIER && DM_I2C
|
||||
default y
|
||||
help
|
||||
Support for UniPhier I2C controller driver. This I2C controller
|
||||
is used on PH1-LD4, PH1-sLD8 or older UniPhier SoCs.
|
||||
|
||||
config SYS_I2C_UNIPHIER_F
|
||||
bool "UniPhier FIFO-builtin I2C driver"
|
||||
depends on ARCH_UNIPHIER && DM_I2C
|
||||
default y
|
||||
help
|
||||
Support for UniPhier FIFO-builtin I2C controller driver.
|
||||
This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs.
|
||||
|
||||
source "drivers/i2c/muxes/Kconfig"
|
||||
|
||||
endmenu
|
||||
45
u-boot/drivers/i2c/Makefile
Normal file
45
u-boot/drivers/i2c/Makefile
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
obj-$(CONFIG_DM_I2C) += i2c-uclass.o
|
||||
obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
|
||||
obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
|
||||
obj-$(CONFIG_$(SPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
|
||||
obj-$(CONFIG_$(SPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o
|
||||
|
||||
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
|
||||
obj-$(CONFIG_I2C_MV) += mv_i2c.o
|
||||
obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
|
||||
obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
|
||||
obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C) += i2c_core.o
|
||||
obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o
|
||||
obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
|
||||
obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
|
||||
obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_MXS) += mxs_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
|
||||
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
|
||||
obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
|
||||
obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
|
||||
obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o
|
||||
|
||||
obj-$(CONFIG_I2C_MUX) += muxes/
|
||||
306
u-boot/drivers/i2c/adi_i2c.c
Normal file
306
u-boot/drivers/i2c/adi_i2c.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* i2c.c - driver for ADI TWI/I2C
|
||||
*
|
||||
* Copyright (c) 2006-2014 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include <asm/clock.h>
|
||||
#include <asm/twi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct twi_regs *i2c_get_base(struct i2c_adapter *adap);
|
||||
|
||||
/* Every register is 32bit aligned, but only 16bits in size */
|
||||
#define ureg(name) u16 name; u16 __pad_##name;
|
||||
struct twi_regs {
|
||||
ureg(clkdiv);
|
||||
ureg(control);
|
||||
ureg(slave_ctl);
|
||||
ureg(slave_stat);
|
||||
ureg(slave_addr);
|
||||
ureg(master_ctl);
|
||||
ureg(master_stat);
|
||||
ureg(master_addr);
|
||||
ureg(int_stat);
|
||||
ureg(int_mask);
|
||||
ureg(fifo_ctl);
|
||||
ureg(fifo_stat);
|
||||
char __pad[0x50];
|
||||
ureg(xmt_data8);
|
||||
ureg(xmt_data16);
|
||||
ureg(rcv_data8);
|
||||
ureg(rcv_data16);
|
||||
};
|
||||
#undef ureg
|
||||
|
||||
#ifdef TWI_CLKDIV
|
||||
#define TWI0_CLKDIV TWI_CLKDIV
|
||||
# ifdef CONFIG_SYS_MAX_I2C_BUS
|
||||
# undef CONFIG_SYS_MAX_I2C_BUS
|
||||
# endif
|
||||
#define CONFIG_SYS_MAX_I2C_BUS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The way speed is changed into duty often results in integer truncation
|
||||
* with 50% duty, so we'll force rounding up to the next duty by adding 1
|
||||
* to the max. In practice this will get us a speed of something like
|
||||
* 385 KHz. The other limit is easy to handle as it is only 8 bits.
|
||||
*/
|
||||
#define I2C_SPEED_MAX 400000
|
||||
#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed))
|
||||
#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1)
|
||||
#define I2C_DUTY_MIN 0xff /* 8 bit limited */
|
||||
#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED)
|
||||
/* Note: duty is inverse of speed, so the comparisons below are correct */
|
||||
#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN
|
||||
# error "The I2C hardware can only operate 20KHz - 400KHz"
|
||||
#endif
|
||||
|
||||
/* All transfers are described by this data structure */
|
||||
struct adi_i2c_msg {
|
||||
u8 flags;
|
||||
#define I2C_M_COMBO 0x4
|
||||
#define I2C_M_STOP 0x2
|
||||
#define I2C_M_READ 0x1
|
||||
int len; /* msg length */
|
||||
u8 *buf; /* pointer to msg data */
|
||||
int alen; /* addr length */
|
||||
u8 *abuf; /* addr buffer */
|
||||
};
|
||||
|
||||
/* Allow msec timeout per ~byte transfer */
|
||||
#define I2C_TIMEOUT 10
|
||||
|
||||
/**
|
||||
* wait_for_completion - manage the actual i2c transfer
|
||||
* @msg: the i2c msg
|
||||
*/
|
||||
static int wait_for_completion(struct twi_regs *twi, struct adi_i2c_msg *msg)
|
||||
{
|
||||
u16 int_stat, ctl;
|
||||
ulong timebase = get_timer(0);
|
||||
|
||||
do {
|
||||
int_stat = readw(&twi->int_stat);
|
||||
|
||||
if (int_stat & XMTSERV) {
|
||||
writew(XMTSERV, &twi->int_stat);
|
||||
if (msg->alen) {
|
||||
writew(*(msg->abuf++), &twi->xmt_data8);
|
||||
--msg->alen;
|
||||
} else if (!(msg->flags & I2C_M_COMBO) && msg->len) {
|
||||
writew(*(msg->buf++), &twi->xmt_data8);
|
||||
--msg->len;
|
||||
} else {
|
||||
ctl = readw(&twi->master_ctl);
|
||||
if (msg->flags & I2C_M_COMBO)
|
||||
writew(ctl | RSTART | MDIR,
|
||||
&twi->master_ctl);
|
||||
else
|
||||
writew(ctl | STOP, &twi->master_ctl);
|
||||
}
|
||||
}
|
||||
if (int_stat & RCVSERV) {
|
||||
writew(RCVSERV, &twi->int_stat);
|
||||
if (msg->len) {
|
||||
*(msg->buf++) = readw(&twi->rcv_data8);
|
||||
--msg->len;
|
||||
} else if (msg->flags & I2C_M_STOP) {
|
||||
ctl = readw(&twi->master_ctl);
|
||||
writew(ctl | STOP, &twi->master_ctl);
|
||||
}
|
||||
}
|
||||
if (int_stat & MERR) {
|
||||
writew(MERR, &twi->int_stat);
|
||||
return msg->len;
|
||||
}
|
||||
if (int_stat & MCOMP) {
|
||||
writew(MCOMP, &twi->int_stat);
|
||||
if (msg->flags & I2C_M_COMBO && msg->len) {
|
||||
ctl = readw(&twi->master_ctl);
|
||||
ctl = (ctl & ~RSTART) |
|
||||
(min(msg->len, 0xff) << 6) | MEN | MDIR;
|
||||
writew(ctl, &twi->master_ctl);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we were able to do something, reset timeout */
|
||||
if (int_stat)
|
||||
timebase = get_timer(0);
|
||||
|
||||
} while (get_timer(timebase) < I2C_TIMEOUT);
|
||||
|
||||
return msg->len;
|
||||
}
|
||||
|
||||
static int i2c_transfer(struct i2c_adapter *adap, uint8_t chip, uint addr,
|
||||
int alen, uint8_t *buffer, int len, uint8_t flags)
|
||||
{
|
||||
struct twi_regs *twi = i2c_get_base(adap);
|
||||
int ret;
|
||||
u16 ctl;
|
||||
uchar addr_buffer[] = {
|
||||
(addr >> 0),
|
||||
(addr >> 8),
|
||||
(addr >> 16),
|
||||
};
|
||||
struct adi_i2c_msg msg = {
|
||||
.flags = flags | (len >= 0xff ? I2C_M_STOP : 0),
|
||||
.buf = buffer,
|
||||
.len = len,
|
||||
.abuf = addr_buffer,
|
||||
.alen = alen,
|
||||
};
|
||||
|
||||
/* wait for things to settle */
|
||||
while (readw(&twi->master_stat) & BUSBUSY)
|
||||
if (ctrlc())
|
||||
return 1;
|
||||
|
||||
/* Set Transmit device address */
|
||||
writew(chip, &twi->master_addr);
|
||||
|
||||
/* Clear the FIFO before starting things */
|
||||
writew(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl);
|
||||
writew(0, &twi->fifo_ctl);
|
||||
|
||||
/* prime the pump */
|
||||
if (msg.alen) {
|
||||
len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len;
|
||||
writew(*(msg.abuf++), &twi->xmt_data8);
|
||||
--msg.alen;
|
||||
} else if (!(msg.flags & I2C_M_READ) && msg.len) {
|
||||
writew(*(msg.buf++), &twi->xmt_data8);
|
||||
--msg.len;
|
||||
}
|
||||
|
||||
/* clear int stat */
|
||||
writew(-1, &twi->master_stat);
|
||||
writew(-1, &twi->int_stat);
|
||||
writew(0, &twi->int_mask);
|
||||
|
||||
/* Master enable */
|
||||
ctl = readw(&twi->master_ctl);
|
||||
ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN |
|
||||
((msg.flags & I2C_M_READ) ? MDIR : 0);
|
||||
writew(ctl, &twi->master_ctl);
|
||||
|
||||
/* process the rest */
|
||||
ret = wait_for_completion(twi, &msg);
|
||||
|
||||
if (ret) {
|
||||
ctl = readw(&twi->master_ctl) & ~MEN;
|
||||
writew(ctl, &twi->master_ctl);
|
||||
ctl = readw(&twi->control) & ~TWI_ENA;
|
||||
writew(ctl, &twi->control);
|
||||
ctl = readw(&twi->control) | TWI_ENA;
|
||||
writew(ctl, &twi->control);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint adi_i2c_setspeed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct twi_regs *twi = i2c_get_base(adap);
|
||||
u16 clkdiv = I2C_SPEED_TO_DUTY(speed);
|
||||
|
||||
/* Set TWI interface clock */
|
||||
if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN)
|
||||
return -1;
|
||||
clkdiv = (clkdiv << 8) | (clkdiv & 0xff);
|
||||
writew(clkdiv, &twi->clkdiv);
|
||||
|
||||
/* Don't turn it on */
|
||||
writew(speed > 100000 ? FAST : 0, &twi->master_ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adi_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
struct twi_regs *twi = i2c_get_base(adap);
|
||||
u16 prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F;
|
||||
|
||||
/* Set TWI internal clock as 10MHz */
|
||||
writew(prescale, &twi->control);
|
||||
|
||||
/* Set TWI interface clock as specified */
|
||||
i2c_set_bus_speed(speed);
|
||||
|
||||
/* Enable it */
|
||||
writew(TWI_ENA | prescale, &twi->control);
|
||||
}
|
||||
|
||||
static int adi_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer, int len)
|
||||
{
|
||||
return i2c_transfer(adap, chip, addr, alen, buffer,
|
||||
len, alen ? I2C_M_COMBO : I2C_M_READ);
|
||||
}
|
||||
|
||||
static int adi_i2c_write(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer, int len)
|
||||
{
|
||||
return i2c_transfer(adap, chip, addr, alen, buffer, len, 0);
|
||||
}
|
||||
|
||||
static int adi_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
|
||||
{
|
||||
u8 byte;
|
||||
return adi_i2c_read(adap, chip, 0, 0, &byte, 1);
|
||||
}
|
||||
|
||||
static struct twi_regs *i2c_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 2
|
||||
case 2:
|
||||
return (struct twi_regs *)TWI2_CLKDIV;
|
||||
#endif
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 1
|
||||
case 1:
|
||||
return (struct twi_regs *)TWI1_CLKDIV;
|
||||
#endif
|
||||
case 0:
|
||||
return (struct twi_regs *)TWI0_CLKDIV;
|
||||
|
||||
default:
|
||||
printf("wrong hwadapnr: %d\n", adap->hwadapnr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(adi_i2c0, adi_i2c_init, adi_i2c_probe,
|
||||
adi_i2c_read, adi_i2c_write,
|
||||
adi_i2c_setspeed,
|
||||
CONFIG_SYS_I2C_SPEED,
|
||||
0,
|
||||
0)
|
||||
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(adi_i2c1, adi_i2c_init, adi_i2c_probe,
|
||||
adi_i2c_read, adi_i2c_write,
|
||||
adi_i2c_setspeed,
|
||||
CONFIG_SYS_I2C_SPEED,
|
||||
0,
|
||||
1)
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(adi_i2c2, adi_i2c_init, adi_i2c_probe,
|
||||
adi_i2c_read, adi_i2c_write,
|
||||
adi_i2c_setspeed,
|
||||
CONFIG_SYS_I2C_SPEED,
|
||||
0,
|
||||
2)
|
||||
#endif
|
||||
77
u-boot/drivers/i2c/cros_ec_ldo.c
Normal file
77
u-boot/drivers/i2c/cros_ec_ldo.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <cros_ec.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <power/tps65090.h>
|
||||
|
||||
static int cros_ec_ldo_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_ldo_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
bool is_read = nmsgs > 1;
|
||||
int fet_id, ret;
|
||||
|
||||
/*
|
||||
* Look for reads and writes of the LDO registers. In either case the
|
||||
* first message is a write with the register number as the first byte.
|
||||
*/
|
||||
if (!nmsgs || !msg->len || (msg->flags & I2C_M_RD)) {
|
||||
debug("%s: Invalid message\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fet_id = msg->buf[0] - REG_FET_BASE;
|
||||
if (fet_id < 1 || fet_id > MAX_FET_NUM) {
|
||||
debug("%s: Invalid FET %d\n", __func__, fet_id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (is_read) {
|
||||
uint8_t state;
|
||||
|
||||
ret = cros_ec_get_ldo(dev->parent, fet_id, &state);
|
||||
if (!ret)
|
||||
msg[1].buf[0] = state ?
|
||||
FET_CTRL_ENFET | FET_CTRL_PGFET : 0;
|
||||
} else {
|
||||
bool on = msg->buf[1] & FET_CTRL_ENFET;
|
||||
|
||||
ret = cros_ec_set_ldo(dev->parent, fet_id, on);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
/* Indicate that the message is unimplemented */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops cros_ec_i2c_ops = {
|
||||
.xfer = cros_ec_ldo_xfer,
|
||||
.set_bus_speed = cros_ec_ldo_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_i2c_ids[] = {
|
||||
{ .compatible = "google,cros-ec-ldo-tunnel" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_ldo) = {
|
||||
.name = "cros_ec_ldo_tunnel",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = cros_ec_i2c_ids,
|
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.ops = &cros_ec_i2c_ops,
|
||||
};
|
||||
41
u-boot/drivers/i2c/cros_ec_tunnel.c
Normal file
41
u-boot/drivers/i2c/cros_ec_tunnel.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <cros_ec.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
|
||||
static int cros_ec_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
return cros_ec_i2c_tunnel(dev->parent, msg, nmsgs);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops cros_ec_i2c_ops = {
|
||||
.xfer = cros_ec_i2c_xfer,
|
||||
.set_bus_speed = cros_ec_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_i2c_ids[] = {
|
||||
{ .compatible = "google,cros-ec-i2c-tunnel" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_tunnel) = {
|
||||
.name = "cros_ec_tunnel",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = cros_ec_i2c_ids,
|
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.ops = &cros_ec_i2c_ops,
|
||||
};
|
||||
384
u-boot/drivers/i2c/davinci_i2c.c
Normal file
384
u-boot/drivers/i2c/davinci_i2c.c
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* TI DaVinci (TMS320DM644x) I2C driver.
|
||||
*
|
||||
* (C) Copyright 2012-2014
|
||||
* Texas Instruments Incorporated, <www.ti.com>
|
||||
* (C) Copyright 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/i2c_defs.h>
|
||||
#include <asm/io.h>
|
||||
#include "davinci_i2c.h"
|
||||
|
||||
#define CHECK_NACK() \
|
||||
do {\
|
||||
if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\
|
||||
REG(&(i2c_base->i2c_con)) = 0;\
|
||||
return 1;\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap);
|
||||
|
||||
static int wait_for_bus(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
int stat, timeout;
|
||||
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
stat = REG(&(i2c_base->i2c_stat));
|
||||
if (!((stat) & I2C_STAT_BB)) {
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
REG(&(i2c_base->i2c_stat)) = stat;
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int poll_i2c_irq(struct i2c_adapter *adap, int mask)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
int stat, timeout;
|
||||
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
udelay(1000);
|
||||
stat = REG(&(i2c_base->i2c_stat));
|
||||
if (stat & mask)
|
||||
return stat;
|
||||
}
|
||||
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
return stat | I2C_TIMEOUT;
|
||||
}
|
||||
|
||||
static void flush_rx(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
|
||||
while (1) {
|
||||
if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_RRDY))
|
||||
break;
|
||||
|
||||
REG(&(i2c_base->i2c_drr));
|
||||
REG(&(i2c_base->i2c_stat)) = I2C_STAT_RRDY;
|
||||
udelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
static uint davinci_i2c_setspeed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
uint32_t div, psc;
|
||||
|
||||
psc = 2;
|
||||
/* SCLL + SCLH */
|
||||
div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10;
|
||||
REG(&(i2c_base->i2c_psc)) = psc; /* 27MHz / (2 + 1) = 9MHz */
|
||||
REG(&(i2c_base->i2c_scll)) = (div * 50) / 100; /* 50% Duty */
|
||||
REG(&(i2c_base->i2c_sclh)) = div - REG(&(i2c_base->i2c_scll));
|
||||
|
||||
adap->speed = speed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void davinci_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
|
||||
if (REG(&(i2c_base->i2c_con)) & I2C_CON_EN) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
davinci_i2c_setspeed(adap, speed);
|
||||
|
||||
REG(&(i2c_base->i2c_oa)) = slaveadd;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
|
||||
/* Interrupts must be enabled or I2C module won't work */
|
||||
REG(&(i2c_base->i2c_ie)) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE |
|
||||
I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE;
|
||||
|
||||
/* Now enable I2C controller (get it out of reset) */
|
||||
REG(&(i2c_base->i2c_con)) = I2C_CON_EN;
|
||||
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
static int davinci_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
int rc = 1;
|
||||
|
||||
if (chip == REG(&(i2c_base->i2c_oa)))
|
||||
return rc;
|
||||
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
if (wait_for_bus(adap))
|
||||
return 1;
|
||||
|
||||
/* try to read one byte from current (or only) address */
|
||||
REG(&(i2c_base->i2c_cnt)) = 1;
|
||||
REG(&(i2c_base->i2c_sa)) = chip;
|
||||
REG(&(i2c_base->i2c_con)) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT |
|
||||
I2C_CON_STP);
|
||||
udelay(50000);
|
||||
|
||||
if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_NACK)) {
|
||||
rc = 0;
|
||||
flush_rx(adap);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_con)) |= I2C_CON_STP;
|
||||
udelay(20000);
|
||||
if (wait_for_bus(adap))
|
||||
return 1;
|
||||
}
|
||||
|
||||
flush_rx(adap);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int davinci_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint32_t addr, int alen, uint8_t *buf, int len)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
uint32_t tmp;
|
||||
int i;
|
||||
|
||||
if ((alen < 0) || (alen > 2)) {
|
||||
printf("%s(): bogus address length %x\n", __func__, alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wait_for_bus(adap))
|
||||
return 1;
|
||||
|
||||
if (alen != 0) {
|
||||
/* Start address phase */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX;
|
||||
REG(&(i2c_base->i2c_cnt)) = alen;
|
||||
REG(&(i2c_base->i2c_sa)) = chip;
|
||||
REG(&(i2c_base->i2c_con)) = tmp;
|
||||
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
switch (alen) {
|
||||
case 2:
|
||||
/* Send address MSByte */
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff;
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
/* No break, fall through */
|
||||
case 1:
|
||||
/* Send address LSByte */
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(&(i2c_base->i2c_dxr)) = addr & 0xff;
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY |
|
||||
I2C_STAT_NACK | I2C_STAT_ARDY);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_ARDY)) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Address phase is over, now read 'len' bytes and stop */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP;
|
||||
REG(&(i2c_base->i2c_cnt)) = len & 0xffff;
|
||||
REG(&(i2c_base->i2c_sa)) = chip;
|
||||
REG(&(i2c_base->i2c_con)) = tmp;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_RRDY | I2C_STAT_NACK |
|
||||
I2C_STAT_ROVR);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_RRDY) {
|
||||
buf[i] = REG(&(i2c_base->i2c_drr));
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
flush_rx(adap);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_i2c_write(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint32_t addr, int alen, uint8_t *buf, int len)
|
||||
{
|
||||
struct i2c_regs *i2c_base = davinci_get_base(adap);
|
||||
uint32_t tmp;
|
||||
int i;
|
||||
|
||||
if ((alen < 0) || (alen > 2)) {
|
||||
printf("%s(): bogus address length %x\n", __func__, alen);
|
||||
return 1;
|
||||
}
|
||||
if (len < 0) {
|
||||
printf("%s(): bogus length %x\n", __func__, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wait_for_bus(adap))
|
||||
return 1;
|
||||
|
||||
/* Start address phase */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT |
|
||||
I2C_CON_TRX | I2C_CON_STP;
|
||||
REG(&(i2c_base->i2c_cnt)) = (alen == 0) ?
|
||||
len & 0xffff : (len & 0xffff) + alen;
|
||||
REG(&(i2c_base->i2c_sa)) = chip;
|
||||
REG(&(i2c_base->i2c_con)) = tmp;
|
||||
|
||||
switch (alen) {
|
||||
case 2:
|
||||
/* Send address MSByte */
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff;
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
/* No break, fall through */
|
||||
case 1:
|
||||
/* Send address LSByte */
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(&(i2c_base->i2c_dxr)) = addr & 0xff;
|
||||
} else {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY)
|
||||
REG(&(i2c_base->i2c_dxr)) = buf[i];
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
flush_rx(adap);
|
||||
REG(&(i2c_base->i2c_stat)) = 0xffff;
|
||||
REG(&(i2c_base->i2c_cnt)) = 0;
|
||||
REG(&(i2c_base->i2c_con)) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
#if I2C_BUS_MAX >= 3
|
||||
case 2:
|
||||
return (struct i2c_regs *)I2C2_BASE;
|
||||
#endif
|
||||
#if I2C_BUS_MAX >= 2
|
||||
case 1:
|
||||
return (struct i2c_regs *)I2C1_BASE;
|
||||
#endif
|
||||
case 0:
|
||||
return (struct i2c_regs *)I2C_BASE;
|
||||
|
||||
default:
|
||||
printf("wrong hwadapnr: %d\n", adap->hwadapnr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_0, davinci_i2c_init, davinci_i2c_probe,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE,
|
||||
0)
|
||||
|
||||
#if I2C_BUS_MAX >= 2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_1, davinci_i2c_init, davinci_i2c_probe,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED1,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE1,
|
||||
1)
|
||||
#endif
|
||||
|
||||
#if I2C_BUS_MAX >= 3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(davinci_2, davinci_i2c_init, davinci_i2c_probe,
|
||||
davinci_i2c_read, davinci_i2c_write,
|
||||
davinci_i2c_setspeed,
|
||||
CONFIG_SYS_DAVINCI_I2C_SPEED2,
|
||||
CONFIG_SYS_DAVINCI_I2C_SLAVE2,
|
||||
2)
|
||||
#endif
|
||||
78
u-boot/drivers/i2c/davinci_i2c.h
Normal file
78
u-boot/drivers/i2c/davinci_i2c.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* (C) Copyright 2004-2014
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Some changes copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#ifndef _DAVINCI_I2C_H_
|
||||
#define _DAVINCI_I2C_H_
|
||||
|
||||
#define I2C_WRITE 0
|
||||
#define I2C_READ 1
|
||||
|
||||
struct i2c_regs {
|
||||
u32 i2c_oa;
|
||||
u32 i2c_ie;
|
||||
u32 i2c_stat;
|
||||
u32 i2c_scll;
|
||||
u32 i2c_sclh;
|
||||
u32 i2c_cnt;
|
||||
u32 i2c_drr;
|
||||
u32 i2c_sa;
|
||||
u32 i2c_dxr;
|
||||
u32 i2c_con;
|
||||
u32 i2c_iv;
|
||||
u32 res_2c;
|
||||
u32 i2c_psc;
|
||||
};
|
||||
|
||||
/* I2C masks */
|
||||
|
||||
/* I2C Interrupt Enable Register (I2C_IE): */
|
||||
#define I2C_IE_SCD_IE (1 << 5) /* Stop condition detect interrupt enable */
|
||||
#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */
|
||||
#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */
|
||||
#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */
|
||||
#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Status Register (I2C_STAT): */
|
||||
|
||||
#define I2C_STAT_BB (1 << 12) /* Bus busy */
|
||||
#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */
|
||||
#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
|
||||
#define I2C_STAT_AAS (1 << 9) /* Address as slave */
|
||||
#define I2C_STAT_SCD (1 << 5) /* Stop condition detect */
|
||||
#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
|
||||
#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */
|
||||
#define I2C_STAT_ARDY (1 << 2) /* Register access ready */
|
||||
#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Interrupt Code Register (I2C_INTCODE): */
|
||||
|
||||
#define I2C_INTCODE_MASK 7
|
||||
#define I2C_INTCODE_NONE 0
|
||||
#define I2C_INTCODE_AL 1 /* Arbitration lost */
|
||||
#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */
|
||||
#define I2C_INTCODE_ARDY 3 /* Register access ready */
|
||||
#define I2C_INTCODE_RRDY 4 /* Rcv data ready */
|
||||
#define I2C_INTCODE_XRDY 5 /* Xmit data ready */
|
||||
#define I2C_INTCODE_SCD 6 /* Stop condition detect */
|
||||
|
||||
/* I2C Configuration Register (I2C_CON): */
|
||||
|
||||
#define I2C_CON_EN (1 << 5) /* I2C module enable */
|
||||
#define I2C_CON_STB (1 << 4) /* Start byte mode (master mode only) */
|
||||
#define I2C_CON_MST (1 << 10) /* Master/slave mode */
|
||||
#define I2C_CON_TRX (1 << 9) /* Tx/Rx mode (master mode only) */
|
||||
#define I2C_CON_XA (1 << 8) /* Expand address */
|
||||
#define I2C_CON_STP (1 << 11) /* Stop condition (master mode only) */
|
||||
#define I2C_CON_STT (1 << 13) /* Start condition (master mode only) */
|
||||
#define I2C_CON_FREE (1 << 14) /* Free run on emulation */
|
||||
|
||||
#define I2C_TIMEOUT 0xffff0000 /* Timeout mask for poll_i2c_irq() */
|
||||
|
||||
#endif
|
||||
605
u-boot/drivers/i2c/designware_i2c.c
Normal file
605
u-boot/drivers/i2c/designware_i2c.c
Normal file
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#include "designware_i2c.h"
|
||||
|
||||
struct dw_scl_sda_cfg {
|
||||
u32 ss_hcnt;
|
||||
u32 fs_hcnt;
|
||||
u32 ss_lcnt;
|
||||
u32 fs_lcnt;
|
||||
u32 sda_hold;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/* BayTrail HCNT/LCNT/SDA hold time */
|
||||
static struct dw_scl_sda_cfg byt_config = {
|
||||
.ss_hcnt = 0x200,
|
||||
.fs_hcnt = 0x55,
|
||||
.ss_lcnt = 0x200,
|
||||
.fs_lcnt = 0x99,
|
||||
.sda_hold = 0x6,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dw_i2c {
|
||||
struct i2c_regs *regs;
|
||||
struct dw_scl_sda_cfg *scl_sda_cfg;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
|
||||
static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
|
||||
{
|
||||
u32 ena = enable ? IC_ENABLE_0B : 0;
|
||||
|
||||
writel(ena, &i2c_base->ic_enable);
|
||||
}
|
||||
#else
|
||||
static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
|
||||
{
|
||||
u32 ena = enable ? IC_ENABLE_0B : 0;
|
||||
int timeout = 100;
|
||||
|
||||
do {
|
||||
writel(ena, &i2c_base->ic_enable);
|
||||
if ((readl(&i2c_base->ic_enable_status) & IC_ENABLE_0B) == ena)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Wait 10 times the signaling period of the highest I2C
|
||||
* transfer supported by the driver (for 400KHz this is
|
||||
* 25us) as described in the DesignWare I2C databook.
|
||||
*/
|
||||
udelay(25);
|
||||
} while (timeout--);
|
||||
|
||||
printf("timeout in %sabling I2C adapter\n", enable ? "en" : "dis");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* i2c_set_bus_speed - Set the i2c speed
|
||||
* @speed: required i2c speed
|
||||
*
|
||||
* Set the i2c speed.
|
||||
*/
|
||||
static unsigned int __dw_i2c_set_bus_speed(struct i2c_regs *i2c_base,
|
||||
struct dw_scl_sda_cfg *scl_sda_cfg,
|
||||
unsigned int speed)
|
||||
{
|
||||
unsigned int cntl;
|
||||
unsigned int hcnt, lcnt;
|
||||
int i2c_spd;
|
||||
|
||||
if (speed >= I2C_MAX_SPEED)
|
||||
i2c_spd = IC_SPEED_MODE_MAX;
|
||||
else if (speed >= I2C_FAST_SPEED)
|
||||
i2c_spd = IC_SPEED_MODE_FAST;
|
||||
else
|
||||
i2c_spd = IC_SPEED_MODE_STANDARD;
|
||||
|
||||
/* to set speed cltr must be disabled */
|
||||
dw_i2c_enable(i2c_base, false);
|
||||
|
||||
cntl = (readl(&i2c_base->ic_con) & (~IC_CON_SPD_MSK));
|
||||
|
||||
switch (i2c_spd) {
|
||||
#ifndef CONFIG_X86 /* No High-speed for BayTrail yet */
|
||||
case IC_SPEED_MODE_MAX:
|
||||
cntl |= IC_CON_SPD_SS;
|
||||
if (scl_sda_cfg) {
|
||||
hcnt = scl_sda_cfg->fs_hcnt;
|
||||
lcnt = scl_sda_cfg->fs_lcnt;
|
||||
} else {
|
||||
hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
|
||||
}
|
||||
writel(hcnt, &i2c_base->ic_hs_scl_hcnt);
|
||||
writel(lcnt, &i2c_base->ic_hs_scl_lcnt);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case IC_SPEED_MODE_STANDARD:
|
||||
cntl |= IC_CON_SPD_SS;
|
||||
if (scl_sda_cfg) {
|
||||
hcnt = scl_sda_cfg->ss_hcnt;
|
||||
lcnt = scl_sda_cfg->ss_lcnt;
|
||||
} else {
|
||||
hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
|
||||
}
|
||||
writel(hcnt, &i2c_base->ic_ss_scl_hcnt);
|
||||
writel(lcnt, &i2c_base->ic_ss_scl_lcnt);
|
||||
break;
|
||||
|
||||
case IC_SPEED_MODE_FAST:
|
||||
default:
|
||||
cntl |= IC_CON_SPD_FS;
|
||||
if (scl_sda_cfg) {
|
||||
hcnt = scl_sda_cfg->fs_hcnt;
|
||||
lcnt = scl_sda_cfg->fs_lcnt;
|
||||
} else {
|
||||
hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
|
||||
lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
|
||||
}
|
||||
writel(hcnt, &i2c_base->ic_fs_scl_hcnt);
|
||||
writel(lcnt, &i2c_base->ic_fs_scl_lcnt);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(cntl, &i2c_base->ic_con);
|
||||
|
||||
/* Configure SDA Hold Time if required */
|
||||
if (scl_sda_cfg)
|
||||
writel(scl_sda_cfg->sda_hold, &i2c_base->ic_sda_hold);
|
||||
|
||||
/* Enable back i2c now speed set */
|
||||
dw_i2c_enable(i2c_base, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_setaddress - Sets the target slave address
|
||||
* @i2c_addr: target i2c address
|
||||
*
|
||||
* Sets the target slave address.
|
||||
*/
|
||||
static void i2c_setaddress(struct i2c_regs *i2c_base, unsigned int i2c_addr)
|
||||
{
|
||||
/* Disable i2c */
|
||||
dw_i2c_enable(i2c_base, false);
|
||||
|
||||
writel(i2c_addr, &i2c_base->ic_tar);
|
||||
|
||||
/* Enable i2c */
|
||||
dw_i2c_enable(i2c_base, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_flush_rxfifo - Flushes the i2c RX FIFO
|
||||
*
|
||||
* Flushes the i2c RX FIFO
|
||||
*/
|
||||
static void i2c_flush_rxfifo(struct i2c_regs *i2c_base)
|
||||
{
|
||||
while (readl(&i2c_base->ic_status) & IC_STATUS_RFNE)
|
||||
readl(&i2c_base->ic_cmd_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_wait_for_bb - Waits for bus busy
|
||||
*
|
||||
* Waits for bus busy
|
||||
*/
|
||||
static int i2c_wait_for_bb(struct i2c_regs *i2c_base)
|
||||
{
|
||||
unsigned long start_time_bb = get_timer(0);
|
||||
|
||||
while ((readl(&i2c_base->ic_status) & IC_STATUS_MA) ||
|
||||
!(readl(&i2c_base->ic_status) & IC_STATUS_TFE)) {
|
||||
|
||||
/* Evaluate timeout */
|
||||
if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_xfer_init(struct i2c_regs *i2c_base, uchar chip, uint addr,
|
||||
int alen)
|
||||
{
|
||||
if (i2c_wait_for_bb(i2c_base))
|
||||
return 1;
|
||||
|
||||
i2c_setaddress(i2c_base, chip);
|
||||
while (alen) {
|
||||
alen--;
|
||||
/* high byte address going out first */
|
||||
writel((addr >> (alen * 8)) & 0xff,
|
||||
&i2c_base->ic_cmd_data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_xfer_finish(struct i2c_regs *i2c_base)
|
||||
{
|
||||
ulong start_stop_det = get_timer(0);
|
||||
|
||||
while (1) {
|
||||
if ((readl(&i2c_base->ic_raw_intr_stat) & IC_STOP_DET)) {
|
||||
readl(&i2c_base->ic_clr_stop_det);
|
||||
break;
|
||||
} else if (get_timer(start_stop_det) > I2C_STOPDET_TO) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c_wait_for_bb(i2c_base)) {
|
||||
printf("Timed out waiting for bus\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
i2c_flush_rxfifo(i2c_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read - Read from i2c memory
|
||||
* @chip: target i2c address
|
||||
* @addr: address to read from
|
||||
* @alen:
|
||||
* @buffer: buffer for read data
|
||||
* @len: no of bytes to be read
|
||||
*
|
||||
* Read from i2c memory.
|
||||
*/
|
||||
static int __dw_i2c_read(struct i2c_regs *i2c_base, u8 dev, uint addr,
|
||||
int alen, u8 *buffer, int len)
|
||||
{
|
||||
unsigned long start_time_rx;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
dev |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8));
|
||||
|
||||
debug("%s: fix addr_overflow: dev %02x addr %02x\n", __func__, dev,
|
||||
addr);
|
||||
#endif
|
||||
|
||||
if (i2c_xfer_init(i2c_base, dev, addr, alen))
|
||||
return 1;
|
||||
|
||||
start_time_rx = get_timer(0);
|
||||
while (len) {
|
||||
if (len == 1)
|
||||
writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
|
||||
else
|
||||
writel(IC_CMD, &i2c_base->ic_cmd_data);
|
||||
|
||||
if (readl(&i2c_base->ic_status) & IC_STATUS_RFNE) {
|
||||
*buffer++ = (uchar)readl(&i2c_base->ic_cmd_data);
|
||||
len--;
|
||||
start_time_rx = get_timer(0);
|
||||
|
||||
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_xfer_finish(i2c_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write - Write to i2c memory
|
||||
* @chip: target i2c address
|
||||
* @addr: address to read from
|
||||
* @alen:
|
||||
* @buffer: buffer for read data
|
||||
* @len: no of bytes to be read
|
||||
*
|
||||
* Write to i2c memory.
|
||||
*/
|
||||
static int __dw_i2c_write(struct i2c_regs *i2c_base, u8 dev, uint addr,
|
||||
int alen, u8 *buffer, int len)
|
||||
{
|
||||
int nb = len;
|
||||
unsigned long start_time_tx;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
dev |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8));
|
||||
|
||||
debug("%s: fix addr_overflow: dev %02x addr %02x\n", __func__, dev,
|
||||
addr);
|
||||
#endif
|
||||
|
||||
if (i2c_xfer_init(i2c_base, dev, addr, alen))
|
||||
return 1;
|
||||
|
||||
start_time_tx = get_timer(0);
|
||||
while (len) {
|
||||
if (readl(&i2c_base->ic_status) & IC_STATUS_TFNF) {
|
||||
if (--len == 0) {
|
||||
writel(*buffer | IC_STOP,
|
||||
&i2c_base->ic_cmd_data);
|
||||
} else {
|
||||
writel(*buffer, &i2c_base->ic_cmd_data);
|
||||
}
|
||||
buffer++;
|
||||
start_time_tx = get_timer(0);
|
||||
|
||||
} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
|
||||
printf("Timed out. i2c write Failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_xfer_finish(i2c_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* __dw_i2c_init - Init function
|
||||
* @speed: required i2c speed
|
||||
* @slaveaddr: slave address for the device
|
||||
*
|
||||
* Initialization function.
|
||||
*/
|
||||
static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
|
||||
{
|
||||
/* Disable i2c */
|
||||
dw_i2c_enable(i2c_base, false);
|
||||
|
||||
writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_base->ic_con);
|
||||
writel(IC_RX_TL, &i2c_base->ic_rx_tl);
|
||||
writel(IC_TX_TL, &i2c_base->ic_tx_tl);
|
||||
writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
|
||||
#ifndef CONFIG_DM_I2C
|
||||
__dw_i2c_set_bus_speed(i2c_base, NULL, speed);
|
||||
writel(slaveaddr, &i2c_base->ic_sar);
|
||||
#endif
|
||||
|
||||
/* Enable i2c */
|
||||
dw_i2c_enable(i2c_base, true);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
/*
|
||||
* The legacy I2C functions. These need to get removed once
|
||||
* all users of this driver are converted to DM.
|
||||
*/
|
||||
static struct i2c_regs *i2c_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 4
|
||||
case 3:
|
||||
return (struct i2c_regs *)CONFIG_SYS_I2C_BASE3;
|
||||
#endif
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 3
|
||||
case 2:
|
||||
return (struct i2c_regs *)CONFIG_SYS_I2C_BASE2;
|
||||
#endif
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 2
|
||||
case 1:
|
||||
return (struct i2c_regs *)CONFIG_SYS_I2C_BASE1;
|
||||
#endif
|
||||
case 0:
|
||||
return (struct i2c_regs *)CONFIG_SYS_I2C_BASE;
|
||||
default:
|
||||
printf("Wrong I2C-adapter number %d\n", adap->hwadapnr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned int dw_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
adap->speed = speed;
|
||||
return __dw_i2c_set_bus_speed(i2c_get_base(adap), NULL, speed);
|
||||
}
|
||||
|
||||
static void dw_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
__dw_i2c_init(i2c_get_base(adap), speed, slaveaddr);
|
||||
}
|
||||
|
||||
static int dw_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *buffer, int len)
|
||||
{
|
||||
return __dw_i2c_read(i2c_get_base(adap), dev, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
static int dw_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *buffer, int len)
|
||||
{
|
||||
return __dw_i2c_write(i2c_get_base(adap), dev, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
/* dw_i2c_probe - Probe the i2c chip */
|
||||
static int dw_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
struct i2c_regs *i2c_base = i2c_get_base(adap);
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Try to read the first location of the chip.
|
||||
*/
|
||||
ret = __dw_i2c_read(i2c_base, dev, 0, 1, (uchar *)&tmp, 1);
|
||||
if (ret)
|
||||
dw_i2c_init(adap, adap->speed, adap->slaveaddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(dw_0, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
|
||||
dw_i2c_write, dw_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 0)
|
||||
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(dw_1, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
|
||||
dw_i2c_write, dw_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED1, CONFIG_SYS_I2C_SLAVE1, 1)
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(dw_2, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
|
||||
dw_i2c_write, dw_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED2, CONFIG_SYS_I2C_SLAVE2, 2)
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_I2C_BUS_MAX >= 4
|
||||
U_BOOT_I2C_ADAP_COMPLETE(dw_3, dw_i2c_init, dw_i2c_probe, dw_i2c_read,
|
||||
dw_i2c_write, dw_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED3, CONFIG_SYS_I2C_SLAVE3, 3)
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_DM_I2C */
|
||||
/* The DM I2C functions */
|
||||
|
||||
static int designware_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct dw_i2c *i2c = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = __dw_i2c_read(i2c->regs, msg->addr, 0, 0,
|
||||
msg->buf, msg->len);
|
||||
} else {
|
||||
ret = __dw_i2c_write(i2c->regs, msg->addr, 0, 0,
|
||||
msg->buf, msg->len);
|
||||
}
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct dw_i2c *i2c = dev_get_priv(bus);
|
||||
|
||||
return __dw_i2c_set_bus_speed(i2c->regs, i2c->scl_sda_cfg, speed);
|
||||
}
|
||||
|
||||
static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct dw_i2c *i2c = dev_get_priv(bus);
|
||||
struct i2c_regs *i2c_base = i2c->regs;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
/* Try to read the first location of the chip */
|
||||
ret = __dw_i2c_read(i2c_base, chip_addr, 0, 1, (uchar *)&tmp, 1);
|
||||
if (ret)
|
||||
__dw_i2c_init(i2c_base, 0, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int designware_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct dw_i2c *priv = dev_get_priv(bus);
|
||||
|
||||
if (device_is_on_pci_bus(bus)) {
|
||||
#ifdef CONFIG_DM_PCI
|
||||
/* Save base address from PCI BAR */
|
||||
priv->regs = (struct i2c_regs *)
|
||||
dm_pci_map_bar(bus, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
#ifdef CONFIG_X86
|
||||
/* Use BayTrail specific timing values */
|
||||
priv->scl_sda_cfg = &byt_config;
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
priv->regs = (struct i2c_regs *)dev_get_addr_ptr(bus);
|
||||
}
|
||||
|
||||
__dw_i2c_init(priv->regs, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int designware_i2c_bind(struct udevice *dev)
|
||||
{
|
||||
static int num_cards;
|
||||
char name[20];
|
||||
|
||||
/* Create a unique device name for PCI type devices */
|
||||
if (device_is_on_pci_bus(dev)) {
|
||||
/*
|
||||
* ToDo:
|
||||
* Setting req_seq in the driver is probably not recommended.
|
||||
* But without a DT alias the number is not configured. And
|
||||
* using this driver is impossible for PCIe I2C devices.
|
||||
* This can be removed, once a better (correct) way for this
|
||||
* is found and implemented.
|
||||
*/
|
||||
dev->req_seq = num_cards;
|
||||
sprintf(name, "i2c_designware#%u", num_cards++);
|
||||
device_set_name(dev, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops designware_i2c_ops = {
|
||||
.xfer = designware_i2c_xfer,
|
||||
.probe_chip = designware_i2c_probe_chip,
|
||||
.set_bus_speed = designware_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id designware_i2c_ids[] = {
|
||||
{ .compatible = "snps,designware-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_designware) = {
|
||||
.name = "i2c_designware",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = designware_i2c_ids,
|
||||
.bind = designware_i2c_bind,
|
||||
.probe = designware_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct dw_i2c),
|
||||
.ops = &designware_i2c_ops,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static struct pci_device_id designware_pci_supported[] = {
|
||||
/* Intel BayTrail has 7 I2C controller located on the PCI bus */
|
||||
{ PCI_VDEVICE(INTEL, 0x0f41) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f42) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f43) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f44) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f45) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f46) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0f47) },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(i2c_designware, designware_pci_supported);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
135
u-boot/drivers/i2c/designware_i2c.h
Normal file
135
u-boot/drivers/i2c/designware_i2c.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __DW_I2C_H_
|
||||
#define __DW_I2C_H_
|
||||
|
||||
struct i2c_regs {
|
||||
u32 ic_con; /* 0x00 */
|
||||
u32 ic_tar; /* 0x04 */
|
||||
u32 ic_sar; /* 0x08 */
|
||||
u32 ic_hs_maddr; /* 0x0c */
|
||||
u32 ic_cmd_data; /* 0x10 */
|
||||
u32 ic_ss_scl_hcnt; /* 0x14 */
|
||||
u32 ic_ss_scl_lcnt; /* 0x18 */
|
||||
u32 ic_fs_scl_hcnt; /* 0x1c */
|
||||
u32 ic_fs_scl_lcnt; /* 0x20 */
|
||||
u32 ic_hs_scl_hcnt; /* 0x24 */
|
||||
u32 ic_hs_scl_lcnt; /* 0x28 */
|
||||
u32 ic_intr_stat; /* 0x2c */
|
||||
u32 ic_intr_mask; /* 0x30 */
|
||||
u32 ic_raw_intr_stat; /* 0x34 */
|
||||
u32 ic_rx_tl; /* 0x38 */
|
||||
u32 ic_tx_tl; /* 0x3c */
|
||||
u32 ic_clr_intr; /* 0x40 */
|
||||
u32 ic_clr_rx_under; /* 0x44 */
|
||||
u32 ic_clr_rx_over; /* 0x48 */
|
||||
u32 ic_clr_tx_over; /* 0x4c */
|
||||
u32 ic_clr_rd_req; /* 0x50 */
|
||||
u32 ic_clr_tx_abrt; /* 0x54 */
|
||||
u32 ic_clr_rx_done; /* 0x58 */
|
||||
u32 ic_clr_activity; /* 0x5c */
|
||||
u32 ic_clr_stop_det; /* 0x60 */
|
||||
u32 ic_clr_start_det; /* 0x64 */
|
||||
u32 ic_clr_gen_call; /* 0x68 */
|
||||
u32 ic_enable; /* 0x6c */
|
||||
u32 ic_status; /* 0x70 */
|
||||
u32 ic_txflr; /* 0x74 */
|
||||
u32 ic_rxflr; /* 0x78 */
|
||||
u32 ic_sda_hold; /* 0x7c */
|
||||
u32 ic_tx_abrt_source; /* 0x80 */
|
||||
u8 res1[0x18]; /* 0x84 */
|
||||
u32 ic_enable_status; /* 0x9c */
|
||||
};
|
||||
|
||||
#if !defined(IC_CLK)
|
||||
#define IC_CLK 166
|
||||
#endif
|
||||
#define NANO_TO_MICRO 1000
|
||||
|
||||
/* High and low times in different speed modes (in ns) */
|
||||
#define MIN_SS_SCL_HIGHTIME 4000
|
||||
#define MIN_SS_SCL_LOWTIME 4700
|
||||
#define MIN_FS_SCL_HIGHTIME 600
|
||||
#define MIN_FS_SCL_LOWTIME 1300
|
||||
#define MIN_HS_SCL_HIGHTIME 60
|
||||
#define MIN_HS_SCL_LOWTIME 160
|
||||
|
||||
/* Worst case timeout for 1 byte is kept as 2ms */
|
||||
#define I2C_BYTE_TO (CONFIG_SYS_HZ/500)
|
||||
#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500)
|
||||
#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16)
|
||||
|
||||
/* i2c control register definitions */
|
||||
#define IC_CON_SD 0x0040
|
||||
#define IC_CON_RE 0x0020
|
||||
#define IC_CON_10BITADDRMASTER 0x0010
|
||||
#define IC_CON_10BITADDR_SLAVE 0x0008
|
||||
#define IC_CON_SPD_MSK 0x0006
|
||||
#define IC_CON_SPD_SS 0x0002
|
||||
#define IC_CON_SPD_FS 0x0004
|
||||
#define IC_CON_SPD_HS 0x0006
|
||||
#define IC_CON_MM 0x0001
|
||||
|
||||
/* i2c target address register definitions */
|
||||
#define TAR_ADDR 0x0050
|
||||
|
||||
/* i2c slave address register definitions */
|
||||
#define IC_SLAVE_ADDR 0x0002
|
||||
|
||||
/* i2c data buffer and command register definitions */
|
||||
#define IC_CMD 0x0100
|
||||
#define IC_STOP 0x0200
|
||||
|
||||
/* i2c interrupt status register definitions */
|
||||
#define IC_GEN_CALL 0x0800
|
||||
#define IC_START_DET 0x0400
|
||||
#define IC_STOP_DET 0x0200
|
||||
#define IC_ACTIVITY 0x0100
|
||||
#define IC_RX_DONE 0x0080
|
||||
#define IC_TX_ABRT 0x0040
|
||||
#define IC_RD_REQ 0x0020
|
||||
#define IC_TX_EMPTY 0x0010
|
||||
#define IC_TX_OVER 0x0008
|
||||
#define IC_RX_FULL 0x0004
|
||||
#define IC_RX_OVER 0x0002
|
||||
#define IC_RX_UNDER 0x0001
|
||||
|
||||
/* fifo threshold register definitions */
|
||||
#define IC_TL0 0x00
|
||||
#define IC_TL1 0x01
|
||||
#define IC_TL2 0x02
|
||||
#define IC_TL3 0x03
|
||||
#define IC_TL4 0x04
|
||||
#define IC_TL5 0x05
|
||||
#define IC_TL6 0x06
|
||||
#define IC_TL7 0x07
|
||||
#define IC_RX_TL IC_TL0
|
||||
#define IC_TX_TL IC_TL0
|
||||
|
||||
/* i2c enable register definitions */
|
||||
#define IC_ENABLE_0B 0x0001
|
||||
|
||||
/* i2c status register definitions */
|
||||
#define IC_STATUS_SA 0x0040
|
||||
#define IC_STATUS_MA 0x0020
|
||||
#define IC_STATUS_RFF 0x0010
|
||||
#define IC_STATUS_RFNE 0x0008
|
||||
#define IC_STATUS_TFE 0x0004
|
||||
#define IC_STATUS_TFNF 0x0002
|
||||
#define IC_STATUS_ACT 0x0001
|
||||
|
||||
/* Speed Selection */
|
||||
#define IC_SPEED_MODE_STANDARD 1
|
||||
#define IC_SPEED_MODE_FAST 2
|
||||
#define IC_SPEED_MODE_MAX 3
|
||||
|
||||
#define I2C_MAX_SPEED 3400000
|
||||
#define I2C_FAST_SPEED 400000
|
||||
#define I2C_STANDARD_SPEED 100000
|
||||
|
||||
#endif /* __DW_I2C_H_ */
|
||||
664
u-boot/drivers/i2c/fsl_i2c.c
Normal file
664
u-boot/drivers/i2c/fsl_i2c.c
Normal file
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
* Copyright 2006,2009 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 2012, Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
* Changes for multibus/multiadapter I2C support.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <i2c.h> /* Functional interface */
|
||||
#include <asm/io.h>
|
||||
#include <asm/fsl_i2c.h> /* HW definitions */
|
||||
#include <dm.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
/* The maximum number of microseconds we will wait until another master has
|
||||
* released the bus. If not defined in the board header file, then use a
|
||||
* generic value.
|
||||
*/
|
||||
#ifndef CONFIG_I2C_MBB_TIMEOUT
|
||||
#define CONFIG_I2C_MBB_TIMEOUT 100000
|
||||
#endif
|
||||
|
||||
/* The maximum number of microseconds we will wait for a read or write
|
||||
* operation to complete. If not defined in the board header file, then use a
|
||||
* generic value.
|
||||
*/
|
||||
#ifndef CONFIG_I2C_TIMEOUT
|
||||
#define CONFIG_I2C_TIMEOUT 100000
|
||||
#endif
|
||||
|
||||
#define I2C_READ_BIT 1
|
||||
#define I2C_WRITE_BIT 0
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static const struct fsl_i2c_base *i2c_base[4] = {
|
||||
(struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET),
|
||||
#ifdef CONFIG_SYS_FSL_I2C2_OFFSET
|
||||
(struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_I2C3_OFFSET
|
||||
(struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C3_OFFSET),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_I2C4_OFFSET
|
||||
(struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C4_OFFSET)
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
/* I2C speed map for a DFSR value of 1 */
|
||||
|
||||
/*
|
||||
* Map I2C frequency dividers to FDR and DFSR values
|
||||
*
|
||||
* This structure is used to define the elements of a table that maps I2C
|
||||
* frequency divider (I2C clock rate divided by I2C bus speed) to a value to be
|
||||
* programmed into the Frequency Divider Ratio (FDR) and Digital Filter
|
||||
* Sampling Rate (DFSR) registers.
|
||||
*
|
||||
* The actual table should be defined in the board file, and it must be called
|
||||
* fsl_i2c_speed_map[].
|
||||
*
|
||||
* The last entry of the table must have a value of {-1, X}, where X is same
|
||||
* FDR/DFSR values as the second-to-last entry. This guarantees that any
|
||||
* search through the array will always find a match.
|
||||
*
|
||||
* The values of the divider must be in increasing numerical order, i.e.
|
||||
* fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider.
|
||||
*
|
||||
* For this table, the values are based on a value of 1 for the DFSR
|
||||
* register. See the application note AN2919 "Determining the I2C Frequency
|
||||
* Divider Ratio for SCL"
|
||||
*
|
||||
* ColdFire I2C frequency dividers for FDR values are different from
|
||||
* PowerPC. The protocol to use the I2C module is still the same.
|
||||
* A different table is defined and are based on MCF5xxx user manual.
|
||||
*
|
||||
*/
|
||||
static const struct {
|
||||
unsigned short divider;
|
||||
u8 fdr;
|
||||
} fsl_i2c_speed_map[] = {
|
||||
#ifdef __M68K__
|
||||
{20, 32}, {22, 33}, {24, 34}, {26, 35},
|
||||
{28, 0}, {28, 36}, {30, 1}, {32, 37},
|
||||
{34, 2}, {36, 38}, {40, 3}, {40, 39},
|
||||
{44, 4}, {48, 5}, {48, 40}, {56, 6},
|
||||
{56, 41}, {64, 42}, {68, 7}, {72, 43},
|
||||
{80, 8}, {80, 44}, {88, 9}, {96, 41},
|
||||
{104, 10}, {112, 42}, {128, 11}, {128, 43},
|
||||
{144, 12}, {160, 13}, {160, 48}, {192, 14},
|
||||
{192, 49}, {224, 50}, {240, 15}, {256, 51},
|
||||
{288, 16}, {320, 17}, {320, 52}, {384, 18},
|
||||
{384, 53}, {448, 54}, {480, 19}, {512, 55},
|
||||
{576, 20}, {640, 21}, {640, 56}, {768, 22},
|
||||
{768, 57}, {960, 23}, {896, 58}, {1024, 59},
|
||||
{1152, 24}, {1280, 25}, {1280, 60}, {1536, 26},
|
||||
{1536, 61}, {1792, 62}, {1920, 27}, {2048, 63},
|
||||
{2304, 28}, {2560, 29}, {3072, 30}, {3840, 31},
|
||||
{-1, 31}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the I2C bus speed for a given I2C device
|
||||
*
|
||||
* @param base: the I2C device registers
|
||||
* @i2c_clk: I2C bus clock frequency
|
||||
* @speed: the desired speed of the bus
|
||||
*
|
||||
* The I2C device must be stopped before calling this function.
|
||||
*
|
||||
* The return value is the actual bus speed that is set.
|
||||
*/
|
||||
static unsigned int set_i2c_bus_speed(const struct fsl_i2c_base *base,
|
||||
unsigned int i2c_clk, unsigned int speed)
|
||||
{
|
||||
unsigned short divider = min(i2c_clk / speed, (unsigned int)USHRT_MAX);
|
||||
|
||||
/*
|
||||
* We want to choose an FDR/DFSR that generates an I2C bus speed that
|
||||
* is equal to or lower than the requested speed. That means that we
|
||||
* want the first divider that is equal to or greater than the
|
||||
* calculated divider.
|
||||
*/
|
||||
#ifdef __PPC__
|
||||
u8 dfsr, fdr = 0x31; /* Default if no FDR found */
|
||||
/* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */
|
||||
unsigned short a, b, ga, gb;
|
||||
unsigned long c_div, est_div;
|
||||
|
||||
#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR
|
||||
dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR;
|
||||
#else
|
||||
/* Condition 1: dfsr <= 50/T */
|
||||
dfsr = (5 * (i2c_clk / 1000)) / 100000;
|
||||
#endif
|
||||
#ifdef CONFIG_FSL_I2C_CUSTOM_FDR
|
||||
fdr = CONFIG_FSL_I2C_CUSTOM_FDR;
|
||||
speed = i2c_clk / divider; /* Fake something */
|
||||
#else
|
||||
debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk);
|
||||
if (!dfsr)
|
||||
dfsr = 1;
|
||||
|
||||
est_div = ~0;
|
||||
for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
|
||||
for (gb = 0; gb < 8; gb++) {
|
||||
b = 16 << gb;
|
||||
c_div = b * (a + ((3*dfsr)/b)*2);
|
||||
if ((c_div > divider) && (c_div < est_div)) {
|
||||
unsigned short bin_gb, bin_ga;
|
||||
|
||||
est_div = c_div;
|
||||
bin_gb = gb << 2;
|
||||
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
|
||||
fdr = bin_gb | bin_ga;
|
||||
speed = i2c_clk / est_div;
|
||||
debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, "
|
||||
"a:%d, b:%d, speed:%d\n",
|
||||
fdr, est_div, ga, gb, a, b, speed);
|
||||
/* Condition 2 not accounted for */
|
||||
debug("Tr <= %d ns\n",
|
||||
(b - 3 * dfsr) * 1000000 /
|
||||
(i2c_clk / 1000));
|
||||
}
|
||||
}
|
||||
if (a == 20)
|
||||
a += 2;
|
||||
if (a == 24)
|
||||
a += 4;
|
||||
}
|
||||
debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
|
||||
debug("FDR:0x%.2x, speed:%d\n", fdr, speed);
|
||||
#endif
|
||||
writeb(dfsr, &base->dfsrr); /* set default filter */
|
||||
writeb(fdr, &base->fdr); /* set bus speed */
|
||||
#else
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++)
|
||||
if (fsl_i2c_speed_map[i].divider >= divider) {
|
||||
u8 fdr;
|
||||
|
||||
fdr = fsl_i2c_speed_map[i].fdr;
|
||||
speed = i2c_clk / fsl_i2c_speed_map[i].divider;
|
||||
writeb(fdr, &base->fdr); /* set bus speed */
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return speed;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static unsigned int get_i2c_clock(int bus)
|
||||
{
|
||||
if (bus)
|
||||
return gd->arch.i2c2_clk; /* I2C2 clock */
|
||||
else
|
||||
return gd->arch.i2c1_clk; /* I2C1 clock */
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fsl_i2c_fixup(const struct fsl_i2c_base *base)
|
||||
{
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
|
||||
unsigned long long timeval = 0;
|
||||
int ret = -1;
|
||||
unsigned int flags = 0;
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447
|
||||
unsigned int svr = get_svr();
|
||||
if ((SVR_SOC_VER(svr) == SVR_8548 && IS_SVR_REV(svr, 3, 1)) ||
|
||||
(SVR_REV(svr) <= CONFIG_SYS_FSL_A004447_SVR_REV))
|
||||
flags = I2C_CR_BIT6;
|
||||
#endif
|
||||
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA, &base->cr);
|
||||
|
||||
timeval = get_ticks();
|
||||
while (!(readb(&base->sr) & I2C_SR_MBB)) {
|
||||
if ((get_ticks() - timeval) > timeout)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (readb(&base->sr) & I2C_SR_MAL) {
|
||||
/* SDA is stuck low */
|
||||
writeb(0, &base->cr);
|
||||
udelay(100);
|
||||
writeb(I2C_CR_MSTA | flags, &base->cr);
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &base->cr);
|
||||
}
|
||||
|
||||
readb(&base->dr);
|
||||
|
||||
timeval = get_ticks();
|
||||
while (!(readb(&base->sr) & I2C_SR_MIF)) {
|
||||
if ((get_ticks() - timeval) > timeout)
|
||||
goto err;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
writeb(I2C_CR_MEN | flags, &base->cr);
|
||||
writeb(0, &base->sr);
|
||||
udelay(100);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __i2c_init(const struct fsl_i2c_base *base, int speed, int
|
||||
slaveadd, int i2c_clk, int busnum)
|
||||
{
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
|
||||
unsigned long long timeval;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
writeb(0, &base->cr); /* stop I2C controller */
|
||||
udelay(5); /* let it shutdown in peace */
|
||||
set_i2c_bus_speed(base, i2c_clk, speed);
|
||||
writeb(slaveadd << 1, &base->adr);/* write slave address */
|
||||
writeb(0x0, &base->sr); /* clear status register */
|
||||
writeb(I2C_CR_MEN, &base->cr); /* start I2C controller */
|
||||
|
||||
timeval = get_ticks();
|
||||
while (readb(&base->sr) & I2C_SR_MBB) {
|
||||
if ((get_ticks() - timeval) < timeout)
|
||||
continue;
|
||||
|
||||
if (fsl_i2c_fixup(base))
|
||||
debug("i2c_init: BUS#%d failed to init\n",
|
||||
busnum);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT
|
||||
/* Call board specific i2c bus reset routine AFTER the bus has been
|
||||
* initialized. Use either this callpoint or i2c_init_board;
|
||||
* which is called before i2c_init operations.
|
||||
* For details about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_board_late_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_wait4bus(const struct fsl_i2c_base *base)
|
||||
{
|
||||
unsigned long long timeval = get_ticks();
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
|
||||
|
||||
while (readb(&base->sr) & I2C_SR_MBB) {
|
||||
if ((get_ticks() - timeval) > timeout)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
i2c_wait(const struct fsl_i2c_base *base, int write)
|
||||
{
|
||||
u32 csr;
|
||||
unsigned long long timeval = get_ticks();
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT);
|
||||
|
||||
do {
|
||||
csr = readb(&base->sr);
|
||||
if (!(csr & I2C_SR_MIF))
|
||||
continue;
|
||||
/* Read again to allow register to stabilise */
|
||||
csr = readb(&base->sr);
|
||||
|
||||
writeb(0x0, &base->sr);
|
||||
|
||||
if (csr & I2C_SR_MAL) {
|
||||
debug("i2c_wait: MAL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(csr & I2C_SR_MCF)) {
|
||||
debug("i2c_wait: unfinished\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) {
|
||||
debug("i2c_wait: No RXACK\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while ((get_ticks() - timeval) < timeout);
|
||||
|
||||
debug("i2c_wait: timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
i2c_write_addr(const struct fsl_i2c_base *base, u8 dev, u8 dir, int rsta)
|
||||
{
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
|
||||
| (rsta ? I2C_CR_RSTA : 0),
|
||||
&base->cr);
|
||||
|
||||
writeb((dev << 1) | dir, &base->dr);
|
||||
|
||||
if (i2c_wait(base, I2C_WRITE_BIT) < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__i2c_write_data(const struct fsl_i2c_base *base, u8 *data, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
writeb(data[i], &base->dr);
|
||||
|
||||
if (i2c_wait(base, I2C_WRITE_BIT) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__i2c_read_data(const struct fsl_i2c_base *base, u8 *data, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0),
|
||||
&base->cr);
|
||||
|
||||
/* dummy read */
|
||||
readb(&base->dr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if (i2c_wait(base, I2C_READ_BIT) < 0)
|
||||
break;
|
||||
|
||||
/* Generate ack on last next to last byte */
|
||||
if (i == length - 2)
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK,
|
||||
&base->cr);
|
||||
|
||||
/* Do not generate stop on last byte */
|
||||
if (i == length - 1)
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
|
||||
&base->cr);
|
||||
|
||||
data[i] = readb(&base->dr);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
__i2c_read(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
|
||||
u8 *data, int dlen)
|
||||
{
|
||||
int ret = -1; /* signal error */
|
||||
|
||||
if (i2c_wait4bus(base) < 0)
|
||||
return -1;
|
||||
|
||||
/* Some drivers use offset lengths in excess of 4 bytes. These drivers
|
||||
* adhere to the following convention:
|
||||
* - the offset length is passed as negative (that is, the absolute
|
||||
* value of olen is the actual offset length)
|
||||
* - the offset itself is passed in data, which is overwritten by the
|
||||
* subsequent read operation
|
||||
*/
|
||||
if (olen < 0) {
|
||||
if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0)
|
||||
ret = __i2c_write_data(base, data, -olen);
|
||||
|
||||
if (ret != -olen)
|
||||
return -1;
|
||||
|
||||
if (dlen && i2c_write_addr(base, chip_addr,
|
||||
I2C_READ_BIT, 1) != 0)
|
||||
ret = __i2c_read_data(base, data, dlen);
|
||||
} else {
|
||||
if ((!dlen || olen > 0) &&
|
||||
i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
|
||||
__i2c_write_data(base, offset, olen) == olen)
|
||||
ret = 0; /* No error so far */
|
||||
|
||||
if (dlen && i2c_write_addr(base, chip_addr, I2C_READ_BIT,
|
||||
olen ? 1 : 0) != 0)
|
||||
ret = __i2c_read_data(base, data, dlen);
|
||||
}
|
||||
|
||||
writeb(I2C_CR_MEN, &base->cr);
|
||||
|
||||
if (i2c_wait4bus(base)) /* Wait until STOP */
|
||||
debug("i2c_read: wait4bus timed out\n");
|
||||
|
||||
if (ret == dlen)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
__i2c_write(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
|
||||
u8 *data, int dlen)
|
||||
{
|
||||
int ret = -1; /* signal error */
|
||||
|
||||
if (i2c_wait4bus(base) < 0)
|
||||
return -1;
|
||||
|
||||
if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
|
||||
__i2c_write_data(base, offset, olen) == olen) {
|
||||
ret = __i2c_write_data(base, data, dlen);
|
||||
}
|
||||
|
||||
writeb(I2C_CR_MEN, &base->cr);
|
||||
if (i2c_wait4bus(base)) /* Wait until STOP */
|
||||
debug("i2c_write: wait4bus timed out\n");
|
||||
|
||||
if (ret == dlen)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
__i2c_probe_chip(const struct fsl_i2c_base *base, uchar chip)
|
||||
{
|
||||
/* For unknow reason the controller will ACK when
|
||||
* probing for a slave with the same address, so skip
|
||||
* it.
|
||||
*/
|
||||
if (chip == (readb(&base->adr) >> 1))
|
||||
return -1;
|
||||
|
||||
return __i2c_read(base, chip, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static unsigned int __i2c_set_bus_speed(const struct fsl_i2c_base *base,
|
||||
unsigned int speed, int i2c_clk)
|
||||
{
|
||||
writeb(0, &base->cr); /* stop controller */
|
||||
set_i2c_bus_speed(base, i2c_clk, speed);
|
||||
writeb(I2C_CR_MEN, &base->cr); /* start controller */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
__i2c_init(i2c_base[adap->hwadapnr], speed, slaveadd,
|
||||
get_i2c_clock(adap->hwadapnr), adap->hwadapnr);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_i2c_probe_chip(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
return __i2c_probe_chip(i2c_base[adap->hwadapnr], chip);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_i2c_read(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
|
||||
u8 *data, int dlen)
|
||||
{
|
||||
u8 *o = (u8 *)&offset;
|
||||
return __i2c_read(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
|
||||
olen, data, dlen);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_i2c_write(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
|
||||
u8 *data, int dlen)
|
||||
{
|
||||
u8 *o = (u8 *)&offset;
|
||||
return __i2c_write(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
|
||||
olen, data, dlen);
|
||||
}
|
||||
|
||||
static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
return __i2c_set_bus_speed(i2c_base[adap->hwadapnr], speed,
|
||||
get_i2c_clock(adap->hwadapnr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Register fsl i2c adapters
|
||||
*/
|
||||
U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
|
||||
fsl_i2c_write, fsl_i2c_set_bus_speed,
|
||||
CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE,
|
||||
0)
|
||||
#ifdef CONFIG_SYS_FSL_I2C2_OFFSET
|
||||
U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
|
||||
fsl_i2c_write, fsl_i2c_set_bus_speed,
|
||||
CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE,
|
||||
1)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_I2C3_OFFSET
|
||||
U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
|
||||
fsl_i2c_write, fsl_i2c_set_bus_speed,
|
||||
CONFIG_SYS_FSL_I2C3_SPEED, CONFIG_SYS_FSL_I2C3_SLAVE,
|
||||
2)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_FSL_I2C4_OFFSET
|
||||
U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
|
||||
fsl_i2c_write, fsl_i2c_set_bus_speed,
|
||||
CONFIG_SYS_FSL_I2C4_SPEED, CONFIG_SYS_FSL_I2C4_SLAVE,
|
||||
3)
|
||||
#endif
|
||||
#else /* CONFIG_DM_I2C */
|
||||
static int fsl_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
||||
u32 chip_flags)
|
||||
{
|
||||
struct fsl_i2c_dev *dev = dev_get_priv(bus);
|
||||
return __i2c_probe_chip(dev->base, chip_addr);
|
||||
}
|
||||
|
||||
static int fsl_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct fsl_i2c_dev *dev = dev_get_priv(bus);
|
||||
return __i2c_set_bus_speed(dev->base, speed, dev->i2c_clk);
|
||||
}
|
||||
|
||||
static int fsl_i2c_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct fsl_i2c_dev *dev = dev_get_priv(bus);
|
||||
u64 reg;
|
||||
u32 addr, size;
|
||||
|
||||
reg = fdtdec_get_addr(gd->fdt_blob, bus->of_offset, "reg");
|
||||
addr = reg >> 32;
|
||||
size = reg & 0xFFFFFFFF;
|
||||
|
||||
dev->base = map_sysmem(CONFIG_SYS_IMMR + addr, size);
|
||||
|
||||
if (!dev->base)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->index = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
||||
"cell-index", -1);
|
||||
dev->slaveadd = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
||||
"u-boot,i2c-slave-addr", 0x7f);
|
||||
dev->speed = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
||||
"clock-frequency", 400000);
|
||||
|
||||
dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : gd->arch.i2c1_clk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct fsl_i2c_dev *dev = dev_get_priv(bus);
|
||||
__i2c_init(dev->base, dev->speed, dev->slaveadd, dev->i2c_clk,
|
||||
dev->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct fsl_i2c_dev *dev = dev_get_priv(bus);
|
||||
struct i2c_msg *dmsg, *omsg, dummy;
|
||||
|
||||
memset(&dummy, 0, sizeof(struct i2c_msg));
|
||||
|
||||
/* We expect either two messages (one with an offset and one with the
|
||||
* actucal data) or one message (just data) */
|
||||
if (nmsgs > 2 || nmsgs == 0) {
|
||||
debug("%s: Only one or two messages are supported.", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
omsg = nmsgs == 1 ? &dummy : msg;
|
||||
dmsg = nmsgs == 1 ? msg : msg + 1;
|
||||
|
||||
if (dmsg->flags & I2C_M_RD)
|
||||
return __i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len,
|
||||
dmsg->buf, dmsg->len);
|
||||
else
|
||||
return __i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len,
|
||||
dmsg->buf, dmsg->len);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops fsl_i2c_ops = {
|
||||
.xfer = fsl_i2c_xfer,
|
||||
.probe_chip = fsl_i2c_probe_chip,
|
||||
.set_bus_speed = fsl_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id fsl_i2c_ids[] = {
|
||||
{ .compatible = "fsl-i2c", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_fsl) = {
|
||||
.name = "i2c_fsl",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = fsl_i2c_ids,
|
||||
.probe = fsl_i2c_probe,
|
||||
.ofdata_to_platdata = fsl_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct fsl_i2c_dev),
|
||||
.ops = &fsl_i2c_ops,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DM_I2C */
|
||||
346
u-boot/drivers/i2c/fti2c010.c
Normal file
346
u-boot/drivers/i2c/fti2c010.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Faraday I2C Controller
|
||||
*
|
||||
* (C) Copyright 2010 Faraday Technology
|
||||
* Dante Su <dantesu@faraday-tech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include "fti2c010.h"
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_SPEED
|
||||
#define CONFIG_SYS_I2C_SPEED 5000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_SLAVE
|
||||
#define CONFIG_SYS_I2C_SLAVE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_FTI2C010_CLOCK
|
||||
#define CONFIG_FTI2C010_CLOCK clk_get_rate("I2C")
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_FTI2C010_TIMEOUT
|
||||
#define CONFIG_FTI2C010_TIMEOUT 10 /* ms */
|
||||
#endif
|
||||
|
||||
/* 7-bit dev address + 1-bit read/write */
|
||||
#define I2C_RD(dev) ((((dev) << 1) & 0xfe) | 1)
|
||||
#define I2C_WR(dev) (((dev) << 1) & 0xfe)
|
||||
|
||||
struct fti2c010_chip {
|
||||
struct fti2c010_regs *regs;
|
||||
};
|
||||
|
||||
static struct fti2c010_chip chip_list[] = {
|
||||
{
|
||||
.regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE,
|
||||
},
|
||||
#ifdef CONFIG_FTI2C010_BASE1
|
||||
{
|
||||
.regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE1,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_FTI2C010_BASE2
|
||||
{
|
||||
.regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE2,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_FTI2C010_BASE3
|
||||
{
|
||||
.regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE3,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static int fti2c010_reset(struct fti2c010_chip *chip)
|
||||
{
|
||||
ulong ts;
|
||||
int ret = -1;
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
|
||||
writel(CR_I2CRST, ®s->cr);
|
||||
for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) {
|
||||
if (!(readl(®s->cr) & CR_I2CRST)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
printf("fti2c010: reset timeout\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fti2c010_wait(struct fti2c010_chip *chip, uint32_t mask)
|
||||
{
|
||||
int ret = -1;
|
||||
uint32_t stat, ts;
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
|
||||
for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) {
|
||||
stat = readl(®s->sr);
|
||||
if ((stat & mask) == mask) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int set_i2c_bus_speed(struct fti2c010_chip *chip,
|
||||
unsigned int speed)
|
||||
{
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
unsigned int clk = CONFIG_FTI2C010_CLOCK;
|
||||
unsigned int gsr = 0;
|
||||
unsigned int tsr = 32;
|
||||
unsigned int div, rate;
|
||||
|
||||
for (div = 0; div < 0x3ffff; ++div) {
|
||||
/* SCLout = PCLK/(2*(COUNT + 2) + GSR) */
|
||||
rate = clk / (2 * (div + 2) + gsr);
|
||||
if (rate <= speed)
|
||||
break;
|
||||
}
|
||||
|
||||
writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr);
|
||||
writel(CDR_DIV(div), ®s->cdr);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization, must be called once on start up, may be called
|
||||
* repeatedly to change the speed and slave addresses.
|
||||
*/
|
||||
static void fti2c010_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
struct fti2c010_chip *chip = chip_list + adap->hwadapnr;
|
||||
|
||||
if (adap->init_done)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
|
||||
/* master init */
|
||||
|
||||
fti2c010_reset(chip);
|
||||
|
||||
set_i2c_bus_speed(chip, speed);
|
||||
|
||||
/* slave init, don't care */
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT
|
||||
/* Call board specific i2c bus reset routine AFTER the bus has been
|
||||
* initialized. Use either this callpoint or i2c_init_board;
|
||||
* which is called before fti2c010_init operations.
|
||||
* For details about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_board_late_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
static int fti2c010_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
struct fti2c010_chip *chip = chip_list + adap->hwadapnr;
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
int ret;
|
||||
|
||||
/* 1. Select slave device (7bits Address + 1bit R/W) */
|
||||
writel(I2C_WR(dev), ®s->dr);
|
||||
writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* 2. Select device register */
|
||||
writel(0, ®s->dr);
|
||||
writel(CR_ENABLE | CR_TBEN, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void to_i2c_addr(u8 *buf, uint32_t addr, int alen)
|
||||
{
|
||||
int i, shift;
|
||||
|
||||
if (!buf || alen <= 0)
|
||||
return;
|
||||
|
||||
/* MSB first */
|
||||
i = 0;
|
||||
shift = (alen - 1) * 8;
|
||||
while (alen-- > 0) {
|
||||
buf[i] = (u8)(addr >> shift);
|
||||
shift -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int fti2c010_read(struct i2c_adapter *adap,
|
||||
u8 dev, uint addr, int alen, uchar *buf, int len)
|
||||
{
|
||||
struct fti2c010_chip *chip = chip_list + adap->hwadapnr;
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
int ret, pos;
|
||||
uchar paddr[4] = { 0 };
|
||||
|
||||
to_i2c_addr(paddr, addr, alen);
|
||||
|
||||
/*
|
||||
* Phase A. Set register address
|
||||
*/
|
||||
|
||||
/* A.1 Select slave device (7bits Address + 1bit R/W) */
|
||||
writel(I2C_WR(dev), ®s->dr);
|
||||
writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* A.2 Select device register */
|
||||
for (pos = 0; pos < alen; ++pos) {
|
||||
uint32_t ctrl = CR_ENABLE | CR_TBEN;
|
||||
|
||||
writel(paddr[pos], ®s->dr);
|
||||
writel(ctrl, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase B. Get register data
|
||||
*/
|
||||
|
||||
/* B.1 Select slave device (7bits Address + 1bit R/W) */
|
||||
writel(I2C_RD(dev), ®s->dr);
|
||||
writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* B.2 Get register data */
|
||||
for (pos = 0; pos < len; ++pos) {
|
||||
uint32_t ctrl = CR_ENABLE | CR_TBEN;
|
||||
uint32_t stat = SR_DR;
|
||||
|
||||
if (pos == len - 1) {
|
||||
ctrl |= CR_NAK | CR_STOP;
|
||||
stat |= SR_ACK;
|
||||
}
|
||||
writel(ctrl, ®s->cr);
|
||||
ret = fti2c010_wait(chip, stat);
|
||||
if (ret)
|
||||
break;
|
||||
buf[pos] = (uchar)(readl(®s->dr) & 0xFF);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fti2c010_write(struct i2c_adapter *adap,
|
||||
u8 dev, uint addr, int alen, u8 *buf, int len)
|
||||
{
|
||||
struct fti2c010_chip *chip = chip_list + adap->hwadapnr;
|
||||
struct fti2c010_regs *regs = chip->regs;
|
||||
int ret, pos;
|
||||
uchar paddr[4] = { 0 };
|
||||
|
||||
to_i2c_addr(paddr, addr, alen);
|
||||
|
||||
/*
|
||||
* Phase A. Set register address
|
||||
*
|
||||
* A.1 Select slave device (7bits Address + 1bit R/W)
|
||||
*/
|
||||
writel(I2C_WR(dev), ®s->dr);
|
||||
writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* A.2 Select device register */
|
||||
for (pos = 0; pos < alen; ++pos) {
|
||||
uint32_t ctrl = CR_ENABLE | CR_TBEN;
|
||||
|
||||
writel(paddr[pos], ®s->dr);
|
||||
writel(ctrl, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase B. Set register data
|
||||
*/
|
||||
for (pos = 0; pos < len; ++pos) {
|
||||
uint32_t ctrl = CR_ENABLE | CR_TBEN;
|
||||
|
||||
if (pos == len - 1)
|
||||
ctrl |= CR_STOP;
|
||||
writel(buf[pos], ®s->dr);
|
||||
writel(ctrl, ®s->cr);
|
||||
ret = fti2c010_wait(chip, SR_DT);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int fti2c010_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
struct fti2c010_chip *chip = chip_list + adap->hwadapnr;
|
||||
int ret;
|
||||
|
||||
fti2c010_reset(chip);
|
||||
ret = set_i2c_bus_speed(chip, speed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register i2c adapters
|
||||
*/
|
||||
U_BOOT_I2C_ADAP_COMPLETE(i2c_0, fti2c010_init, fti2c010_probe, fti2c010_read,
|
||||
fti2c010_write, fti2c010_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE,
|
||||
0)
|
||||
#ifdef CONFIG_FTI2C010_BASE1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(i2c_1, fti2c010_init, fti2c010_probe, fti2c010_read,
|
||||
fti2c010_write, fti2c010_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE,
|
||||
1)
|
||||
#endif
|
||||
#ifdef CONFIG_FTI2C010_BASE2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(i2c_2, fti2c010_init, fti2c010_probe, fti2c010_read,
|
||||
fti2c010_write, fti2c010_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE,
|
||||
2)
|
||||
#endif
|
||||
#ifdef CONFIG_FTI2C010_BASE3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(i2c_3, fti2c010_init, fti2c010_probe, fti2c010_read,
|
||||
fti2c010_write, fti2c010_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE,
|
||||
3)
|
||||
#endif
|
||||
80
u-boot/drivers/i2c/fti2c010.h
Normal file
80
u-boot/drivers/i2c/fti2c010.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Faraday I2C Controller
|
||||
*
|
||||
* (C) Copyright 2010 Faraday Technology
|
||||
* Dante Su <dantesu@faraday-tech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __FTI2C010_H
|
||||
#define __FTI2C010_H
|
||||
|
||||
/*
|
||||
* FTI2C010 registers
|
||||
*/
|
||||
struct fti2c010_regs {
|
||||
uint32_t cr; /* 0x00: control register */
|
||||
uint32_t sr; /* 0x04: status register */
|
||||
uint32_t cdr; /* 0x08: clock division register */
|
||||
uint32_t dr; /* 0x0c: data register */
|
||||
uint32_t sar; /* 0x10: slave address register */
|
||||
uint32_t tgsr;/* 0x14: time & glitch suppression register */
|
||||
uint32_t bmr; /* 0x18: bus monitor register */
|
||||
uint32_t rsvd[5];
|
||||
uint32_t revr;/* 0x30: revision register */
|
||||
};
|
||||
|
||||
/*
|
||||
* control register
|
||||
*/
|
||||
#define CR_ALIRQ 0x2000 /* arbitration lost interrupt (master) */
|
||||
#define CR_SAMIRQ 0x1000 /* slave address match interrupt (slave) */
|
||||
#define CR_STOPIRQ 0x800 /* stop condition interrupt (slave) */
|
||||
#define CR_NAKRIRQ 0x400 /* NACK response interrupt (master) */
|
||||
#define CR_DRIRQ 0x200 /* rx interrupt (both) */
|
||||
#define CR_DTIRQ 0x100 /* tx interrupt (both) */
|
||||
#define CR_TBEN 0x80 /* tx enable (both) */
|
||||
#define CR_NAK 0x40 /* NACK (both) */
|
||||
#define CR_STOP 0x20 /* stop (master) */
|
||||
#define CR_START 0x10 /* start (master) */
|
||||
#define CR_GCEN 0x8 /* general call support (slave) */
|
||||
#define CR_SCLEN 0x4 /* enable clock out (master) */
|
||||
#define CR_I2CEN 0x2 /* enable I2C (both) */
|
||||
#define CR_I2CRST 0x1 /* reset I2C (both) */
|
||||
#define CR_ENABLE \
|
||||
(CR_ALIRQ | CR_NAKRIRQ | CR_DRIRQ | CR_DTIRQ | CR_SCLEN | CR_I2CEN)
|
||||
|
||||
/*
|
||||
* status register
|
||||
*/
|
||||
#define SR_CLRAL 0x400 /* clear arbitration lost */
|
||||
#define SR_CLRGC 0x200 /* clear general call */
|
||||
#define SR_CLRSAM 0x100 /* clear slave address match */
|
||||
#define SR_CLRSTOP 0x80 /* clear stop */
|
||||
#define SR_CLRNAKR 0x40 /* clear NACK respond */
|
||||
#define SR_DR 0x20 /* rx ready */
|
||||
#define SR_DT 0x10 /* tx done */
|
||||
#define SR_BB 0x8 /* bus busy */
|
||||
#define SR_BUSY 0x4 /* chip busy */
|
||||
#define SR_ACK 0x2 /* ACK/NACK received */
|
||||
#define SR_RW 0x1 /* set when master-rx or slave-tx mode */
|
||||
|
||||
/*
|
||||
* clock division register
|
||||
*/
|
||||
#define CDR_DIV(n) ((n) & 0x3ffff)
|
||||
|
||||
/*
|
||||
* time & glitch suppression register
|
||||
*/
|
||||
#define TGSR_GSR(n) (((n) & 0x7) << 10)
|
||||
#define TGSR_TSR(n) ((n) & 0x3ff)
|
||||
|
||||
/*
|
||||
* bus monitor register
|
||||
*/
|
||||
#define BMR_SCL 0x2 /* SCL is pull-up */
|
||||
#define BMR_SDA 0x1 /* SDA is pull-up */
|
||||
|
||||
#endif /* __FTI2C010_H */
|
||||
379
u-boot/drivers/i2c/i2c-cdns.c
Normal file
379
u-boot/drivers/i2c/i2c-cdns.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Moritz Fischer <moritz.fischer@ettus.com>
|
||||
* IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2)
|
||||
*
|
||||
* This file is based on: drivers/i2c/zynq_i2c.c,
|
||||
* with added driver-model support and code cleanup.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <i2c.h>
|
||||
#include <fdtdec.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* i2c register set */
|
||||
struct cdns_i2c_regs {
|
||||
u32 control;
|
||||
u32 status;
|
||||
u32 address;
|
||||
u32 data;
|
||||
u32 interrupt_status;
|
||||
u32 transfer_size;
|
||||
u32 slave_mon_pause;
|
||||
u32 time_out;
|
||||
u32 interrupt_mask;
|
||||
u32 interrupt_enable;
|
||||
u32 interrupt_disable;
|
||||
};
|
||||
|
||||
/* Control register fields */
|
||||
#define CDNS_I2C_CONTROL_RW 0x00000001
|
||||
#define CDNS_I2C_CONTROL_MS 0x00000002
|
||||
#define CDNS_I2C_CONTROL_NEA 0x00000004
|
||||
#define CDNS_I2C_CONTROL_ACKEN 0x00000008
|
||||
#define CDNS_I2C_CONTROL_HOLD 0x00000010
|
||||
#define CDNS_I2C_CONTROL_SLVMON 0x00000020
|
||||
#define CDNS_I2C_CONTROL_CLR_FIFO 0x00000040
|
||||
#define CDNS_I2C_CONTROL_DIV_B_SHIFT 8
|
||||
#define CDNS_I2C_CONTROL_DIV_B_MASK 0x00003F00
|
||||
#define CDNS_I2C_CONTROL_DIV_A_SHIFT 14
|
||||
#define CDNS_I2C_CONTROL_DIV_A_MASK 0x0000C000
|
||||
|
||||
/* Status register values */
|
||||
#define CDNS_I2C_STATUS_RXDV 0x00000020
|
||||
#define CDNS_I2C_STATUS_TXDV 0x00000040
|
||||
#define CDNS_I2C_STATUS_RXOVF 0x00000080
|
||||
#define CDNS_I2C_STATUS_BA 0x00000100
|
||||
|
||||
/* Interrupt register fields */
|
||||
#define CDNS_I2C_INTERRUPT_COMP 0x00000001
|
||||
#define CDNS_I2C_INTERRUPT_DATA 0x00000002
|
||||
#define CDNS_I2C_INTERRUPT_NACK 0x00000004
|
||||
#define CDNS_I2C_INTERRUPT_TO 0x00000008
|
||||
#define CDNS_I2C_INTERRUPT_SLVRDY 0x00000010
|
||||
#define CDNS_I2C_INTERRUPT_RXOVF 0x00000020
|
||||
#define CDNS_I2C_INTERRUPT_TXOVF 0x00000040
|
||||
#define CDNS_I2C_INTERRUPT_RXUNF 0x00000080
|
||||
#define CDNS_I2C_INTERRUPT_ARBLOST 0x00000200
|
||||
|
||||
#define CDNS_I2C_FIFO_DEPTH 16
|
||||
#define CDNS_I2C_TRANSFER_SIZE_MAX 255 /* Controller transfer limit */
|
||||
|
||||
#ifdef DEBUG
|
||||
static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
|
||||
{
|
||||
int int_status;
|
||||
int status;
|
||||
int_status = readl(&cdns_i2c->interrupt_status);
|
||||
|
||||
status = readl(&cdns_i2c->status);
|
||||
if (int_status || status) {
|
||||
debug("Status: ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_COMP)
|
||||
debug("COMP ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_DATA)
|
||||
debug("DATA ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_NACK)
|
||||
debug("NACK ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_TO)
|
||||
debug("TO ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_SLVRDY)
|
||||
debug("SLVRDY ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_RXOVF)
|
||||
debug("RXOVF ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_TXOVF)
|
||||
debug("TXOVF ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_RXUNF)
|
||||
debug("RXUNF ");
|
||||
if (int_status & CDNS_I2C_INTERRUPT_ARBLOST)
|
||||
debug("ARBLOST ");
|
||||
if (status & CDNS_I2C_STATUS_RXDV)
|
||||
debug("RXDV ");
|
||||
if (status & CDNS_I2C_STATUS_TXDV)
|
||||
debug("TXDV ");
|
||||
if (status & CDNS_I2C_STATUS_RXOVF)
|
||||
debug("RXOVF ");
|
||||
if (status & CDNS_I2C_STATUS_BA)
|
||||
debug("BA ");
|
||||
debug("TS%d ", readl(&cdns_i2c->transfer_size));
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct i2c_cdns_bus {
|
||||
int id;
|
||||
unsigned int input_freq;
|
||||
struct cdns_i2c_regs __iomem *regs; /* register base */
|
||||
};
|
||||
|
||||
/* Wait for an interrupt */
|
||||
static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
|
||||
{
|
||||
int timeout, int_status;
|
||||
|
||||
for (timeout = 0; timeout < 100; timeout++) {
|
||||
udelay(100);
|
||||
int_status = readl(&cdns_i2c->interrupt_status);
|
||||
if (int_status & mask)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear interrupt status flags */
|
||||
writel(int_status & mask, &cdns_i2c->interrupt_status);
|
||||
|
||||
return int_status & mask;
|
||||
}
|
||||
|
||||
#define CDNS_I2C_DIVA_MAX 4
|
||||
#define CDNS_I2C_DIVB_MAX 64
|
||||
|
||||
static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
|
||||
unsigned int *a, unsigned int *b)
|
||||
{
|
||||
unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
|
||||
unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
|
||||
unsigned int last_error, current_error;
|
||||
|
||||
/* calculate (divisor_a+1) x (divisor_b+1) */
|
||||
temp = input_clk / (22 * fscl);
|
||||
|
||||
/*
|
||||
* If the calculated value is negative or 0CDNS_I2C_DIVA_MAX,
|
||||
* the fscl input is out of range. Return error.
|
||||
*/
|
||||
if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
|
||||
return -EINVAL;
|
||||
|
||||
last_error = -1;
|
||||
for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
|
||||
div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
|
||||
|
||||
if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
|
||||
continue;
|
||||
div_b--;
|
||||
|
||||
actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
|
||||
|
||||
if (actual_fscl > fscl)
|
||||
continue;
|
||||
|
||||
current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
|
||||
(fscl - actual_fscl));
|
||||
|
||||
if (last_error > current_error) {
|
||||
calc_div_a = div_a;
|
||||
calc_div_b = div_b;
|
||||
best_fscl = actual_fscl;
|
||||
last_error = current_error;
|
||||
}
|
||||
}
|
||||
|
||||
*a = calc_div_a;
|
||||
*b = calc_div_b;
|
||||
*f = best_fscl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct i2c_cdns_bus *bus = dev_get_priv(dev);
|
||||
u32 div_a = 0, div_b = 0;
|
||||
unsigned long speed_p = speed;
|
||||
int ret = 0;
|
||||
|
||||
if (speed > 400000) {
|
||||
debug("%s, failed to set clock speed to %u\n", __func__,
|
||||
speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = cdns_i2c_calc_divs(&speed_p, bus->input_freq, &div_a, &div_b);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
debug("%s: div_a: %d, div_b: %d, input freq: %d, speed: %d/%ld\n",
|
||||
__func__, div_a, div_b, bus->input_freq, speed, speed_p);
|
||||
|
||||
writel((div_b << CDNS_I2C_CONTROL_DIV_B_SHIFT) |
|
||||
(div_a << CDNS_I2C_CONTROL_DIV_A_SHIFT), &bus->regs->control);
|
||||
|
||||
/* Enable master mode, ack, and 7-bit addressing */
|
||||
setbits_le32(&bus->regs->control, CDNS_I2C_CONTROL_MS |
|
||||
CDNS_I2C_CONTROL_ACKEN | CDNS_I2C_CONTROL_NEA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probe to see if a chip is present. */
|
||||
static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct i2c_cdns_bus *i2c_bus = dev_get_priv(bus);
|
||||
struct cdns_i2c_regs *regs = i2c_bus->regs;
|
||||
|
||||
/* Attempt to read a byte */
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
|
||||
CDNS_I2C_CONTROL_RW);
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
writel(0xFF, ®s->interrupt_status);
|
||||
writel(chip_addr, ®s->address);
|
||||
writel(1, ®s->transfer_size);
|
||||
|
||||
return (cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
|
||||
CDNS_I2C_INTERRUPT_NACK) &
|
||||
CDNS_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 len, bool next_is_read)
|
||||
{
|
||||
u8 *cur_data = data;
|
||||
|
||||
struct cdns_i2c_regs *regs = i2c_bus->regs;
|
||||
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
|
||||
CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
/* if next is a read, we need to clear HOLD, doesn't work */
|
||||
if (next_is_read)
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_RW);
|
||||
|
||||
writel(0xFF, ®s->interrupt_status);
|
||||
writel(addr, ®s->address);
|
||||
|
||||
while (len--) {
|
||||
writel(*(cur_data++), ®s->data);
|
||||
if (readl(®s->transfer_size) == CDNS_I2C_FIFO_DEPTH) {
|
||||
if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP)) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(®s->control,
|
||||
CDNS_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All done... release the bus */
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
/* Wait for the address and data to be sent */
|
||||
if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 len)
|
||||
{
|
||||
u32 status;
|
||||
u32 i = 0;
|
||||
u8 *cur_data = data;
|
||||
|
||||
/* TODO: Fix this */
|
||||
struct cdns_i2c_regs *regs = i2c_bus->regs;
|
||||
|
||||
/* Check the hardware can handle the requested bytes */
|
||||
if ((len < 0) || (len > CDNS_I2C_TRANSFER_SIZE_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO |
|
||||
CDNS_I2C_CONTROL_RW);
|
||||
|
||||
/* Start reading data */
|
||||
writel(addr, ®s->address);
|
||||
writel(len, ®s->transfer_size);
|
||||
|
||||
/* Wait for data */
|
||||
do {
|
||||
status = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
|
||||
CDNS_I2C_INTERRUPT_DATA);
|
||||
if (!status) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
debug("Read %d bytes\n",
|
||||
len - readl(®s->transfer_size));
|
||||
for (; i < len - readl(®s->transfer_size); i++)
|
||||
*(cur_data++) = readl(®s->data);
|
||||
} while (readl(®s->transfer_size) != 0);
|
||||
/* All done... release the bus */
|
||||
clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
|
||||
|
||||
#ifdef DEBUG
|
||||
cdns_i2c_debug_status(regs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
|
||||
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
} else {
|
||||
ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len, next_is_read);
|
||||
}
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
|
||||
if (!i2c_bus->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_bus->input_freq = 100000000; /* TODO hardcode input freq for now */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops cdns_i2c_ops = {
|
||||
.xfer = cdns_i2c_xfer,
|
||||
.probe_chip = cdns_i2c_probe_chip,
|
||||
.set_bus_speed = cdns_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id cdns_i2c_of_match[] = {
|
||||
{ .compatible = "cdns,i2c-r1p10" },
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cdns_i2c) = {
|
||||
.name = "i2c-cdns",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = cdns_i2c_of_match,
|
||||
.ofdata_to_platdata = cdns_i2c_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_cdns_bus),
|
||||
.ops = &cdns_i2c_ops,
|
||||
};
|
||||
14
u-boot/drivers/i2c/i2c-emul-uclass.c
Normal file
14
u-boot/drivers/i2c/i2c-emul-uclass.c
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
|
||||
UCLASS_DRIVER(i2c_emul) = {
|
||||
.id = UCLASS_I2C_EMUL,
|
||||
.name = "i2c_emul",
|
||||
};
|
||||
347
u-boot/drivers/i2c/i2c-gpio.c
Normal file
347
u-boot/drivers/i2c/i2c-gpio.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* (C) Copyright 2015, Samsung Electronics
|
||||
* Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
*
|
||||
* This file is based on: drivers/i2c/soft-i2c.c,
|
||||
* with added driver-model support and code cleanup.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#define DEFAULT_UDELAY 5
|
||||
#define RETRIES 0
|
||||
#define I2C_ACK 0
|
||||
#define I2C_NOACK 1
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum {
|
||||
PIN_SDA = 0,
|
||||
PIN_SCL,
|
||||
PIN_COUNT,
|
||||
};
|
||||
|
||||
struct i2c_gpio_bus {
|
||||
/**
|
||||
* udelay - delay [us] between GPIO toggle operations,
|
||||
* which is 1/4 of I2C speed clock period.
|
||||
*/
|
||||
int udelay;
|
||||
/* sda, scl */
|
||||
struct gpio_desc gpios[PIN_COUNT];
|
||||
};
|
||||
|
||||
static int i2c_gpio_sda_get(struct gpio_desc *sda)
|
||||
{
|
||||
return dm_gpio_get_value(sda);
|
||||
}
|
||||
|
||||
static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit)
|
||||
{
|
||||
if (bit)
|
||||
dm_gpio_set_dir_flags(sda, GPIOD_IS_IN);
|
||||
else
|
||||
dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT);
|
||||
}
|
||||
|
||||
static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit)
|
||||
{
|
||||
ulong flags = GPIOD_IS_OUT;
|
||||
|
||||
if (bit)
|
||||
flags |= GPIOD_IS_OUT_ACTIVE;
|
||||
dm_gpio_set_dir_flags(scl, flags);
|
||||
}
|
||||
|
||||
static void i2c_gpio_write_bit(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay, uchar bit)
|
||||
{
|
||||
i2c_gpio_scl_set(scl, 0);
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, bit);
|
||||
udelay(delay);
|
||||
i2c_gpio_scl_set(scl, 1);
|
||||
udelay(2 * delay);
|
||||
}
|
||||
|
||||
static int i2c_gpio_read_bit(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay)
|
||||
{
|
||||
int value;
|
||||
|
||||
i2c_gpio_scl_set(scl, 1);
|
||||
udelay(delay);
|
||||
value = i2c_gpio_sda_get(sda);
|
||||
udelay(delay);
|
||||
i2c_gpio_scl_set(scl, 0);
|
||||
udelay(2 * delay);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* START: High -> Low on SDA while SCL is High */
|
||||
static void i2c_gpio_send_start(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay)
|
||||
{
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, 1);
|
||||
udelay(delay);
|
||||
i2c_gpio_scl_set(scl, 1);
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, 0);
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
/* STOP: Low -> High on SDA while SCL is High */
|
||||
static void i2c_gpio_send_stop(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay)
|
||||
{
|
||||
i2c_gpio_scl_set(scl, 0);
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, 0);
|
||||
udelay(delay);
|
||||
i2c_gpio_scl_set(scl, 1);
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, 1);
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
/* ack should be I2C_ACK or I2C_NOACK */
|
||||
static void i2c_gpio_send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay, int ack)
|
||||
{
|
||||
i2c_gpio_write_bit(scl, sda, delay, ack);
|
||||
i2c_gpio_scl_set(scl, 0);
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset sequence consisting of 9 clocks with the data signal high
|
||||
* to clock any confused device back into an idle state. Also send a
|
||||
* <stop> at the end of the sequence for belts & suspenders.
|
||||
*/
|
||||
static void i2c_gpio_send_reset(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 9; j++)
|
||||
i2c_gpio_write_bit(scl, sda, delay, 1);
|
||||
|
||||
i2c_gpio_send_stop(scl, sda, delay);
|
||||
}
|
||||
|
||||
/* Set sda high with low clock, before reading slave data */
|
||||
static void i2c_gpio_sda_high(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay)
|
||||
{
|
||||
i2c_gpio_scl_set(scl, 0);
|
||||
udelay(delay);
|
||||
i2c_gpio_sda_set(sda, 1);
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
/* Send 8 bits and look for an acknowledgement */
|
||||
static int i2c_gpio_write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay, uchar data)
|
||||
{
|
||||
int j;
|
||||
int nack;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
i2c_gpio_write_bit(scl, sda, delay, data & 0x80);
|
||||
data <<= 1;
|
||||
}
|
||||
|
||||
udelay(delay);
|
||||
|
||||
/* Look for an <ACK>(negative logic) and return it */
|
||||
i2c_gpio_sda_high(scl, sda, delay);
|
||||
nack = i2c_gpio_read_bit(scl, sda, delay);
|
||||
|
||||
return nack; /* not a nack is an ack */
|
||||
}
|
||||
|
||||
/**
|
||||
* if ack == I2C_ACK, ACK the byte so can continue reading, else
|
||||
* send I2C_NOACK to end the read.
|
||||
*/
|
||||
static uchar i2c_gpio_read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
|
||||
int delay, int ack)
|
||||
{
|
||||
int data;
|
||||
int j;
|
||||
|
||||
i2c_gpio_sda_high(scl, sda, delay);
|
||||
data = 0;
|
||||
for (j = 0; j < 8; j++) {
|
||||
data <<= 1;
|
||||
data |= i2c_gpio_read_bit(scl, sda, delay);
|
||||
}
|
||||
i2c_gpio_send_ack(scl, sda, delay, ack);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* send start and the slave chip address */
|
||||
int i2c_send_slave_addr(struct gpio_desc *scl, struct gpio_desc *sda, int delay,
|
||||
uchar chip)
|
||||
{
|
||||
i2c_gpio_send_start(scl, sda, delay);
|
||||
|
||||
if (i2c_gpio_write_byte(scl, sda, delay, chip)) {
|
||||
i2c_gpio_send_stop(scl, sda, delay);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_gpio_write_data(struct i2c_gpio_bus *bus, uchar chip,
|
||||
uchar *buffer, int len,
|
||||
bool end_with_repeated_start)
|
||||
{
|
||||
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
|
||||
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
|
||||
unsigned int delay = bus->udelay;
|
||||
int failures = 0;
|
||||
|
||||
debug("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
|
||||
|
||||
if (i2c_send_slave_addr(scl, sda, delay, chip << 1)) {
|
||||
debug("i2c_write, no chip responded %02X\n", chip);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (len-- > 0) {
|
||||
if (i2c_gpio_write_byte(scl, sda, delay, *buffer++))
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (!end_with_repeated_start) {
|
||||
i2c_gpio_send_stop(scl, sda, delay);
|
||||
return failures;
|
||||
}
|
||||
|
||||
if (i2c_send_slave_addr(scl, sda, delay, (chip << 1) | 0x1)) {
|
||||
debug("i2c_write, no chip responded %02X\n", chip);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
static int i2c_gpio_read_data(struct i2c_gpio_bus *bus, uchar chip,
|
||||
uchar *buffer, int len)
|
||||
{
|
||||
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
|
||||
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
|
||||
unsigned int delay = bus->udelay;
|
||||
|
||||
debug("%s: chip %x buffer: %p len %d\n", __func__, chip, buffer, len);
|
||||
|
||||
while (len-- > 0)
|
||||
*buffer++ = i2c_gpio_read_byte(scl, sda, delay, len == 0);
|
||||
|
||||
i2c_gpio_send_stop(scl, sda, delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct i2c_gpio_bus *bus = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = i2c_gpio_read_data(bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
} else {
|
||||
ret = i2c_gpio_write_data(bus, msg->addr, msg->buf,
|
||||
msg->len, next_is_read);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags)
|
||||
{
|
||||
struct i2c_gpio_bus *bus = dev_get_priv(dev);
|
||||
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
|
||||
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
|
||||
unsigned int delay = bus->udelay;
|
||||
int ret;
|
||||
|
||||
i2c_gpio_send_start(scl, sda, delay);
|
||||
ret = i2c_gpio_write_byte(scl, sda, delay, (chip << 1) | 0);
|
||||
i2c_gpio_send_stop(scl, sda, delay);
|
||||
|
||||
debug("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
|
||||
__func__, dev->seq, dev->name, chip, chip_flags, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz)
|
||||
{
|
||||
struct i2c_gpio_bus *bus = dev_get_priv(dev);
|
||||
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
|
||||
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
|
||||
|
||||
bus->udelay = 1000000 / (speed_hz << 2);
|
||||
|
||||
i2c_gpio_send_reset(scl, sda, bus->udelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_gpio_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct i2c_gpio_bus *bus = dev_get_priv(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev->of_offset;
|
||||
int ret;
|
||||
|
||||
ret = gpio_request_list_by_name(dev, "gpios", bus->gpios,
|
||||
ARRAY_SIZE(bus->gpios), 0);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us",
|
||||
DEFAULT_UDELAY);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
error("Can't get %s gpios! Error: %d", dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops i2c_gpio_ops = {
|
||||
.xfer = i2c_gpio_xfer,
|
||||
.probe_chip = i2c_gpio_probe,
|
||||
.set_bus_speed = i2c_gpio_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id i2c_gpio_ids[] = {
|
||||
{ .compatible = "i2c-gpio" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_gpio) = {
|
||||
.name = "i2c-gpio",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = i2c_gpio_ids,
|
||||
.ofdata_to_platdata = i2c_gpio_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_gpio_bus),
|
||||
.ops = &i2c_gpio_ops,
|
||||
};
|
||||
129
u-boot/drivers/i2c/i2c-uclass-compat.c
Normal file
129
u-boot/drivers/i2c/i2c-uclass-compat.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
|
||||
static int cur_busnum;
|
||||
|
||||
static int i2c_compat_get_device(uint chip_addr, int alen,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct dm_i2c_chip *chip;
|
||||
int ret;
|
||||
|
||||
ret = i2c_get_chip_for_busnum(cur_busnum, chip_addr, alen, devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
chip = dev_get_parent_platdata(*devp);
|
||||
if (chip->offset_len != alen) {
|
||||
printf("I2C chip %x: requested alen %d does not match chip offset_len %d\n",
|
||||
chip_addr, alen, chip->offset_len);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_probe(uint8_t chip_addr)
|
||||
{
|
||||
struct udevice *bus, *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_I2C, cur_busnum, &bus);
|
||||
if (ret) {
|
||||
debug("Cannot find I2C bus %d: err=%d\n", cur_busnum, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!bus)
|
||||
return -ENOENT;
|
||||
|
||||
return dm_i2c_probe(bus, chip_addr, 0, &dev);
|
||||
}
|
||||
|
||||
int i2c_read(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_compat_get_device(chip_addr, alen, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dm_i2c_read(dev, addr, buffer, len);
|
||||
}
|
||||
|
||||
int i2c_write(uint8_t chip_addr, unsigned int addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_compat_get_device(chip_addr, alen, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dm_i2c_write(dev, addr, buffer, len);
|
||||
}
|
||||
|
||||
int i2c_get_bus_num_fdt(int node)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_I2C, node, &bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return bus->seq;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return cur_busnum;
|
||||
}
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
cur_busnum = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
/* Nothing to do here - the init happens through driver model */
|
||||
}
|
||||
|
||||
void board_i2c_init(const void *blob)
|
||||
{
|
||||
/* Nothing to do here - the init happens through driver model */
|
||||
}
|
||||
|
||||
uint8_t i2c_reg_read(uint8_t chip_addr, uint8_t offset)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_compat_get_device(chip_addr, 1, &dev);
|
||||
if (ret)
|
||||
return 0xff;
|
||||
return dm_i2c_reg_read(dev, offset);
|
||||
}
|
||||
|
||||
void i2c_reg_write(uint8_t chip_addr, uint8_t offset, uint8_t val)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_compat_get_device(chip_addr, 1, &dev);
|
||||
if (!ret)
|
||||
dm_i2c_reg_write(dev, offset, val);
|
||||
}
|
||||
531
u-boot/drivers/i2c/i2c-uclass.c
Normal file
531
u-boot/drivers/i2c/i2c-uclass.c
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <malloc.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define I2C_MAX_OFFSET_LEN 4
|
||||
|
||||
/* Useful debugging function */
|
||||
void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nmsgs; i++) {
|
||||
struct i2c_msg *m = &msg[i];
|
||||
|
||||
printf(" %s %x len=%x", m->flags & I2C_M_RD ? "R" : "W",
|
||||
msg->addr, msg->len);
|
||||
if (!(m->flags & I2C_M_RD))
|
||||
printf(": %x", m->buf[0]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_setup_offset() - Set up a new message with a chip offset
|
||||
*
|
||||
* @chip: Chip to use
|
||||
* @offset: Byte offset within chip
|
||||
* @offset_buf: Place to put byte offset
|
||||
* @msg: Message buffer
|
||||
* @return 0 if OK, -EADDRNOTAVAIL if the offset length is 0. In that case the
|
||||
* message is still set up but will not contain an offset.
|
||||
*/
|
||||
static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset,
|
||||
uint8_t offset_buf[], struct i2c_msg *msg)
|
||||
{
|
||||
int offset_len;
|
||||
|
||||
msg->addr = chip->chip_addr;
|
||||
msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
|
||||
msg->len = chip->offset_len;
|
||||
msg->buf = offset_buf;
|
||||
if (!chip->offset_len)
|
||||
return -EADDRNOTAVAIL;
|
||||
assert(chip->offset_len <= I2C_MAX_OFFSET_LEN);
|
||||
offset_len = chip->offset_len;
|
||||
while (offset_len--)
|
||||
*offset_buf++ = offset >> (8 * offset_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_read_bytewise(struct udevice *dev, uint offset,
|
||||
uint8_t *buffer, int len)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct i2c_msg msg[2], *ptr;
|
||||
uint8_t offset_buf[I2C_MAX_OFFSET_LEN];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_setup_offset(chip, offset + i, offset_buf, msg))
|
||||
return -EINVAL;
|
||||
ptr = msg + 1;
|
||||
ptr->addr = chip->chip_addr;
|
||||
ptr->flags = msg->flags | I2C_M_RD;
|
||||
ptr->len = 1;
|
||||
ptr->buf = &buffer[i];
|
||||
ptr++;
|
||||
|
||||
ret = ops->xfer(bus, msg, ptr - msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_write_bytewise(struct udevice *dev, uint offset,
|
||||
const uint8_t *buffer, int len)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct i2c_msg msg[1];
|
||||
uint8_t buf[I2C_MAX_OFFSET_LEN + 1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_setup_offset(chip, offset + i, buf, msg))
|
||||
return -EINVAL;
|
||||
buf[msg->len++] = buffer[i];
|
||||
|
||||
ret = ops->xfer(bus, msg, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct i2c_msg msg[2], *ptr;
|
||||
uint8_t offset_buf[I2C_MAX_OFFSET_LEN];
|
||||
int msg_count;
|
||||
|
||||
if (!ops->xfer)
|
||||
return -ENOSYS;
|
||||
if (chip->flags & DM_I2C_CHIP_RD_ADDRESS)
|
||||
return i2c_read_bytewise(dev, offset, buffer, len);
|
||||
ptr = msg;
|
||||
if (!i2c_setup_offset(chip, offset, offset_buf, ptr))
|
||||
ptr++;
|
||||
|
||||
if (len) {
|
||||
ptr->addr = chip->chip_addr;
|
||||
ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
|
||||
ptr->flags |= I2C_M_RD;
|
||||
ptr->len = len;
|
||||
ptr->buf = buffer;
|
||||
ptr++;
|
||||
}
|
||||
msg_count = ptr - msg;
|
||||
|
||||
return ops->xfer(bus, msg, msg_count);
|
||||
}
|
||||
|
||||
int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct i2c_msg msg[1];
|
||||
|
||||
if (!ops->xfer)
|
||||
return -ENOSYS;
|
||||
|
||||
if (chip->flags & DM_I2C_CHIP_WR_ADDRESS)
|
||||
return i2c_write_bytewise(dev, offset, buffer, len);
|
||||
/*
|
||||
* The simple approach would be to send two messages here: one to
|
||||
* set the offset and one to write the bytes. However some drivers
|
||||
* will not be expecting this, and some chips won't like how the
|
||||
* driver presents this on the I2C bus.
|
||||
*
|
||||
* The API does not support separate offset and data. We could extend
|
||||
* it with a flag indicating that there is data in the next message
|
||||
* that needs to be processed in the same transaction. We could
|
||||
* instead add an additional buffer to each message. For now, handle
|
||||
* this in the uclass since it isn't clear what the impact on drivers
|
||||
* would be with this extra complication. Unfortunately this means
|
||||
* copying the message.
|
||||
*
|
||||
* Use the stack for small messages, malloc() for larger ones. We
|
||||
* need to allow space for the offset (up to 4 bytes) and the message
|
||||
* itself.
|
||||
*/
|
||||
if (len < 64) {
|
||||
uint8_t buf[I2C_MAX_OFFSET_LEN + len];
|
||||
|
||||
i2c_setup_offset(chip, offset, buf, msg);
|
||||
msg->len += len;
|
||||
memcpy(buf + chip->offset_len, buffer, len);
|
||||
|
||||
return ops->xfer(bus, msg, 1);
|
||||
} else {
|
||||
uint8_t *buf;
|
||||
int ret;
|
||||
|
||||
buf = malloc(I2C_MAX_OFFSET_LEN + len);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
i2c_setup_offset(chip, offset, buf, msg);
|
||||
msg->len += len;
|
||||
memcpy(buf + chip->offset_len, buffer, len);
|
||||
|
||||
ret = ops->xfer(bus, msg, 1);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
|
||||
if (!ops->xfer)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->xfer(bus, msg, nmsgs);
|
||||
}
|
||||
|
||||
int dm_i2c_reg_read(struct udevice *dev, uint offset)
|
||||
{
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(dev, offset, &val, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value)
|
||||
{
|
||||
uint8_t val = value;
|
||||
|
||||
return dm_i2c_write(dev, offset, &val, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_probe_chip() - probe for a chip on a bus
|
||||
*
|
||||
* @bus: Bus to probe
|
||||
* @chip_addr: Chip address to probe
|
||||
* @flags: Flags for the chip
|
||||
* @return 0 if found, -ENOSYS if the driver is invalid, -EREMOTEIO if the chip
|
||||
* does not respond to probe
|
||||
*/
|
||||
static int i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
enum dm_i2c_chip_flags chip_flags)
|
||||
{
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct i2c_msg msg[1];
|
||||
int ret;
|
||||
|
||||
if (ops->probe_chip) {
|
||||
ret = ops->probe_chip(bus, chip_addr, chip_flags);
|
||||
if (!ret || ret != -ENOSYS)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ops->xfer)
|
||||
return -ENOSYS;
|
||||
|
||||
/* Probe with a zero-length message */
|
||||
msg->addr = chip_addr;
|
||||
msg->flags = chip_flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0;
|
||||
msg->len = 0;
|
||||
msg->buf = NULL;
|
||||
|
||||
return ops->xfer(bus, msg, 1);
|
||||
}
|
||||
|
||||
static int i2c_bind_driver(struct udevice *bus, uint chip_addr, uint offset_len,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct dm_i2c_chip *chip;
|
||||
char name[30], *str;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
snprintf(name, sizeof(name), "generic_%x", chip_addr);
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
ret = device_bind_driver(bus, "i2c_generic_chip_drv", str, &dev);
|
||||
debug("%s: device_bind_driver: ret=%d\n", __func__, ret);
|
||||
if (ret)
|
||||
goto err_bind;
|
||||
|
||||
/* Tell the device what we know about it */
|
||||
chip = dev_get_parent_platdata(dev);
|
||||
chip->chip_addr = chip_addr;
|
||||
chip->offset_len = offset_len;
|
||||
ret = device_probe(dev);
|
||||
debug("%s: device_probe: ret=%d\n", __func__, ret);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
|
||||
*devp = dev;
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
/*
|
||||
* If the device failed to probe, unbind it. There is nothing there
|
||||
* on the bus so we don't want to leave it lying around
|
||||
*/
|
||||
device_unbind(dev);
|
||||
err_bind:
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
debug("%s: Searching bus '%s' for address %02x: ", __func__,
|
||||
bus->name, chip_addr);
|
||||
for (device_find_first_child(bus, &dev); dev;
|
||||
device_find_next_child(&dev)) {
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
int ret;
|
||||
|
||||
if (chip->chip_addr == chip_addr) {
|
||||
ret = device_probe(dev);
|
||||
debug("found, ret=%d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
debug("not found\n");
|
||||
return i2c_bind_driver(bus, chip_addr, offset_len, devp);
|
||||
}
|
||||
|
||||
int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus);
|
||||
if (ret) {
|
||||
debug("Cannot find I2C bus %d\n", busnum);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_get_chip(bus, chip_addr, offset_len, devp);
|
||||
if (ret) {
|
||||
debug("Cannot find I2C chip %02x on bus %d\n", chip_addr,
|
||||
busnum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags,
|
||||
struct udevice **devp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
|
||||
/* First probe that chip */
|
||||
ret = i2c_probe_chip(bus, chip_addr, chip_flags);
|
||||
debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name,
|
||||
chip_addr, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The chip was found, see if we have a driver, and probe it */
|
||||
ret = i2c_get_chip(bus, chip_addr, 1, devp);
|
||||
debug("%s: i2c_get_chip: ret=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dm_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we have a method, call it. If not then the driver probably wants
|
||||
* to deal with speed changes on the next transfer. It can easily read
|
||||
* the current speed from this uclass
|
||||
*/
|
||||
if (ops->set_bus_speed) {
|
||||
ret = ops->set_bus_speed(bus, speed);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
i2c->speed_hz = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_i2c_get_bus_speed(struct udevice *bus)
|
||||
{
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
|
||||
|
||||
if (!ops->get_bus_speed)
|
||||
return i2c->speed_hz;
|
||||
|
||||
return ops->get_bus_speed(bus);
|
||||
}
|
||||
|
||||
int i2c_set_chip_flags(struct udevice *dev, uint flags)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
int ret;
|
||||
|
||||
if (ops->set_flags) {
|
||||
ret = ops->set_flags(dev, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
chip->flags = flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_get_chip_flags(struct udevice *dev, uint *flagsp)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
|
||||
*flagsp = chip->flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
|
||||
if (offset_len > I2C_MAX_OFFSET_LEN)
|
||||
return -EINVAL;
|
||||
chip->offset_len = offset_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_get_chip_offset_len(struct udevice *dev)
|
||||
{
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
|
||||
|
||||
return chip->offset_len;
|
||||
}
|
||||
|
||||
int i2c_deblock(struct udevice *bus)
|
||||
{
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
||||
|
||||
/*
|
||||
* We could implement a software deblocking here if we could get
|
||||
* access to the GPIOs used by I2C, and switch them to GPIO mode
|
||||
* and then back to I2C. This is somewhat beyond our powers in
|
||||
* driver model at present, so for now just fail.
|
||||
*
|
||||
* See https://patchwork.ozlabs.org/patch/399040/
|
||||
*/
|
||||
if (!ops->deblock)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->deblock(bus);
|
||||
}
|
||||
|
||||
int i2c_chip_ofdata_to_platdata(const void *blob, int node,
|
||||
struct dm_i2c_chip *chip)
|
||||
{
|
||||
chip->offset_len = fdtdec_get_int(gd->fdt_blob, node,
|
||||
"u-boot,i2c-offset-len", 1);
|
||||
chip->flags = 0;
|
||||
chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1);
|
||||
if (chip->chip_addr == -1) {
|
||||
debug("%s: I2C Node '%s' has no 'reg' property\n", __func__,
|
||||
fdt_get_name(blob, node, NULL));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_post_probe(struct udevice *dev)
|
||||
{
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(dev);
|
||||
|
||||
i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
|
||||
"clock-frequency", 100000);
|
||||
|
||||
return dm_i2c_set_bus_speed(dev, i2c->speed_hz);
|
||||
}
|
||||
|
||||
static int i2c_post_bind(struct udevice *dev)
|
||||
{
|
||||
/* Scan the bus for devices */
|
||||
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
|
||||
}
|
||||
|
||||
static int i2c_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
struct dm_i2c_chip *plat = dev_get_parent_platdata(dev);
|
||||
|
||||
if (dev->of_offset == -1)
|
||||
return 0;
|
||||
|
||||
return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(i2c) = {
|
||||
.id = UCLASS_I2C,
|
||||
.name = "i2c",
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
.post_bind = i2c_post_bind,
|
||||
.post_probe = i2c_post_probe,
|
||||
.per_device_auto_alloc_size = sizeof(struct dm_i2c_bus),
|
||||
.per_child_platdata_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.child_post_bind = i2c_child_post_bind,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(i2c_generic) = {
|
||||
.id = UCLASS_I2C_GENERIC,
|
||||
.name = "i2c_generic",
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_generic_chip_drv) = {
|
||||
.name = "i2c_generic_chip_drv",
|
||||
.id = UCLASS_I2C_GENERIC,
|
||||
};
|
||||
365
u-boot/drivers/i2c/i2c-uniphier-f.c
Normal file
365
u-boot/drivers/i2c/i2c-uniphier-f.c
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/errno.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <i2c.h>
|
||||
#include <fdtdec.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
struct uniphier_fi2c_regs {
|
||||
u32 cr; /* control register */
|
||||
#define I2C_CR_MST (1 << 3) /* master mode */
|
||||
#define I2C_CR_STA (1 << 2) /* start condition */
|
||||
#define I2C_CR_STO (1 << 1) /* stop condition */
|
||||
#define I2C_CR_NACK (1 << 0) /* not ACK */
|
||||
u32 dttx; /* send FIFO (write-only) */
|
||||
#define dtrx dttx /* receive FIFO (read-only) */
|
||||
#define I2C_DTTX_CMD (1 << 8) /* send command (slave addr) */
|
||||
#define I2C_DTTX_RD (1 << 0) /* read */
|
||||
u32 __reserved; /* no register at offset 0x08 */
|
||||
u32 slad; /* slave address */
|
||||
u32 cyc; /* clock cycle control */
|
||||
u32 lctl; /* clock low period control */
|
||||
u32 ssut; /* restart/stop setup time control */
|
||||
u32 dsut; /* data setup time control */
|
||||
u32 intr; /* interrupt status */
|
||||
u32 ie; /* interrupt enable */
|
||||
u32 ic; /* interrupt clear */
|
||||
#define I2C_INT_TE (1 << 9) /* TX FIFO empty */
|
||||
#define I2C_INT_RB (1 << 4) /* received specified bytes */
|
||||
#define I2C_INT_NA (1 << 2) /* no answer */
|
||||
#define I2C_INT_AL (1 << 1) /* arbitration lost */
|
||||
u32 sr; /* status register */
|
||||
#define I2C_SR_DB (1 << 12) /* device busy */
|
||||
#define I2C_SR_BB (1 << 8) /* bus busy */
|
||||
#define I2C_SR_RFF (1 << 3) /* Rx FIFO full */
|
||||
#define I2C_SR_RNE (1 << 2) /* Rx FIFO not empty */
|
||||
#define I2C_SR_TNF (1 << 1) /* Tx FIFO not full */
|
||||
#define I2C_SR_TFE (1 << 0) /* Tx FIFO empty */
|
||||
u32 __reserved2; /* no register at offset 0x30 */
|
||||
u32 rst; /* reset control */
|
||||
#define I2C_RST_TBRST (1 << 2) /* clear Tx FIFO */
|
||||
#define I2C_RST_RBRST (1 << 1) /* clear Rx FIFO */
|
||||
#define I2C_RST_RST (1 << 0) /* forcible bus reset */
|
||||
u32 bm; /* bus monitor */
|
||||
u32 noise; /* noise filter control */
|
||||
u32 tbc; /* Tx byte count setting */
|
||||
u32 rbc; /* Rx byte count setting */
|
||||
u32 tbcm; /* Tx byte count monitor */
|
||||
u32 rbcm; /* Rx byte count monitor */
|
||||
u32 brst; /* bus reset */
|
||||
#define I2C_BRST_FOEN (1 << 1) /* normal operation */
|
||||
#define I2C_BRST_RSCLO (1 << 0) /* release SCL low fixing */
|
||||
};
|
||||
|
||||
#define FIOCLK 50000000
|
||||
|
||||
struct uniphier_fi2c_dev {
|
||||
struct uniphier_fi2c_regs __iomem *regs; /* register base */
|
||||
unsigned long fioclk; /* internal operation clock */
|
||||
unsigned long timeout; /* time out (us) */
|
||||
};
|
||||
|
||||
static int poll_status(u32 __iomem *reg, u32 flag)
|
||||
{
|
||||
int wait = 1000000; /* 1 sec is long enough */
|
||||
|
||||
while (readl(reg) & flag) {
|
||||
if (wait-- < 0)
|
||||
return -EREMOTEIO;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_bus(struct uniphier_fi2c_regs __iomem *regs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* bus forcible reset */
|
||||
writel(I2C_RST_RST, ®s->rst);
|
||||
ret = poll_status(®s->rst, I2C_RST_RST);
|
||||
if (ret < 0)
|
||||
debug("error: fail to reset I2C controller\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_device_busy(struct uniphier_fi2c_regs __iomem *regs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = poll_status(®s->sr, I2C_SR_DB);
|
||||
if (ret < 0) {
|
||||
debug("error: device busy too long. reset...\n");
|
||||
ret = reset_bus(regs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_probe(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
struct uniphier_fi2c_dev *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
addr = dev_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = map_sysmem(addr, SZ_128);
|
||||
if (!priv->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->fioclk = FIOCLK;
|
||||
|
||||
/* bus forcible reset */
|
||||
ret = reset_bus(priv->regs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, &priv->regs->brst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_remove(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_fi2c_dev *priv = dev_get_priv(dev);
|
||||
|
||||
unmap_sysmem(priv->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_irq(struct uniphier_fi2c_dev *dev, u32 flags,
|
||||
bool *stop)
|
||||
{
|
||||
u32 irq;
|
||||
unsigned long wait = dev->timeout;
|
||||
int ret = -EREMOTEIO;
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
irq = readl(&dev->regs->intr);
|
||||
} while (!(irq & flags) && wait--);
|
||||
|
||||
if (wait < 0) {
|
||||
debug("error: time out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq & I2C_INT_AL) {
|
||||
debug("error: arbitration lost\n");
|
||||
*stop = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq & I2C_INT_NA) {
|
||||
debug("error: no answer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int issue_stop(struct uniphier_fi2c_dev *dev, int old_ret)
|
||||
{
|
||||
int ret;
|
||||
|
||||
debug("stop condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STO, &dev->regs->cr);
|
||||
|
||||
ret = poll_status(&dev->regs->sr, I2C_SR_DB);
|
||||
if (ret < 0)
|
||||
debug("error: device busy after operation\n");
|
||||
|
||||
return old_ret ? old_ret : ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_transmit(struct uniphier_fi2c_dev *dev, uint addr,
|
||||
uint len, const u8 *buf, bool *stop)
|
||||
{
|
||||
int ret;
|
||||
const u32 irq_flags = I2C_INT_TE | I2C_INT_NA | I2C_INT_AL;
|
||||
struct uniphier_fi2c_regs __iomem *regs = dev->regs;
|
||||
|
||||
debug("%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
writel(I2C_DTTX_CMD | addr << 1, ®s->dttx);
|
||||
|
||||
writel(irq_flags, ®s->ie);
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
debug("start condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STA, ®s->cr);
|
||||
|
||||
ret = wait_for_irq(dev, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
while (len--) {
|
||||
debug("sending %x\n", *buf);
|
||||
writel(*buf++, ®s->dttx);
|
||||
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
ret = wait_for_irq(dev, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
if (*stop)
|
||||
ret = issue_stop(dev, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_receive(struct uniphier_fi2c_dev *dev, uint addr,
|
||||
uint len, u8 *buf, bool *stop)
|
||||
{
|
||||
int ret = 0;
|
||||
const u32 irq_flags = I2C_INT_RB | I2C_INT_NA | I2C_INT_AL;
|
||||
struct uniphier_fi2c_regs __iomem *regs = dev->regs;
|
||||
|
||||
debug("%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
/*
|
||||
* In case 'len == 0', only the slave address should be sent
|
||||
* for probing, which is covered by the transmit function.
|
||||
*/
|
||||
if (len == 0)
|
||||
return uniphier_fi2c_transmit(dev, addr, len, buf, stop);
|
||||
|
||||
writel(I2C_DTTX_CMD | I2C_DTTX_RD | addr << 1, ®s->dttx);
|
||||
|
||||
writel(0, ®s->rbc);
|
||||
writel(irq_flags, ®s->ie);
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
debug("start condition\n");
|
||||
writel(I2C_CR_MST | I2C_CR_STA | (len == 1 ? I2C_CR_NACK : 0),
|
||||
®s->cr);
|
||||
|
||||
while (len--) {
|
||||
ret = wait_for_irq(dev, irq_flags, stop);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
*buf++ = readl(®s->dtrx);
|
||||
debug("received %x\n", *(buf - 1));
|
||||
|
||||
if (len == 1)
|
||||
writel(I2C_CR_MST | I2C_CR_NACK, ®s->cr);
|
||||
|
||||
writel(irq_flags, ®s->ic);
|
||||
}
|
||||
|
||||
error:
|
||||
writel(irq_flags, ®s->ic);
|
||||
|
||||
if (*stop)
|
||||
ret = issue_stop(dev, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
int ret;
|
||||
struct uniphier_fi2c_dev *dev = dev_get_priv(bus);
|
||||
bool stop;
|
||||
|
||||
ret = check_device_busy(dev->regs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
/* If next message is read, skip the stop condition */
|
||||
stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = uniphier_fi2c_receive(dev, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
else
|
||||
ret = uniphier_fi2c_transmit(dev, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
int ret;
|
||||
unsigned int clk_count;
|
||||
struct uniphier_fi2c_dev *dev = dev_get_priv(bus);
|
||||
struct uniphier_fi2c_regs __iomem *regs = dev->regs;
|
||||
|
||||
/* max supported frequency is 400 kHz */
|
||||
if (speed > 400000)
|
||||
return -EINVAL;
|
||||
|
||||
ret = check_device_busy(dev->regs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* make sure the bus is idle when changing the frequency */
|
||||
writel(I2C_BRST_RSCLO, ®s->brst);
|
||||
|
||||
clk_count = dev->fioclk / speed;
|
||||
|
||||
writel(clk_count, ®s->cyc);
|
||||
writel(clk_count / 2, ®s->lctl);
|
||||
writel(clk_count / 2, ®s->ssut);
|
||||
writel(clk_count / 16, ®s->dsut);
|
||||
|
||||
writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, ®s->brst);
|
||||
|
||||
/*
|
||||
* Theoretically, each byte can be transferred in
|
||||
* 1000000 * 9 / speed usec.
|
||||
* This time out value is long enough.
|
||||
*/
|
||||
dev->timeout = 100000000L / speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops uniphier_fi2c_ops = {
|
||||
.xfer = uniphier_fi2c_xfer,
|
||||
.set_bus_speed = uniphier_fi2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id uniphier_fi2c_of_match[] = {
|
||||
{ .compatible = "socionext,uniphier-fi2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(uniphier_fi2c) = {
|
||||
.name = "uniphier-fi2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = uniphier_fi2c_of_match,
|
||||
.probe = uniphier_fi2c_probe,
|
||||
.remove = uniphier_fi2c_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_fi2c_dev),
|
||||
.ops = &uniphier_fi2c_ops,
|
||||
};
|
||||
226
u-boot/drivers/i2c/i2c-uniphier.c
Normal file
226
u-boot/drivers/i2c/i2c-uniphier.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/errno.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <i2c.h>
|
||||
#include <fdtdec.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
struct uniphier_i2c_regs {
|
||||
u32 dtrm; /* data transmission */
|
||||
#define I2C_DTRM_STA (1 << 10)
|
||||
#define I2C_DTRM_STO (1 << 9)
|
||||
#define I2C_DTRM_NACK (1 << 8)
|
||||
#define I2C_DTRM_RD (1 << 0)
|
||||
u32 drec; /* data reception */
|
||||
#define I2C_DREC_STS (1 << 12)
|
||||
#define I2C_DREC_LRB (1 << 11)
|
||||
#define I2C_DREC_LAB (1 << 9)
|
||||
u32 myad; /* slave address */
|
||||
u32 clk; /* clock frequency control */
|
||||
u32 brst; /* bus reset */
|
||||
#define I2C_BRST_FOEN (1 << 1)
|
||||
#define I2C_BRST_BRST (1 << 0)
|
||||
u32 hold; /* hold time control */
|
||||
u32 bsts; /* bus status monitor */
|
||||
u32 noise; /* noise filter control */
|
||||
u32 setup; /* setup time control */
|
||||
};
|
||||
|
||||
#define IOBUS_FREQ 100000000
|
||||
|
||||
struct uniphier_i2c_dev {
|
||||
struct uniphier_i2c_regs __iomem *regs; /* register base */
|
||||
unsigned long input_clk; /* master clock (Hz) */
|
||||
unsigned long wait_us; /* wait for every byte transfer (us) */
|
||||
};
|
||||
|
||||
static int uniphier_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t addr;
|
||||
struct uniphier_i2c_dev *priv = dev_get_priv(dev);
|
||||
|
||||
addr = dev_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
priv->regs = map_sysmem(addr, SZ_64);
|
||||
if (!priv->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->input_clk = IOBUS_FREQ;
|
||||
|
||||
/* deassert reset */
|
||||
writel(0x3, &priv->regs->brst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_remove(struct udevice *dev)
|
||||
{
|
||||
struct uniphier_i2c_dev *priv = dev_get_priv(dev);
|
||||
|
||||
unmap_sysmem(priv->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_and_recv_byte(struct uniphier_i2c_dev *dev, u32 dtrm)
|
||||
{
|
||||
writel(dtrm, &dev->regs->dtrm);
|
||||
|
||||
/*
|
||||
* This controller only provides interruption to inform the completion
|
||||
* of each byte transfer. (No status register to poll it.)
|
||||
* Unfortunately, U-Boot does not have a good support of interrupt.
|
||||
* Wait for a while.
|
||||
*/
|
||||
udelay(dev->wait_us);
|
||||
|
||||
return readl(&dev->regs->drec);
|
||||
}
|
||||
|
||||
static int send_byte(struct uniphier_i2c_dev *dev, u32 dtrm, bool *stop)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 drec;
|
||||
|
||||
drec = send_and_recv_byte(dev, dtrm);
|
||||
|
||||
if (drec & I2C_DREC_LAB) {
|
||||
debug("uniphier_i2c: bus arbitration failed\n");
|
||||
*stop = false;
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
if (drec & I2C_DREC_LRB) {
|
||||
debug("uniphier_i2c: slave did not return ACK\n");
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_transmit(struct uniphier_i2c_dev *dev, uint addr,
|
||||
uint len, const u8 *buf, bool *stop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
debug("%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK | addr << 1, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
while (len--) {
|
||||
ret = send_byte(dev, I2C_DTRM_NACK | *buf++, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (*stop)
|
||||
writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_receive(struct uniphier_i2c_dev *dev, uint addr,
|
||||
uint len, u8 *buf, bool *stop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
debug("%s: addr = %x, len = %d\n", __func__, addr, len);
|
||||
|
||||
ret = send_byte(dev, I2C_DTRM_STA | I2C_DTRM_NACK |
|
||||
I2C_DTRM_RD | addr << 1, stop);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
while (len--)
|
||||
*buf++ = send_and_recv_byte(dev, len ? 0 : I2C_DTRM_NACK);
|
||||
|
||||
fail:
|
||||
if (*stop)
|
||||
writel(I2C_DTRM_STO | I2C_DTRM_NACK, &dev->regs->dtrm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
int ret = 0;
|
||||
struct uniphier_i2c_dev *dev = dev_get_priv(bus);
|
||||
bool stop;
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
/* If next message is read, skip the stop condition */
|
||||
stop = nmsgs > 1 && msg[1].flags & I2C_M_RD ? false : true;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = uniphier_i2c_receive(dev, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
else
|
||||
ret = uniphier_i2c_transmit(dev, msg->addr, msg->len,
|
||||
msg->buf, &stop);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct uniphier_i2c_dev *priv = dev_get_priv(bus);
|
||||
|
||||
/* max supported frequency is 400 kHz */
|
||||
if (speed > 400000)
|
||||
return -EINVAL;
|
||||
|
||||
/* bus reset: make sure the bus is idle when change the frequency */
|
||||
writel(0x1, &priv->regs->brst);
|
||||
|
||||
writel((priv->input_clk / speed / 2 << 16) | (priv->input_clk / speed),
|
||||
&priv->regs->clk);
|
||||
|
||||
writel(0x3, &priv->regs->brst);
|
||||
|
||||
/*
|
||||
* Theoretically, each byte can be transferred in
|
||||
* 1000000 * 9 / speed usec. For safety, wait more than double.
|
||||
*/
|
||||
priv->wait_us = 20000000 / speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct dm_i2c_ops uniphier_i2c_ops = {
|
||||
.xfer = uniphier_i2c_xfer,
|
||||
.set_bus_speed = uniphier_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id uniphier_i2c_of_match[] = {
|
||||
{ .compatible = "socionext,uniphier-i2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(uniphier_i2c) = {
|
||||
.name = "uniphier-i2c",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = uniphier_i2c_of_match,
|
||||
.probe = uniphier_i2c_probe,
|
||||
.remove = uniphier_i2c_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct uniphier_i2c_dev),
|
||||
.ops = &uniphier_i2c_ops,
|
||||
};
|
||||
404
u-boot/drivers/i2c/i2c_core.c
Normal file
404
u-boot/drivers/i2c/i2c_core.c
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Sergey Kubushyn <ksi@koi8.net>
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*
|
||||
* Multibus/multiadapter I2C core functions (wrappers)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
|
||||
struct i2c_adapter *i2c_get_adapter(int index)
|
||||
{
|
||||
struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter,
|
||||
i2c);
|
||||
int max = ll_entry_count(struct i2c_adapter, i2c);
|
||||
int i;
|
||||
|
||||
if (index >= max) {
|
||||
printf("Error, wrong i2c adapter %d max %d possible\n",
|
||||
index, max);
|
||||
return i2c_adap_p;
|
||||
}
|
||||
if (index == 0)
|
||||
return i2c_adap_p;
|
||||
|
||||
for (i = 0; i < index; i++)
|
||||
i2c_adap_p++;
|
||||
|
||||
return i2c_adap_p;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_DIRECT_BUS)
|
||||
struct i2c_bus_hose i2c_bus[CONFIG_SYS_NUM_I2C_BUSES] =
|
||||
CONFIG_SYS_I2C_BUSES;
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
void i2c_reloc_fixup(void)
|
||||
{
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
||||
struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter,
|
||||
i2c);
|
||||
struct i2c_adapter *tmp = i2c_adap_p;
|
||||
int max = ll_entry_count(struct i2c_adapter, i2c);
|
||||
int i;
|
||||
unsigned long addr;
|
||||
|
||||
if (gd->reloc_off == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
/* i2c_init() */
|
||||
addr = (unsigned long)i2c_adap_p->init;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->init = (void *)addr;
|
||||
/* i2c_probe() */
|
||||
addr = (unsigned long)i2c_adap_p->probe;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->probe = (void *)addr;
|
||||
/* i2c_read() */
|
||||
addr = (unsigned long)i2c_adap_p->read;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->read = (void *)addr;
|
||||
/* i2c_write() */
|
||||
addr = (unsigned long)i2c_adap_p->write;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->write = (void *)addr;
|
||||
/* i2c_set_bus_speed() */
|
||||
addr = (unsigned long)i2c_adap_p->set_bus_speed;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->set_bus_speed = (void *)addr;
|
||||
/* name */
|
||||
addr = (unsigned long)i2c_adap_p->name;
|
||||
addr += gd->reloc_off;
|
||||
i2c_adap_p->name = (char *)addr;
|
||||
tmp++;
|
||||
i2c_adap_p = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
|
||||
/*
|
||||
* i2c_mux_set()
|
||||
* -------------
|
||||
*
|
||||
* This turns on the given channel on I2C multiplexer chip connected to
|
||||
* a given I2C adapter directly or via other multiplexers. In the latter
|
||||
* case the entire multiplexer chain must be initialized first starting
|
||||
* with the one connected directly to the adapter. When disabling a chain
|
||||
* muxes must be programmed in reverse order, starting with the one
|
||||
* farthest from the adapter.
|
||||
*
|
||||
* mux_id is the multiplexer chip type from defined in i2c.h. So far only
|
||||
* NXP (Philips) PCA954x multiplexers are supported. Switches are NOT
|
||||
* supported (anybody uses them?)
|
||||
*/
|
||||
|
||||
static int i2c_mux_set(struct i2c_adapter *adap, int mux_id, int chip,
|
||||
int channel)
|
||||
{
|
||||
uint8_t buf;
|
||||
int ret;
|
||||
|
||||
/* channel < 0 - turn off the mux */
|
||||
if (channel < 0) {
|
||||
buf = 0;
|
||||
ret = adap->write(adap, chip, 0, 0, &buf, 1);
|
||||
if (ret)
|
||||
printf("%s: Could not turn off the mux.\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (mux_id) {
|
||||
case I2C_MUX_PCA9540_ID:
|
||||
case I2C_MUX_PCA9542_ID:
|
||||
if (channel > 1)
|
||||
return -1;
|
||||
buf = (uint8_t)((channel & 0x01) | (1 << 2));
|
||||
break;
|
||||
case I2C_MUX_PCA9544_ID:
|
||||
if (channel > 3)
|
||||
return -1;
|
||||
buf = (uint8_t)((channel & 0x03) | (1 << 2));
|
||||
break;
|
||||
case I2C_MUX_PCA9547_ID:
|
||||
if (channel > 7)
|
||||
return -1;
|
||||
buf = (uint8_t)((channel & 0x07) | (1 << 3));
|
||||
break;
|
||||
case I2C_MUX_PCA9548_ID:
|
||||
if (channel > 7)
|
||||
return -1;
|
||||
buf = (uint8_t)(0x01 << channel);
|
||||
break;
|
||||
default:
|
||||
printf("%s: wrong mux id: %d\n", __func__, mux_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = adap->write(adap, chip, 0, 0, &buf, 1);
|
||||
if (ret)
|
||||
printf("%s: could not set mux: id: %d chip: %x channel: %d\n",
|
||||
__func__, mux_id, chip, channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_mux_set_all(void)
|
||||
{
|
||||
struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS];
|
||||
int i;
|
||||
|
||||
/* Connect requested bus if behind muxes */
|
||||
if (i2c_bus_tmp->next_hop[0].chip != 0) {
|
||||
/* Set all muxes along the path to that bus */
|
||||
for (i = 0; i < CONFIG_SYS_I2C_MAX_HOPS; i++) {
|
||||
int ret;
|
||||
|
||||
if (i2c_bus_tmp->next_hop[i].chip == 0)
|
||||
break;
|
||||
|
||||
ret = i2c_mux_set(I2C_ADAP,
|
||||
i2c_bus_tmp->next_hop[i].mux.id,
|
||||
i2c_bus_tmp->next_hop[i].chip,
|
||||
i2c_bus_tmp->next_hop[i].channel);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_mux_disconnect_all(void)
|
||||
{
|
||||
struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS];
|
||||
int i;
|
||||
uint8_t buf = 0;
|
||||
|
||||
if (I2C_ADAP->init_done == 0)
|
||||
return 0;
|
||||
|
||||
/* Disconnect current bus (turn off muxes if any) */
|
||||
if ((i2c_bus_tmp->next_hop[0].chip != 0) &&
|
||||
(I2C_ADAP->init_done != 0)) {
|
||||
i = CONFIG_SYS_I2C_MAX_HOPS;
|
||||
do {
|
||||
uint8_t chip;
|
||||
int ret;
|
||||
|
||||
chip = i2c_bus_tmp->next_hop[--i].chip;
|
||||
if (chip == 0)
|
||||
continue;
|
||||
|
||||
ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1);
|
||||
if (ret != 0) {
|
||||
printf("i2c: mux disconnect error\n");
|
||||
return ret;
|
||||
}
|
||||
} while (i > 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* i2c_init_bus():
|
||||
* ---------------
|
||||
*
|
||||
* Initializes one bus. Will initialize the parent adapter. No current bus
|
||||
* changes, no mux (if any) setup.
|
||||
*/
|
||||
static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr)
|
||||
{
|
||||
if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES)
|
||||
return;
|
||||
|
||||
I2C_ADAP->init(I2C_ADAP, speed, slaveaddr);
|
||||
|
||||
if (gd->flags & GD_FLG_RELOC) {
|
||||
I2C_ADAP->init_done = 1;
|
||||
I2C_ADAP->speed = speed;
|
||||
I2C_ADAP->slaveaddr = slaveaddr;
|
||||
}
|
||||
}
|
||||
|
||||
/* implement possible board specific board init */
|
||||
__weak void i2c_init_board(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* implement possible for i2c specific early i2c init */
|
||||
__weak void i2c_early_init_f(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_init_all():
|
||||
*
|
||||
* not longer needed, will deleted. Actual init the SPD_BUS
|
||||
* for compatibility.
|
||||
* i2c_adap[] must be initialized beforehead with function pointers and
|
||||
* data, including speed and slaveaddr.
|
||||
*/
|
||||
void i2c_init_all(void)
|
||||
{
|
||||
i2c_init_board();
|
||||
i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_get_bus_num():
|
||||
* ------------------
|
||||
*
|
||||
* Returns index of currently active I2C bus. Zero-based.
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return gd->cur_i2c_bus;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_set_bus_num():
|
||||
* ------------------
|
||||
*
|
||||
* Change the active I2C bus. Subsequent read/write calls will
|
||||
* go to this one. Sets all of the muxes in a proper condition
|
||||
* if that bus is behind muxes.
|
||||
* If previously selected bus is behind the muxes turns off all the
|
||||
* muxes along the path to that bus.
|
||||
*
|
||||
* bus - bus index, zero based
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
int max;
|
||||
|
||||
if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0))
|
||||
return 0;
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
|
||||
if (bus >= CONFIG_SYS_NUM_I2C_BUSES)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
max = ll_entry_count(struct i2c_adapter, i2c);
|
||||
if (I2C_ADAPTER(bus) >= max) {
|
||||
printf("Error, wrong i2c adapter %d max %d possible\n",
|
||||
I2C_ADAPTER(bus), max);
|
||||
return -2;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
|
||||
i2c_mux_disconnect_all();
|
||||
#endif
|
||||
|
||||
gd->cur_i2c_bus = bus;
|
||||
if (I2C_ADAP->init_done == 0)
|
||||
i2c_init_bus(bus, I2C_ADAP->speed, I2C_ADAP->slaveaddr);
|
||||
|
||||
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
|
||||
i2c_mux_set_all();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
int i2c_probe(uint8_t chip)
|
||||
{
|
||||
return I2C_ADAP->probe(I2C_ADAP, chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/Write interface:
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Where to read/write the data
|
||||
* len: How many bytes to read/write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
int i2c_read(uint8_t chip, unsigned int addr, int alen,
|
||||
uint8_t *buffer, int len)
|
||||
{
|
||||
return I2C_ADAP->read(I2C_ADAP, chip, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
int i2c_write(uint8_t chip, unsigned int addr, int alen,
|
||||
uint8_t *buffer, int len)
|
||||
{
|
||||
return I2C_ADAP->write(I2C_ADAP, chip, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
unsigned int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
if (I2C_ADAP->set_bus_speed == NULL)
|
||||
return 0;
|
||||
ret = I2C_ADAP->set_bus_speed(I2C_ADAP, speed);
|
||||
if (gd->flags & GD_FLG_RELOC)
|
||||
I2C_ADAP->speed = (ret == 0) ? speed : 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
struct i2c_adapter *cur = I2C_ADAP;
|
||||
return cur->speed;
|
||||
}
|
||||
|
||||
uint8_t i2c_reg_read(uint8_t addr, uint8_t reg)
|
||||
{
|
||||
uint8_t buf;
|
||||
|
||||
#ifdef CONFIG_8xx
|
||||
/* MPC8xx needs this. Maybe one day we can get rid of it. */
|
||||
/* maybe it is now the time for it ... */
|
||||
i2c_set_bus_num(i2c_get_bus_num());
|
||||
#endif
|
||||
i2c_read(addr, reg, 1, &buf, 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n",
|
||||
__func__, i2c_get_bus_num(), addr, reg, buf);
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val)
|
||||
{
|
||||
#ifdef CONFIG_8xx
|
||||
/* MPC8xx needs this. Maybe one day we can get rid of it. */
|
||||
/* maybe it is now the time for it ... */
|
||||
i2c_set_bus_num(i2c_get_bus_num());
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n",
|
||||
__func__, i2c_get_bus_num(), addr, reg, val);
|
||||
#endif
|
||||
|
||||
i2c_write(addr, reg, 1, &val, 1);
|
||||
}
|
||||
|
||||
__weak void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
i2c_init_bus(i2c_get_bus_num(), speed, slaveaddr);
|
||||
}
|
||||
257
u-boot/drivers/i2c/ihs_i2c.c
Normal file
257
u-boot/drivers/i2c/ihs_i2c.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <gdsys_fpga.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
#define I2C_SET_REG(fld, val) \
|
||||
do { \
|
||||
if (I2C_ADAP_HWNR & 0x10) \
|
||||
FPGA_SET_REG(I2C_ADAP_HWNR & 0xf, i2c1.fld, val); \
|
||||
else \
|
||||
FPGA_SET_REG(I2C_ADAP_HWNR, i2c0.fld, val); \
|
||||
} while (0)
|
||||
#else
|
||||
#define I2C_SET_REG(fld, val) \
|
||||
FPGA_SET_REG(I2C_ADAP_HWNR, i2c0.fld, val)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
#define I2C_GET_REG(fld, val) \
|
||||
do { \
|
||||
if (I2C_ADAP_HWNR & 0x10) \
|
||||
FPGA_GET_REG(I2C_ADAP_HWNR & 0xf, i2c1.fld, val); \
|
||||
else \
|
||||
FPGA_GET_REG(I2C_ADAP_HWNR, i2c0.fld, val); \
|
||||
} while (0)
|
||||
#else
|
||||
#define I2C_GET_REG(fld, val) \
|
||||
FPGA_GET_REG(I2C_ADAP_HWNR, i2c0.fld, val)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
I2CINT_ERROR_EV = 1 << 13,
|
||||
I2CINT_TRANSMIT_EV = 1 << 14,
|
||||
I2CINT_RECEIVE_EV = 1 << 15,
|
||||
};
|
||||
|
||||
enum {
|
||||
I2CMB_WRITE = 1 << 10,
|
||||
I2CMB_2BYTE = 1 << 11,
|
||||
I2CMB_HOLD_BUS = 1 << 13,
|
||||
I2CMB_NATIVE = 2 << 14,
|
||||
};
|
||||
|
||||
static int wait_for_int(bool read)
|
||||
{
|
||||
u16 val;
|
||||
unsigned int ctr = 0;
|
||||
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
while (!(val & (I2CINT_ERROR_EV
|
||||
| (read ? I2CINT_RECEIVE_EV : I2CINT_TRANSMIT_EV)))) {
|
||||
udelay(10);
|
||||
if (ctr++ > 5000) {
|
||||
return 1;
|
||||
}
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
}
|
||||
|
||||
return (val & I2CINT_ERROR_EV) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read,
|
||||
bool is_last)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
I2C_SET_REG(interrupt_status, I2CINT_ERROR_EV
|
||||
| I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV);
|
||||
I2C_GET_REG(interrupt_status, &val);
|
||||
|
||||
if (!read && len) {
|
||||
val = buffer[0];
|
||||
|
||||
if (len > 1)
|
||||
val |= buffer[1] << 8;
|
||||
I2C_SET_REG(write_mailbox_ext, val);
|
||||
}
|
||||
|
||||
I2C_SET_REG(write_mailbox,
|
||||
I2CMB_NATIVE
|
||||
| (read ? 0 : I2CMB_WRITE)
|
||||
| (chip << 1)
|
||||
| ((len > 1) ? I2CMB_2BYTE : 0)
|
||||
| (is_last ? 0 : I2CMB_HOLD_BUS));
|
||||
|
||||
if (wait_for_int(read))
|
||||
return 1;
|
||||
|
||||
if (read) {
|
||||
I2C_GET_REG(read_mailbox_ext, &val);
|
||||
buffer[0] = val & 0xff;
|
||||
if (len > 1)
|
||||
buffer[1] = val >> 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_address(uchar chip, uint addr, int alen, bool hold_bus)
|
||||
{
|
||||
int shift = (alen-1) * 8;
|
||||
|
||||
while (alen) {
|
||||
int transfer = min(alen, 2);
|
||||
uchar buf[2];
|
||||
bool is_last = alen <= transfer;
|
||||
|
||||
buf[0] = addr >> shift;
|
||||
if (alen > 1)
|
||||
buf[1] = addr >> (shift - 8);
|
||||
|
||||
if (ihs_i2c_transfer(chip, buf, transfer, false,
|
||||
hold_bus ? false : is_last))
|
||||
return 1;
|
||||
|
||||
shift -= 16;
|
||||
alen -= transfer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len, bool read)
|
||||
{
|
||||
if (len <= 0)
|
||||
return 1;
|
||||
|
||||
if (ihs_i2c_address(chip, addr, alen, len))
|
||||
return 1;
|
||||
|
||||
while (len) {
|
||||
int transfer = min(len, 2);
|
||||
|
||||
if (ihs_i2c_transfer(chip, buffer, transfer, read,
|
||||
len <= transfer))
|
||||
return 1;
|
||||
|
||||
buffer += transfer;
|
||||
addr += transfer;
|
||||
len -= transfer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ihs_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/*
|
||||
* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ihs_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
uchar buffer[2];
|
||||
|
||||
if (ihs_i2c_transfer(chip, buffer, 0, true, true))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
return ihs_i2c_access(adap, chip, addr, alen, buffer, len, true);
|
||||
}
|
||||
|
||||
static int ihs_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
return ihs_i2c_access(adap, chip, addr, alen, buffer, len, false);
|
||||
}
|
||||
|
||||
static unsigned int ihs_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
if (speed != adap->speed)
|
||||
return 1;
|
||||
return speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register IHS i2c adapters
|
||||
*/
|
||||
#ifdef CONFIG_SYS_I2C_IHS_CH0
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs0, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_0,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_0, 0)
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs0_1, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_0_1,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_0_1, 16)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_IHS_CH1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs1, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_1,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_1, 1)
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs1_1, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_1_1,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_1_1, 17)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_IHS_CH2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs2, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_2,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_2, 2)
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs2_1, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_2_1,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_2_1, 18)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_IHS_CH3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs3, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_3,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_3, 3)
|
||||
#ifdef CONFIG_SYS_I2C_IHS_DUAL
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ihs3_1, ihs_i2c_init, ihs_i2c_probe,
|
||||
ihs_i2c_read, ihs_i2c_write,
|
||||
ihs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_IHS_SPEED_3_1,
|
||||
CONFIG_SYS_I2C_IHS_SLAVE_3_1, 19)
|
||||
#endif
|
||||
#endif
|
||||
75
u-boot/drivers/i2c/intel_i2c.c
Normal file
75
u-boot/drivers/i2c/intel_i2c.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/pch.h>
|
||||
|
||||
int intel_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int intel_i2c_probe_chip(struct udevice *bus, uint chip_addr, uint chip_flags)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int intel_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
/*
|
||||
* So far this is just setup code for ivybridge SMbus. When we have
|
||||
* a full I2C driver this may need to be moved, generalised or made
|
||||
* dependant on a particular compatible string.
|
||||
*
|
||||
* Set SMBus I/O base
|
||||
*/
|
||||
dm_pci_write_config32(dev, SMB_BASE,
|
||||
SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
|
||||
|
||||
/* Set SMBus enable. */
|
||||
dm_pci_write_config8(dev, HOSTC, HST_EN);
|
||||
|
||||
/* Set SMBus I/O space enable. */
|
||||
dm_pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO);
|
||||
|
||||
/* Disable interrupt generation. */
|
||||
outb(0, SMBUS_IO_BASE + SMBHSTCTL);
|
||||
|
||||
/* Clear any lingering errors, so transactions can run. */
|
||||
outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
|
||||
debug("SMBus controller enabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops intel_i2c_ops = {
|
||||
.xfer = intel_i2c_xfer,
|
||||
.probe_chip = intel_i2c_probe_chip,
|
||||
.set_bus_speed = intel_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id intel_i2c_ids[] = {
|
||||
{ .compatible = "intel,ich-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(intel_i2c) = {
|
||||
.name = "i2c_intel",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = intel_i2c_ids,
|
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.ops = &intel_i2c_ops,
|
||||
.probe = intel_i2c_probe,
|
||||
};
|
||||
730
u-boot/drivers/i2c/kona_i2c.c
Normal file
730
u-boot/drivers/i2c/kona_i2c.c
Normal file
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch/sysmap.h>
|
||||
#include <asm/kona-common/clk.h>
|
||||
#include <i2c.h>
|
||||
|
||||
/* Hardware register offsets and field defintions */
|
||||
#define CS_OFFSET 0x00000020
|
||||
#define CS_ACK_SHIFT 3
|
||||
#define CS_ACK_MASK 0x00000008
|
||||
#define CS_ACK_CMD_GEN_START 0x00000000
|
||||
#define CS_ACK_CMD_GEN_RESTART 0x00000001
|
||||
#define CS_CMD_SHIFT 1
|
||||
#define CS_CMD_CMD_NO_ACTION 0x00000000
|
||||
#define CS_CMD_CMD_START_RESTART 0x00000001
|
||||
#define CS_CMD_CMD_STOP 0x00000002
|
||||
#define CS_EN_SHIFT 0
|
||||
#define CS_EN_CMD_ENABLE_BSC 0x00000001
|
||||
|
||||
#define TIM_OFFSET 0x00000024
|
||||
#define TIM_PRESCALE_SHIFT 6
|
||||
#define TIM_P_SHIFT 3
|
||||
#define TIM_NO_DIV_SHIFT 2
|
||||
#define TIM_DIV_SHIFT 0
|
||||
|
||||
#define DAT_OFFSET 0x00000028
|
||||
|
||||
#define TOUT_OFFSET 0x0000002c
|
||||
|
||||
#define TXFCR_OFFSET 0x0000003c
|
||||
#define TXFCR_FIFO_FLUSH_MASK 0x00000080
|
||||
#define TXFCR_FIFO_EN_MASK 0x00000040
|
||||
|
||||
#define IER_OFFSET 0x00000044
|
||||
#define IER_READ_COMPLETE_INT_MASK 0x00000010
|
||||
#define IER_I2C_INT_EN_MASK 0x00000008
|
||||
#define IER_FIFO_INT_EN_MASK 0x00000002
|
||||
#define IER_NOACK_EN_MASK 0x00000001
|
||||
|
||||
#define ISR_OFFSET 0x00000048
|
||||
#define ISR_RESERVED_MASK 0xffffff60
|
||||
#define ISR_CMDBUSY_MASK 0x00000080
|
||||
#define ISR_READ_COMPLETE_MASK 0x00000010
|
||||
#define ISR_SES_DONE_MASK 0x00000008
|
||||
#define ISR_ERR_MASK 0x00000004
|
||||
#define ISR_TXFIFOEMPTY_MASK 0x00000002
|
||||
#define ISR_NOACK_MASK 0x00000001
|
||||
|
||||
#define CLKEN_OFFSET 0x0000004c
|
||||
#define CLKEN_AUTOSENSE_OFF_MASK 0x00000080
|
||||
#define CLKEN_M_SHIFT 4
|
||||
#define CLKEN_N_SHIFT 1
|
||||
#define CLKEN_CLKEN_MASK 0x00000001
|
||||
|
||||
#define FIFO_STATUS_OFFSET 0x00000054
|
||||
#define FIFO_STATUS_RXFIFO_EMPTY_MASK 0x00000004
|
||||
#define FIFO_STATUS_TXFIFO_EMPTY_MASK 0x00000010
|
||||
|
||||
#define HSTIM_OFFSET 0x00000058
|
||||
#define HSTIM_HS_MODE_MASK 0x00008000
|
||||
#define HSTIM_HS_HOLD_SHIFT 10
|
||||
#define HSTIM_HS_HIGH_PHASE_SHIFT 5
|
||||
#define HSTIM_HS_SETUP_SHIFT 0
|
||||
|
||||
#define PADCTL_OFFSET 0x0000005c
|
||||
#define PADCTL_PAD_OUT_EN_MASK 0x00000004
|
||||
|
||||
#define RXFCR_OFFSET 0x00000068
|
||||
#define RXFCR_NACK_EN_SHIFT 7
|
||||
#define RXFCR_READ_COUNT_SHIFT 0
|
||||
#define RXFIFORDOUT_OFFSET 0x0000006c
|
||||
|
||||
/* Locally used constants */
|
||||
#define MAX_RX_FIFO_SIZE 64U /* bytes */
|
||||
#define MAX_TX_FIFO_SIZE 64U /* bytes */
|
||||
|
||||
#define I2C_TIMEOUT 100000 /* usecs */
|
||||
|
||||
#define WAIT_INT_CHK 100 /* usecs */
|
||||
#if I2C_TIMEOUT % WAIT_INT_CHK
|
||||
#error I2C_TIMEOUT must be a multiple of WAIT_INT_CHK
|
||||
#endif
|
||||
|
||||
/* Operations that can be commanded to the controller */
|
||||
enum bcm_kona_cmd_t {
|
||||
BCM_CMD_NOACTION = 0,
|
||||
BCM_CMD_START,
|
||||
BCM_CMD_RESTART,
|
||||
BCM_CMD_STOP,
|
||||
};
|
||||
|
||||
enum bus_speed_index {
|
||||
BCM_SPD_100K = 0,
|
||||
BCM_SPD_400K,
|
||||
BCM_SPD_1MHZ,
|
||||
};
|
||||
|
||||
/* Internal divider settings for standard mode, fast mode and fast mode plus */
|
||||
struct bus_speed_cfg {
|
||||
uint8_t time_m; /* Number of cycles for setup time */
|
||||
uint8_t time_n; /* Number of cycles for hold time */
|
||||
uint8_t prescale; /* Prescale divider */
|
||||
uint8_t time_p; /* Timing coefficient */
|
||||
uint8_t no_div; /* Disable clock divider */
|
||||
uint8_t time_div; /* Post-prescale divider */
|
||||
};
|
||||
|
||||
static const struct bus_speed_cfg std_cfg_table[] = {
|
||||
[BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02},
|
||||
[BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02},
|
||||
[BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03},
|
||||
};
|
||||
|
||||
struct bcm_kona_i2c_dev {
|
||||
void *base;
|
||||
uint speed;
|
||||
const struct bus_speed_cfg *std_cfg;
|
||||
};
|
||||
|
||||
/* Keep these two defines in sync */
|
||||
#define DEF_SPD 100000
|
||||
#define DEF_SPD_ENUM BCM_SPD_100K
|
||||
|
||||
#define DEF_DEVICE(num) \
|
||||
{(void *)CONFIG_SYS_I2C_BASE##num, DEF_SPD, &std_cfg_table[DEF_SPD_ENUM]}
|
||||
|
||||
static struct bcm_kona_i2c_dev g_i2c_devs[CONFIG_SYS_MAX_I2C_BUS] = {
|
||||
#ifdef CONFIG_SYS_I2C_BASE0
|
||||
DEF_DEVICE(0),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE1
|
||||
DEF_DEVICE(1),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE2
|
||||
DEF_DEVICE(2),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE3
|
||||
DEF_DEVICE(3),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE4
|
||||
DEF_DEVICE(4),
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE5
|
||||
DEF_DEVICE(5),
|
||||
#endif
|
||||
};
|
||||
|
||||
#define I2C_M_TEN 0x0010 /* ten bit address */
|
||||
#define I2C_M_RD 0x0001 /* read data */
|
||||
#define I2C_M_NOSTART 0x4000 /* no restart between msgs */
|
||||
|
||||
struct kona_i2c_msg {
|
||||
uint16_t addr;
|
||||
uint16_t flags;
|
||||
uint16_t len;
|
||||
uint8_t *buf;
|
||||
};
|
||||
|
||||
static void bcm_kona_i2c_send_cmd_to_ctrl(struct bcm_kona_i2c_dev *dev,
|
||||
enum bcm_kona_cmd_t cmd)
|
||||
{
|
||||
debug("%s, %d\n", __func__, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case BCM_CMD_NOACTION:
|
||||
writel((CS_CMD_CMD_NO_ACTION << CS_CMD_SHIFT) |
|
||||
(CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
|
||||
dev->base + CS_OFFSET);
|
||||
break;
|
||||
|
||||
case BCM_CMD_START:
|
||||
writel((CS_ACK_CMD_GEN_START << CS_ACK_SHIFT) |
|
||||
(CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) |
|
||||
(CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
|
||||
dev->base + CS_OFFSET);
|
||||
break;
|
||||
|
||||
case BCM_CMD_RESTART:
|
||||
writel((CS_ACK_CMD_GEN_RESTART << CS_ACK_SHIFT) |
|
||||
(CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) |
|
||||
(CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
|
||||
dev->base + CS_OFFSET);
|
||||
break;
|
||||
|
||||
case BCM_CMD_STOP:
|
||||
writel((CS_CMD_CMD_STOP << CS_CMD_SHIFT) |
|
||||
(CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT),
|
||||
dev->base + CS_OFFSET);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown command %d\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm_kona_i2c_enable_clock(struct bcm_kona_i2c_dev *dev)
|
||||
{
|
||||
writel(readl(dev->base + CLKEN_OFFSET) | CLKEN_CLKEN_MASK,
|
||||
dev->base + CLKEN_OFFSET);
|
||||
}
|
||||
|
||||
static void bcm_kona_i2c_disable_clock(struct bcm_kona_i2c_dev *dev)
|
||||
{
|
||||
writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_CLKEN_MASK,
|
||||
dev->base + CLKEN_OFFSET);
|
||||
}
|
||||
|
||||
/* Wait until at least one of the mask bit(s) are set */
|
||||
static unsigned long wait_for_int_timeout(struct bcm_kona_i2c_dev *dev,
|
||||
unsigned long time_left,
|
||||
uint32_t mask)
|
||||
{
|
||||
uint32_t status;
|
||||
|
||||
while (time_left) {
|
||||
status = readl(dev->base + ISR_OFFSET);
|
||||
|
||||
if ((status & ~ISR_RESERVED_MASK) == 0) {
|
||||
debug("Bogus I2C interrupt 0x%x\n", status);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Must flush the TX FIFO when NAK detected */
|
||||
if (status & ISR_NOACK_MASK)
|
||||
writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK,
|
||||
dev->base + TXFCR_OFFSET);
|
||||
|
||||
writel(status & ~ISR_RESERVED_MASK, dev->base + ISR_OFFSET);
|
||||
|
||||
if (status & mask) {
|
||||
/* We are done since one of the mask bits are set */
|
||||
return time_left;
|
||||
}
|
||||
udelay(WAIT_INT_CHK);
|
||||
time_left -= WAIT_INT_CHK;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send command to I2C bus */
|
||||
static int bcm_kona_send_i2c_cmd(struct bcm_kona_i2c_dev *dev,
|
||||
enum bcm_kona_cmd_t cmd)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned long time_left = I2C_TIMEOUT;
|
||||
|
||||
/* Send the command */
|
||||
bcm_kona_i2c_send_cmd_to_ctrl(dev, cmd);
|
||||
|
||||
/* Wait for transaction to finish or timeout */
|
||||
time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK);
|
||||
|
||||
if (!time_left) {
|
||||
printf("controller timed out\n");
|
||||
rc = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Clear command */
|
||||
bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Read a single RX FIFO worth of data from the i2c bus */
|
||||
static int bcm_kona_i2c_read_fifo_single(struct bcm_kona_i2c_dev *dev,
|
||||
uint8_t *buf, unsigned int len,
|
||||
unsigned int last_byte_nak)
|
||||
{
|
||||
unsigned long time_left = I2C_TIMEOUT;
|
||||
|
||||
/* Start the RX FIFO */
|
||||
writel((last_byte_nak << RXFCR_NACK_EN_SHIFT) |
|
||||
(len << RXFCR_READ_COUNT_SHIFT), dev->base + RXFCR_OFFSET);
|
||||
|
||||
/* Wait for FIFO read to complete */
|
||||
time_left =
|
||||
wait_for_int_timeout(dev, time_left, IER_READ_COMPLETE_INT_MASK);
|
||||
|
||||
if (!time_left) {
|
||||
printf("RX FIFO time out\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
/* Read data from FIFO */
|
||||
for (; len > 0; len--, buf++)
|
||||
*buf = readl(dev->base + RXFIFORDOUT_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read any amount of data using the RX FIFO from the i2c bus */
|
||||
static int bcm_kona_i2c_read_fifo(struct bcm_kona_i2c_dev *dev,
|
||||
struct kona_i2c_msg *msg)
|
||||
{
|
||||
unsigned int bytes_to_read = MAX_RX_FIFO_SIZE;
|
||||
unsigned int last_byte_nak = 0;
|
||||
unsigned int bytes_read = 0;
|
||||
int rc;
|
||||
|
||||
uint8_t *tmp_buf = msg->buf;
|
||||
|
||||
while (bytes_read < msg->len) {
|
||||
if (msg->len - bytes_read <= MAX_RX_FIFO_SIZE) {
|
||||
last_byte_nak = 1; /* NAK last byte of transfer */
|
||||
bytes_to_read = msg->len - bytes_read;
|
||||
}
|
||||
|
||||
rc = bcm_kona_i2c_read_fifo_single(dev, tmp_buf, bytes_to_read,
|
||||
last_byte_nak);
|
||||
if (rc < 0)
|
||||
return -EREMOTEIO;
|
||||
|
||||
bytes_read += bytes_to_read;
|
||||
tmp_buf += bytes_to_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write a single byte of data to the i2c bus */
|
||||
static int bcm_kona_i2c_write_byte(struct bcm_kona_i2c_dev *dev, uint8_t data,
|
||||
unsigned int nak_expected)
|
||||
{
|
||||
unsigned long time_left = I2C_TIMEOUT;
|
||||
unsigned int nak_received;
|
||||
|
||||
/* Clear pending session done interrupt */
|
||||
writel(ISR_SES_DONE_MASK, dev->base + ISR_OFFSET);
|
||||
|
||||
/* Send one byte of data */
|
||||
writel(data, dev->base + DAT_OFFSET);
|
||||
|
||||
time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK);
|
||||
|
||||
if (!time_left) {
|
||||
debug("controller timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
nak_received = readl(dev->base + CS_OFFSET) & CS_ACK_MASK ? 1 : 0;
|
||||
|
||||
if (nak_received ^ nak_expected) {
|
||||
debug("unexpected NAK/ACK\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write a single TX FIFO worth of data to the i2c bus */
|
||||
static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev,
|
||||
uint8_t *buf, unsigned int len)
|
||||
{
|
||||
int k;
|
||||
unsigned long time_left = I2C_TIMEOUT;
|
||||
unsigned int fifo_status;
|
||||
|
||||
/* Write data into FIFO */
|
||||
for (k = 0; k < len; k++)
|
||||
writel(buf[k], (dev->base + DAT_OFFSET));
|
||||
|
||||
/* Wait for FIFO to empty */
|
||||
do {
|
||||
time_left =
|
||||
wait_for_int_timeout(dev, time_left,
|
||||
(IER_FIFO_INT_EN_MASK |
|
||||
IER_NOACK_EN_MASK));
|
||||
fifo_status = readl(dev->base + FIFO_STATUS_OFFSET);
|
||||
} while (time_left && !(fifo_status & FIFO_STATUS_TXFIFO_EMPTY_MASK));
|
||||
|
||||
/* Check if there was a NAK */
|
||||
if (readl(dev->base + CS_OFFSET) & CS_ACK_MASK) {
|
||||
printf("unexpected NAK\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
/* Check if a timeout occurred */
|
||||
if (!time_left) {
|
||||
printf("completion timed out\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write any amount of data using TX FIFO to the i2c bus */
|
||||
static int bcm_kona_i2c_write_fifo(struct bcm_kona_i2c_dev *dev,
|
||||
struct kona_i2c_msg *msg)
|
||||
{
|
||||
unsigned int bytes_to_write = MAX_TX_FIFO_SIZE;
|
||||
unsigned int bytes_written = 0;
|
||||
int rc;
|
||||
|
||||
uint8_t *tmp_buf = msg->buf;
|
||||
|
||||
while (bytes_written < msg->len) {
|
||||
if (msg->len - bytes_written <= MAX_TX_FIFO_SIZE)
|
||||
bytes_to_write = msg->len - bytes_written;
|
||||
|
||||
rc = bcm_kona_i2c_write_fifo_single(dev, tmp_buf,
|
||||
bytes_to_write);
|
||||
if (rc < 0)
|
||||
return -EREMOTEIO;
|
||||
|
||||
bytes_written += bytes_to_write;
|
||||
tmp_buf += bytes_to_write;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send i2c address */
|
||||
static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev,
|
||||
struct kona_i2c_msg *msg)
|
||||
{
|
||||
unsigned char addr;
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
/* First byte is 11110XX0 where XX is upper 2 bits */
|
||||
addr = 0xf0 | ((msg->addr & 0x300) >> 7);
|
||||
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
||||
return -EREMOTEIO;
|
||||
|
||||
/* Second byte is the remaining 8 bits */
|
||||
addr = msg->addr & 0xff;
|
||||
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
||||
return -EREMOTEIO;
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
/* For read, send restart command */
|
||||
if (bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART) < 0)
|
||||
return -EREMOTEIO;
|
||||
|
||||
/* Then re-send the first byte with the read bit set */
|
||||
addr = 0xf0 | ((msg->addr & 0x300) >> 7) | 0x01;
|
||||
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
} else {
|
||||
addr = msg->addr << 1;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
|
||||
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_kona_i2c_enable_autosense(struct bcm_kona_i2c_dev *dev)
|
||||
{
|
||||
writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_AUTOSENSE_OFF_MASK,
|
||||
dev->base + CLKEN_OFFSET);
|
||||
}
|
||||
|
||||
static void bcm_kona_i2c_config_timing(struct bcm_kona_i2c_dev *dev)
|
||||
{
|
||||
writel(readl(dev->base + HSTIM_OFFSET) & ~HSTIM_HS_MODE_MASK,
|
||||
dev->base + HSTIM_OFFSET);
|
||||
|
||||
writel((dev->std_cfg->prescale << TIM_PRESCALE_SHIFT) |
|
||||
(dev->std_cfg->time_p << TIM_P_SHIFT) |
|
||||
(dev->std_cfg->no_div << TIM_NO_DIV_SHIFT) |
|
||||
(dev->std_cfg->time_div << TIM_DIV_SHIFT),
|
||||
dev->base + TIM_OFFSET);
|
||||
|
||||
writel((dev->std_cfg->time_m << CLKEN_M_SHIFT) |
|
||||
(dev->std_cfg->time_n << CLKEN_N_SHIFT) |
|
||||
CLKEN_CLKEN_MASK, dev->base + CLKEN_OFFSET);
|
||||
}
|
||||
|
||||
/* Master transfer function */
|
||||
static int bcm_kona_i2c_xfer(struct bcm_kona_i2c_dev *dev,
|
||||
struct kona_i2c_msg msgs[], int num)
|
||||
{
|
||||
struct kona_i2c_msg *pmsg;
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
/* Enable pad output */
|
||||
writel(0, dev->base + PADCTL_OFFSET);
|
||||
|
||||
/* Enable internal clocks */
|
||||
bcm_kona_i2c_enable_clock(dev);
|
||||
|
||||
/* Send start command */
|
||||
rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_START);
|
||||
if (rc < 0) {
|
||||
printf("Start command failed rc = %d\n", rc);
|
||||
goto xfer_disable_pad;
|
||||
}
|
||||
|
||||
/* Loop through all messages */
|
||||
for (i = 0; i < num; i++) {
|
||||
pmsg = &msgs[i];
|
||||
|
||||
/* Send restart for subsequent messages */
|
||||
if ((i != 0) && ((pmsg->flags & I2C_M_NOSTART) == 0)) {
|
||||
rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART);
|
||||
if (rc < 0) {
|
||||
printf("restart cmd failed rc = %d\n", rc);
|
||||
goto xfer_send_stop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send slave address */
|
||||
if (!(pmsg->flags & I2C_M_NOSTART)) {
|
||||
rc = bcm_kona_i2c_do_addr(dev, pmsg);
|
||||
if (rc < 0) {
|
||||
debug("NAK from addr %2.2x msg#%d rc = %d\n",
|
||||
pmsg->addr, i, rc);
|
||||
goto xfer_send_stop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform data transfer */
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
rc = bcm_kona_i2c_read_fifo(dev, pmsg);
|
||||
if (rc < 0) {
|
||||
printf("read failure\n");
|
||||
goto xfer_send_stop;
|
||||
}
|
||||
} else {
|
||||
rc = bcm_kona_i2c_write_fifo(dev, pmsg);
|
||||
if (rc < 0) {
|
||||
printf("write failure");
|
||||
goto xfer_send_stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = num;
|
||||
|
||||
xfer_send_stop:
|
||||
/* Send a STOP command */
|
||||
bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP);
|
||||
|
||||
xfer_disable_pad:
|
||||
/* Disable pad output */
|
||||
writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET);
|
||||
|
||||
/* Stop internal clock */
|
||||
bcm_kona_i2c_disable_clock(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static uint bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev,
|
||||
uint speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case 100000:
|
||||
dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
|
||||
break;
|
||||
case 400000:
|
||||
dev->std_cfg = &std_cfg_table[BCM_SPD_400K];
|
||||
break;
|
||||
case 1000000:
|
||||
dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ];
|
||||
break;
|
||||
default:
|
||||
printf("%d hz bus speed not supported\n", speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev->speed = speed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_kona_i2c_init(struct bcm_kona_i2c_dev *dev)
|
||||
{
|
||||
/* Parse bus speed */
|
||||
bcm_kona_i2c_assign_bus_speed(dev, dev->speed);
|
||||
|
||||
/* Enable internal clocks */
|
||||
bcm_kona_i2c_enable_clock(dev);
|
||||
|
||||
/* Configure internal dividers */
|
||||
bcm_kona_i2c_config_timing(dev);
|
||||
|
||||
/* Disable timeout */
|
||||
writel(0, dev->base + TOUT_OFFSET);
|
||||
|
||||
/* Enable autosense */
|
||||
bcm_kona_i2c_enable_autosense(dev);
|
||||
|
||||
/* Enable TX FIFO */
|
||||
writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK,
|
||||
dev->base + TXFCR_OFFSET);
|
||||
|
||||
/* Mask all interrupts */
|
||||
writel(0, dev->base + IER_OFFSET);
|
||||
|
||||
/* Clear all pending interrupts */
|
||||
writel(ISR_CMDBUSY_MASK |
|
||||
ISR_READ_COMPLETE_MASK |
|
||||
ISR_SES_DONE_MASK |
|
||||
ISR_ERR_MASK |
|
||||
ISR_TXFIFOEMPTY_MASK | ISR_NOACK_MASK, dev->base + ISR_OFFSET);
|
||||
|
||||
/* Enable the controller but leave it idle */
|
||||
bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION);
|
||||
|
||||
/* Disable pad output */
|
||||
writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* uboot layer
|
||||
*/
|
||||
struct bcm_kona_i2c_dev *kona_get_dev(struct i2c_adapter *adap)
|
||||
{
|
||||
return &g_i2c_devs[adap->hwadapnr];
|
||||
}
|
||||
|
||||
static void kona_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
struct bcm_kona_i2c_dev *dev = kona_get_dev(adap);
|
||||
|
||||
if (clk_bsc_enable(dev->base))
|
||||
return;
|
||||
|
||||
bcm_kona_i2c_init(dev);
|
||||
}
|
||||
|
||||
static int kona_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
/* msg[0] writes the addr, msg[1] reads the data */
|
||||
struct kona_i2c_msg msg[2];
|
||||
unsigned char msgbuf0[64];
|
||||
struct bcm_kona_i2c_dev *dev = kona_get_dev(adap);
|
||||
|
||||
msg[0].addr = chip;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = msgbuf0; /* msgbuf0 contains incrementing reg addr */
|
||||
|
||||
msg[1].addr = chip;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
/* msg[1].buf dest ptr increments each read */
|
||||
|
||||
msgbuf0[0] = (unsigned char)addr;
|
||||
msg[1].buf = buffer;
|
||||
msg[1].len = len;
|
||||
if (bcm_kona_i2c_xfer(dev, msg, 2) < 0) {
|
||||
/* Sending 2 i2c messages */
|
||||
kona_i2c_init(adap, adap->speed, adap->slaveaddr);
|
||||
debug("I2C read: I/O error\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kona_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct kona_i2c_msg msg[1];
|
||||
unsigned char msgbuf0[64];
|
||||
unsigned int i;
|
||||
struct bcm_kona_i2c_dev *dev = kona_get_dev(adap);
|
||||
|
||||
msg[0].addr = chip;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = 2; /* addr byte plus data */
|
||||
msg[0].buf = msgbuf0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
msgbuf0[0] = addr++;
|
||||
msgbuf0[1] = buffer[i];
|
||||
if (bcm_kona_i2c_xfer(dev, msg, 1) < 0) {
|
||||
kona_i2c_init(adap, adap->speed, adap->slaveaddr);
|
||||
debug("I2C write: I/O error\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kona_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
uchar tmp;
|
||||
|
||||
/*
|
||||
* read addr 0x0 of the given chip.
|
||||
*/
|
||||
return kona_i2c_read(adap, chip, 0x0, 1, &tmp, 1);
|
||||
}
|
||||
|
||||
static uint kona_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct bcm_kona_i2c_dev *dev = kona_get_dev(adap);
|
||||
return bcm_kona_i2c_assign_bus_speed(dev, speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register kona i2c adapters. Keep the order below so
|
||||
* that the bus number matches the adapter number.
|
||||
*/
|
||||
#define DEF_ADAPTER(num) \
|
||||
U_BOOT_I2C_ADAP_COMPLETE(kona##num, kona_i2c_init, kona_i2c_probe, \
|
||||
kona_i2c_read, kona_i2c_write, \
|
||||
kona_i2c_set_bus_speed, DEF_SPD, 0x00, num)
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_BASE0
|
||||
DEF_ADAPTER(0)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE1
|
||||
DEF_ADAPTER(1)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE2
|
||||
DEF_ADAPTER(2)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE3
|
||||
DEF_ADAPTER(3)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE4
|
||||
DEF_ADAPTER(4)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_BASE5
|
||||
DEF_ADAPTER(5)
|
||||
#endif
|
||||
265
u-boot/drivers/i2c/lpc32xx_i2c.c
Normal file
265
u-boot/drivers/i2c/lpc32xx_i2c.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* LPC32xx I2C interface driver
|
||||
*
|
||||
* (C) Copyright 2014-2015 DENX Software Engineering GmbH
|
||||
* Written-by: Albert ARIBAUD - 3ADEV <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch/clk.h>
|
||||
|
||||
/*
|
||||
* Provide default speed and slave if target did not
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_LPC32XX_SPEED)
|
||||
#define CONFIG_SYS_I2C_LPC32XX_SPEED 350000
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_LPC32XX_SLAVE)
|
||||
#define CONFIG_SYS_I2C_LPC32XX_SLAVE 0
|
||||
#endif
|
||||
|
||||
/* i2c register set */
|
||||
struct lpc32xx_i2c_registers {
|
||||
union {
|
||||
u32 rx;
|
||||
u32 tx;
|
||||
};
|
||||
u32 stat;
|
||||
u32 ctrl;
|
||||
u32 clk_hi;
|
||||
u32 clk_lo;
|
||||
u32 adr;
|
||||
u32 rxfl;
|
||||
u32 txfl;
|
||||
u32 rxb;
|
||||
u32 txb;
|
||||
u32 stx;
|
||||
u32 stxfl;
|
||||
};
|
||||
|
||||
/* TX register fields */
|
||||
#define LPC32XX_I2C_TX_START 0x00000100
|
||||
#define LPC32XX_I2C_TX_STOP 0x00000200
|
||||
|
||||
/* Control register values */
|
||||
#define LPC32XX_I2C_SOFT_RESET 0x00000100
|
||||
|
||||
/* Status register values */
|
||||
#define LPC32XX_I2C_STAT_TFF 0x00000400
|
||||
#define LPC32XX_I2C_STAT_RFE 0x00000200
|
||||
#define LPC32XX_I2C_STAT_DRMI 0x00000008
|
||||
#define LPC32XX_I2C_STAT_NAI 0x00000004
|
||||
#define LPC32XX_I2C_STAT_TDI 0x00000001
|
||||
|
||||
static struct lpc32xx_i2c_registers *lpc32xx_i2c[] = {
|
||||
(struct lpc32xx_i2c_registers *)I2C1_BASE,
|
||||
(struct lpc32xx_i2c_registers *)I2C2_BASE,
|
||||
(struct lpc32xx_i2c_registers *)(USB_BASE + 0x300)
|
||||
};
|
||||
|
||||
/* Set I2C bus speed */
|
||||
static unsigned int lpc32xx_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
int half_period;
|
||||
|
||||
if (speed == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* OTG I2C clock source and CLK registers are different */
|
||||
if (adap->hwadapnr == 2) {
|
||||
half_period = (get_periph_clk_rate() / speed) / 2;
|
||||
if (half_period > 0xFF)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
half_period = (get_hclk_clk_rate() / speed) / 2;
|
||||
if (half_period > 0x3FF)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_hi);
|
||||
writel(half_period, &lpc32xx_i2c[adap->hwadapnr]->clk_lo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C init called by cmd_i2c when doing 'i2c reset'. */
|
||||
static void _i2c_init(struct i2c_adapter *adap,
|
||||
int requested_speed, int slaveadd)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
|
||||
/* soft reset (auto-clears) */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
/* set HI and LO periods for half of the default speed */
|
||||
lpc32xx_i2c_set_bus_speed(adap, requested_speed);
|
||||
}
|
||||
|
||||
/* I2C probe called by cmd_i2c when doing 'i2c probe'. */
|
||||
static int lpc32xx_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* Addre slave for write with start before and stop after */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START | LPC32XX_I2C_TX_STOP,
|
||||
&i2c->tx);
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* was there no acknowledge? */
|
||||
return (stat & LPC32XX_I2C_STAT_NAI) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
||||
*/
|
||||
static int lpc32xx_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat, wlen;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* do we need to write an address at all? */
|
||||
if (alen) {
|
||||
/* Address slave in write mode */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
/* write address bytes */
|
||||
while (alen--) {
|
||||
/* compute address byte + stop for the last one */
|
||||
int a = (addr >> (8 * alen)) & 0xff;
|
||||
if (!alen)
|
||||
a |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send address byte */
|
||||
writel(a, &i2c->tx);
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
}
|
||||
/* do we have to read data at all? */
|
||||
if (length) {
|
||||
/* Address slave in read mode */
|
||||
writel(1 | (dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
wlen = length;
|
||||
/* get data */
|
||||
while (length | wlen) {
|
||||
/* read status for TFF and RFE */
|
||||
stat = readl(&i2c->stat);
|
||||
/* must we, can we write a trigger byte? */
|
||||
if ((wlen > 0)
|
||||
& (!(stat & LPC32XX_I2C_STAT_TFF))) {
|
||||
wlen--;
|
||||
/* write trigger byte + stop if last */
|
||||
writel(wlen ? 0 :
|
||||
LPC32XX_I2C_TX_STOP, &i2c->tx);
|
||||
}
|
||||
/* must we, can we read a data byte? */
|
||||
if ((length > 0)
|
||||
& (!(stat & LPC32XX_I2C_STAT_RFE))) {
|
||||
length--;
|
||||
/* read byte */
|
||||
*(data++) = readl(&i2c->rx);
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
}
|
||||
/* success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), send data bytes, end.
|
||||
*/
|
||||
static int lpc32xx_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
struct lpc32xx_i2c_registers *i2c = lpc32xx_i2c[adap->hwadapnr];
|
||||
int stat;
|
||||
|
||||
/* Soft-reset the controller */
|
||||
writel(LPC32XX_I2C_SOFT_RESET, &i2c->ctrl);
|
||||
while (readl(&i2c->ctrl) & LPC32XX_I2C_SOFT_RESET)
|
||||
;
|
||||
/* do we need to write anything at all? */
|
||||
if (alen | length)
|
||||
/* Address slave in write mode */
|
||||
writel((dev<<1) | LPC32XX_I2C_TX_START, &i2c->tx);
|
||||
else
|
||||
return 0;
|
||||
/* write address bytes */
|
||||
while (alen) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&i2c->stat);
|
||||
if (!(stat & LPC32XX_I2C_STAT_TFF)) {
|
||||
alen--;
|
||||
int a = (addr >> (8 * alen)) & 0xff;
|
||||
if (!(alen | length))
|
||||
a |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send address byte */
|
||||
writel(a, &i2c->tx);
|
||||
}
|
||||
}
|
||||
while (length) {
|
||||
/* wait for transmit fifo not full */
|
||||
stat = readl(&i2c->stat);
|
||||
if (!(stat & LPC32XX_I2C_STAT_TFF)) {
|
||||
/* compute data byte, add stop if length==0 */
|
||||
length--;
|
||||
int d = *(data++);
|
||||
if (!length)
|
||||
d |= LPC32XX_I2C_TX_STOP;
|
||||
/* Send data byte */
|
||||
writel(d, &i2c->tx);
|
||||
}
|
||||
}
|
||||
/* wait for end of transation */
|
||||
while (!((stat = readl(&i2c->stat)) & LPC32XX_I2C_STAT_TDI))
|
||||
;
|
||||
/* clear end-of-transaction flag */
|
||||
writel(1, &i2c->stat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_0, _i2c_init, lpc32xx_i2c_probe,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_LPC32XX_SPEED,
|
||||
CONFIG_SYS_I2C_LPC32XX_SLAVE,
|
||||
0)
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_1, _i2c_init, lpc32xx_i2c_probe,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_LPC32XX_SPEED,
|
||||
CONFIG_SYS_I2C_LPC32XX_SLAVE,
|
||||
1)
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(lpc32xx_2, _i2c_init, NULL,
|
||||
lpc32xx_i2c_read, lpc32xx_i2c_write,
|
||||
lpc32xx_i2c_set_bus_speed,
|
||||
100000,
|
||||
0,
|
||||
2)
|
||||
36
u-boot/drivers/i2c/muxes/Kconfig
Normal file
36
u-boot/drivers/i2c/muxes/Kconfig
Normal file
@@ -0,0 +1,36 @@
|
||||
config I2C_MUX
|
||||
bool "Support I2C multiplexers"
|
||||
depends on DM_I2C
|
||||
help
|
||||
This enables I2C buses to be multiplexed, so that you can select
|
||||
one of several buses using some sort of control mechanism. The
|
||||
bus select is handled automatically when that bus is accessed,
|
||||
using a suitable I2C MUX driver.
|
||||
|
||||
config SPL_I2C_MUX
|
||||
bool "Support I2C multiplexers on SPL"
|
||||
depends on I2C_MUX
|
||||
help
|
||||
This enables I2C buses to be multiplexed, so that you can select
|
||||
one of several buses using some sort of control mechanism. The
|
||||
bus select is handled automatically when that bus is accessed,
|
||||
using a suitable I2C MUX driver.
|
||||
|
||||
config I2C_ARB_GPIO_CHALLENGE
|
||||
bool "GPIO-based I2C arbitration"
|
||||
depends on I2C_MUX
|
||||
help
|
||||
If you say yes to this option, support will be included for an
|
||||
I2C multimaster arbitration scheme using GPIOs and a challenge &
|
||||
response mechanism where masters have to claim the bus by asserting
|
||||
a GPIO.
|
||||
|
||||
config I2C_MUX_PCA954x
|
||||
tristate "TI PCA954x I2C Mux/switches"
|
||||
depends on I2C_MUX
|
||||
help
|
||||
If you say yes here you get support for the TI PCA954x
|
||||
I2C mux/switch devices. It is x width I2C multiplexer which enables to
|
||||
paritioning I2C bus and connect multiple devices with the same address
|
||||
to the same I2C controller where driver handles proper routing to
|
||||
target i2c device. PCA9544 and PCA9548 are supported.
|
||||
8
u-boot/drivers/i2c/muxes/Makefile
Normal file
8
u-boot/drivers/i2c/muxes/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
|
||||
obj-$(CONFIG_$(SPL_)I2C_MUX) += i2c-mux-uclass.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
|
||||
147
u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
Normal file
147
u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct i2c_arbitrator_priv {
|
||||
struct gpio_desc ap_claim;
|
||||
struct gpio_desc ec_claim;
|
||||
uint slew_delay_us;
|
||||
uint wait_retry_ms;
|
||||
uint wait_free_ms;
|
||||
};
|
||||
|
||||
int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
|
||||
int ret;
|
||||
|
||||
debug("%s: %s\n", __func__, mux->name);
|
||||
ret = dm_gpio_set_value(&priv->ap_claim, 0);
|
||||
udelay(priv->slew_delay_us);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
|
||||
unsigned start;
|
||||
int ret;
|
||||
|
||||
debug("%s: %s\n", __func__, mux->name);
|
||||
/* Start a round of trying to claim the bus */
|
||||
start = get_timer(0);
|
||||
do {
|
||||
unsigned start_retry;
|
||||
int waiting = 0;
|
||||
|
||||
/* Indicate that we want to claim the bus */
|
||||
ret = dm_gpio_set_value(&priv->ap_claim, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
udelay(priv->slew_delay_us);
|
||||
|
||||
/* Wait for the EC to release it */
|
||||
start_retry = get_timer(0);
|
||||
while (get_timer(start_retry) < priv->wait_retry_ms) {
|
||||
ret = dm_gpio_get_value(&priv->ec_claim);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
} else if (!ret) {
|
||||
/* We got it, so return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!waiting)
|
||||
waiting = 1;
|
||||
}
|
||||
|
||||
/* It didn't release, so give up, wait, and try again */
|
||||
ret = dm_gpio_set_value(&priv->ap_claim, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
mdelay(priv->wait_retry_ms);
|
||||
} while (get_timer(start) < priv->wait_free_ms);
|
||||
|
||||
/* Give up, release our claim */
|
||||
printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start));
|
||||
ret = -ETIMEDOUT;
|
||||
ret = 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_arbitrator_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev->of_offset;
|
||||
int ret;
|
||||
|
||||
debug("%s: %s\n", __func__, dev->name);
|
||||
priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0);
|
||||
priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) /
|
||||
1000;
|
||||
priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) /
|
||||
1000;
|
||||
ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim,
|
||||
GPIOD_IS_OUT);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim,
|
||||
GPIOD_IS_IN);
|
||||
if (ret)
|
||||
goto err_ec_gpio;
|
||||
|
||||
return 0;
|
||||
|
||||
err_ec_gpio:
|
||||
dm_gpio_free(dev, &priv->ap_claim);
|
||||
err:
|
||||
debug("%s: ret=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_arbitrator_remove(struct udevice *dev)
|
||||
{
|
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
|
||||
|
||||
dm_gpio_free(dev, &priv->ap_claim);
|
||||
dm_gpio_free(dev, &priv->ec_claim);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_mux_ops i2c_arbitrator_ops = {
|
||||
.select = i2c_arbitrator_select,
|
||||
.deselect = i2c_arbitrator_deselect,
|
||||
};
|
||||
|
||||
static const struct udevice_id i2c_arbitrator_ids[] = {
|
||||
{ .compatible = "i2c-arb-gpio-challenge" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_arbitrator) = {
|
||||
.name = "i2c_arbitrator",
|
||||
.id = UCLASS_I2C_MUX,
|
||||
.of_match = i2c_arbitrator_ids,
|
||||
.probe = i2c_arbitrator_probe,
|
||||
.remove = i2c_arbitrator_remove,
|
||||
.ops = &i2c_arbitrator_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv),
|
||||
};
|
||||
198
u-boot/drivers/i2c/muxes/i2c-mux-uclass.c
Normal file
198
u-boot/drivers/i2c/muxes/i2c-mux-uclass.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/**
|
||||
* struct i2c_mux: Information the uclass stores about an I2C mux
|
||||
*
|
||||
* @selected: Currently selected mux, or -1 for none
|
||||
* @i2c_bus: I2C bus to use for communcation
|
||||
*/
|
||||
struct i2c_mux {
|
||||
int selected;
|
||||
struct udevice *i2c_bus;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_mux_bus: Information about each bus the mux controls
|
||||
*
|
||||
* @channel: Channel number used to select this bus
|
||||
*/
|
||||
struct i2c_mux_bus {
|
||||
uint channel;
|
||||
};
|
||||
|
||||
/* Find out the mux channel number */
|
||||
static int i2c_mux_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
||||
int channel;
|
||||
|
||||
channel = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
|
||||
if (channel < 0)
|
||||
return -EINVAL;
|
||||
plat->channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the I2C buses selected by this mux */
|
||||
static int i2c_mux_post_bind(struct udevice *mux)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
int ret;
|
||||
int offset;
|
||||
|
||||
debug("%s: %s\n", __func__, mux->name);
|
||||
/*
|
||||
* There is no compatible string in the sub-nodes, so we must manually
|
||||
* bind these
|
||||
*/
|
||||
for (offset = fdt_first_subnode(blob, mux->of_offset);
|
||||
offset > 0;
|
||||
offset = fdt_next_subnode(blob, offset)) {
|
||||
struct udevice *dev;
|
||||
const char *name;
|
||||
|
||||
name = fdt_get_name(blob, offset, NULL);
|
||||
ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name,
|
||||
offset, &dev);
|
||||
debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set up the mux ready for use */
|
||||
static int i2c_mux_post_probe(struct udevice *mux)
|
||||
{
|
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
||||
int ret;
|
||||
|
||||
debug("%s: %s\n", __func__, mux->name);
|
||||
priv->selected = -1;
|
||||
|
||||
ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent",
|
||||
&priv->i2c_bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_mux_select(struct udevice *dev)
|
||||
{
|
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
||||
struct udevice *mux = dev->parent;
|
||||
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
|
||||
|
||||
if (!ops->select)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->select(mux, dev, plat->channel);
|
||||
}
|
||||
|
||||
int i2c_mux_deselect(struct udevice *dev)
|
||||
{
|
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
|
||||
struct udevice *mux = dev->parent;
|
||||
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
|
||||
|
||||
if (!ops->deselect)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->deselect(mux, dev, plat->channel);
|
||||
}
|
||||
|
||||
static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct udevice *mux = dev->parent;
|
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
||||
int ret, ret2;
|
||||
|
||||
ret = i2c_mux_select(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed);
|
||||
ret2 = i2c_mux_deselect(dev);
|
||||
|
||||
return ret ? ret : ret2;
|
||||
}
|
||||
|
||||
static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct udevice *mux = dev->parent;
|
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
|
||||
int ret, ret2;
|
||||
|
||||
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
|
||||
if (!ops->probe_chip)
|
||||
return -ENOSYS;
|
||||
ret = i2c_mux_select(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags);
|
||||
ret2 = i2c_mux_deselect(dev);
|
||||
|
||||
return ret ? ret : ret2;
|
||||
}
|
||||
|
||||
static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct udevice *mux = dev->parent;
|
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux);
|
||||
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
|
||||
int ret, ret2;
|
||||
|
||||
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
|
||||
if (!ops->xfer)
|
||||
return -ENOSYS;
|
||||
ret = i2c_mux_select(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ops->xfer(priv->i2c_bus, msg, nmsgs);
|
||||
ret2 = i2c_mux_deselect(dev);
|
||||
|
||||
return ret ? ret : ret2;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops i2c_mux_bus_ops = {
|
||||
.xfer = i2c_mux_bus_xfer,
|
||||
.probe_chip = i2c_mux_bus_probe,
|
||||
.set_bus_speed = i2c_mux_bus_set_bus_speed,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_mux_bus) = {
|
||||
.name = "i2c_mux_bus_drv",
|
||||
.id = UCLASS_I2C,
|
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
|
||||
.ops = &i2c_mux_bus_ops,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(i2c_mux) = {
|
||||
.id = UCLASS_I2C_MUX,
|
||||
.name = "i2c_mux",
|
||||
.post_bind = i2c_mux_post_bind,
|
||||
.post_probe = i2c_mux_post_probe,
|
||||
.per_device_auto_alloc_size = sizeof(struct i2c_mux),
|
||||
.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
|
||||
.child_post_bind = i2c_mux_child_post_bind,
|
||||
};
|
||||
79
u-boot/drivers/i2c/muxes/pca954x.c
Normal file
79
u-boot/drivers/i2c/muxes/pca954x.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2015 - 2016 Xilinx, Inc.
|
||||
* Written by Michal Simek
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct pca954x_priv {
|
||||
u32 addr; /* I2C mux address */
|
||||
u32 width; /* I2C mux width - number of busses */
|
||||
};
|
||||
|
||||
static int pca954x_deselect(struct udevice *mux, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct pca954x_priv *priv = dev_get_priv(mux);
|
||||
uchar byte = 0;
|
||||
|
||||
return dm_i2c_write(mux, priv->addr, &byte, 1);
|
||||
}
|
||||
|
||||
static int pca954x_select(struct udevice *mux, struct udevice *bus,
|
||||
uint channel)
|
||||
{
|
||||
struct pca954x_priv *priv = dev_get_priv(mux);
|
||||
uchar byte = 1 << channel;
|
||||
|
||||
return dm_i2c_write(mux, priv->addr, &byte, 1);
|
||||
}
|
||||
|
||||
static const struct i2c_mux_ops pca954x_ops = {
|
||||
.select = pca954x_select,
|
||||
.deselect = pca954x_deselect,
|
||||
};
|
||||
|
||||
static const struct udevice_id pca954x_ids[] = {
|
||||
{ .compatible = "nxp,pca9548", .data = (ulong)8 },
|
||||
{ .compatible = "nxp,pca9544", .data = (ulong)4 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int pca954x_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct pca954x_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", 0);
|
||||
if (!priv->addr) {
|
||||
debug("MUX not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
priv->width = dev_get_driver_data(dev);
|
||||
|
||||
if (!priv->width) {
|
||||
debug("No I2C MUX width specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("Device %s at 0x%x with width %d\n",
|
||||
dev->name, priv->addr, priv->width);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(pca954x) = {
|
||||
.name = "pca954x",
|
||||
.id = UCLASS_I2C_MUX,
|
||||
.of_match = pca954x_ids,
|
||||
.ops = &pca954x_ops,
|
||||
.ofdata_to_platdata = pca954x_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct pca954x_priv),
|
||||
};
|
||||
471
u-boot/drivers/i2c/mv_i2c.c
Normal file
471
u-boot/drivers/i2c/mv_i2c.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* (C) Copyright 2000
|
||||
* Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
|
||||
*
|
||||
* (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*
|
||||
* (C) Copyright 2003 Pengutronix e.K.
|
||||
* Robert Schwebel <r.schwebel@pengutronix.de>
|
||||
*
|
||||
* (C) Copyright 2011 Marvell Inc.
|
||||
* Lei Wen <leiwen@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Back ported to the 8xx platform (from the 8260 platform) by
|
||||
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_HARD_I2C
|
||||
#include <i2c.h>
|
||||
#include "mv_i2c.h"
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define PRINTD(x) printf x
|
||||
#else
|
||||
#define PRINTD(x)
|
||||
#endif
|
||||
|
||||
/* All transfers are described by this data structure */
|
||||
struct mv_i2c_msg {
|
||||
u8 condition;
|
||||
u8 acknack;
|
||||
u8 direction;
|
||||
u8 data;
|
||||
};
|
||||
|
||||
struct mv_i2c {
|
||||
u32 ibmr;
|
||||
u32 pad0;
|
||||
u32 idbr;
|
||||
u32 pad1;
|
||||
u32 icr;
|
||||
u32 pad2;
|
||||
u32 isr;
|
||||
u32 pad3;
|
||||
u32 isar;
|
||||
};
|
||||
|
||||
static struct mv_i2c *base;
|
||||
static void i2c_board_init(struct mv_i2c *base)
|
||||
{
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
u32 icr;
|
||||
/*
|
||||
* call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*
|
||||
* disable I2C controller first, otherwhise it thinks we want to
|
||||
* talk to the slave port...
|
||||
*/
|
||||
icr = readl(&base->icr);
|
||||
writel(readl(&base->icr) & ~(ICR_SCLE | ICR_IUE), &base->icr);
|
||||
|
||||
i2c_init_board();
|
||||
|
||||
writel(icr, &base->icr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
static unsigned long i2c_regs[CONFIG_MV_I2C_NUM] = CONFIG_MV_I2C_REG;
|
||||
static unsigned int bus_initialized[CONFIG_MV_I2C_NUM];
|
||||
static unsigned int current_bus;
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if ((bus < 0) || (bus >= CONFIG_MV_I2C_NUM)) {
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
base = (struct mv_i2c *)i2c_regs[bus];
|
||||
current_bus = bus;
|
||||
|
||||
if (!bus_initialized[current_bus]) {
|
||||
i2c_board_init(base);
|
||||
bus_initialized[current_bus] = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* i2c_reset: - reset the host controller
|
||||
*
|
||||
*/
|
||||
static void i2c_reset(void)
|
||||
{
|
||||
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
|
||||
writel(readl(&base->icr) | ICR_UR, &base->icr); /* reset the unit */
|
||||
udelay(100);
|
||||
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
|
||||
|
||||
i2c_clk_enable();
|
||||
|
||||
writel(CONFIG_SYS_I2C_SLAVE, &base->isar); /* set our slave address */
|
||||
writel(I2C_ICR_INIT, &base->icr); /* set control reg values */
|
||||
writel(I2C_ISR_INIT, &base->isr); /* set clear interrupt bits */
|
||||
writel(readl(&base->icr) | ICR_IUE, &base->icr); /* enable unit */
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_isr_set_cleared: - wait until certain bits of the I2C status register
|
||||
* are set and cleared
|
||||
*
|
||||
* @return: 1 in case of success, 0 means timeout (no match within 10 ms).
|
||||
*/
|
||||
static int i2c_isr_set_cleared(unsigned long set_mask,
|
||||
unsigned long cleared_mask)
|
||||
{
|
||||
int timeout = 1000, isr;
|
||||
|
||||
do {
|
||||
isr = readl(&base->isr);
|
||||
udelay(10);
|
||||
if (timeout-- < 0)
|
||||
return 0;
|
||||
} while (((isr & set_mask) != set_mask)
|
||||
|| ((isr & cleared_mask) != 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_transfer: - Transfer one byte over the i2c bus
|
||||
*
|
||||
* This function can tranfer a byte over the i2c bus in both directions.
|
||||
* It is used by the public API functions.
|
||||
*
|
||||
* @return: 0: transfer successful
|
||||
* -1: message is empty
|
||||
* -2: transmit timeout
|
||||
* -3: ACK missing
|
||||
* -4: receive timeout
|
||||
* -5: illegal parameters
|
||||
* -6: bus is busy and couldn't be aquired
|
||||
*/
|
||||
int i2c_transfer(struct mv_i2c_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
goto transfer_error_msg_empty;
|
||||
|
||||
switch (msg->direction) {
|
||||
case I2C_WRITE:
|
||||
/* check if bus is not busy */
|
||||
if (!i2c_isr_set_cleared(0, ISR_IBB))
|
||||
goto transfer_error_bus_busy;
|
||||
|
||||
/* start transmission */
|
||||
writel(readl(&base->icr) & ~ICR_START, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
|
||||
writel(msg->data, &base->idbr);
|
||||
if (msg->condition == I2C_COND_START)
|
||||
writel(readl(&base->icr) | ICR_START, &base->icr);
|
||||
if (msg->condition == I2C_COND_STOP)
|
||||
writel(readl(&base->icr) | ICR_STOP, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDNAK)
|
||||
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDACK)
|
||||
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
|
||||
writel(readl(&base->icr) | ICR_TB, &base->icr);
|
||||
|
||||
/* transmit register empty? */
|
||||
if (!i2c_isr_set_cleared(ISR_ITE, 0))
|
||||
goto transfer_error_transmit_timeout;
|
||||
|
||||
/* clear 'transmit empty' state */
|
||||
writel(readl(&base->isr) | ISR_ITE, &base->isr);
|
||||
|
||||
/* wait for ACK from slave */
|
||||
if (msg->acknack == I2C_ACKNAK_WAITACK)
|
||||
if (!i2c_isr_set_cleared(0, ISR_ACKNAK))
|
||||
goto transfer_error_ack_missing;
|
||||
break;
|
||||
|
||||
case I2C_READ:
|
||||
|
||||
/* check if bus is not busy */
|
||||
if (!i2c_isr_set_cleared(0, ISR_IBB))
|
||||
goto transfer_error_bus_busy;
|
||||
|
||||
/* start receive */
|
||||
writel(readl(&base->icr) & ~ICR_START, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
|
||||
if (msg->condition == I2C_COND_START)
|
||||
writel(readl(&base->icr) | ICR_START, &base->icr);
|
||||
if (msg->condition == I2C_COND_STOP)
|
||||
writel(readl(&base->icr) | ICR_STOP, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDNAK)
|
||||
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDACK)
|
||||
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
|
||||
writel(readl(&base->icr) | ICR_TB, &base->icr);
|
||||
|
||||
/* receive register full? */
|
||||
if (!i2c_isr_set_cleared(ISR_IRF, 0))
|
||||
goto transfer_error_receive_timeout;
|
||||
|
||||
msg->data = readl(&base->idbr);
|
||||
|
||||
/* clear 'receive empty' state */
|
||||
writel(readl(&base->isr) | ISR_IRF, &base->isr);
|
||||
break;
|
||||
default:
|
||||
goto transfer_error_illegal_param;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
transfer_error_msg_empty:
|
||||
PRINTD(("i2c_transfer: error: 'msg' is empty\n"));
|
||||
ret = -1; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_transmit_timeout:
|
||||
PRINTD(("i2c_transfer: error: transmit timeout\n"));
|
||||
ret = -2; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_ack_missing:
|
||||
PRINTD(("i2c_transfer: error: ACK missing\n"));
|
||||
ret = -3; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_receive_timeout:
|
||||
PRINTD(("i2c_transfer: error: receive timeout\n"));
|
||||
ret = -4; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_illegal_param:
|
||||
PRINTD(("i2c_transfer: error: illegal parameters\n"));
|
||||
ret = -5; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_bus_busy:
|
||||
PRINTD(("i2c_transfer: error: bus is busy\n"));
|
||||
ret = -6; goto i2c_transfer_finish;
|
||||
|
||||
i2c_transfer_finish:
|
||||
PRINTD(("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr)));
|
||||
i2c_reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* API Functions */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
base = (struct mv_i2c *)i2c_regs[current_bus];
|
||||
#else
|
||||
base = (struct mv_i2c *)CONFIG_MV_I2C_REG;
|
||||
#endif
|
||||
|
||||
i2c_board_init(base);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: - Test if a chip answers for a given i2c address
|
||||
*
|
||||
* @chip: address of the chip which is searched for
|
||||
* @return: 0 if a chip was found, -1 otherwhise
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
|
||||
i2c_reset();
|
||||
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1) + 1;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
msg.condition = I2C_COND_STOP;
|
||||
msg.acknack = I2C_ACKNAK_SENDNAK;
|
||||
msg.direction = I2C_READ;
|
||||
msg.data = 0x00;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: - Read multiple bytes from an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be read
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to write the data
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
u8 addr_bytes[3]; /* lowest...highest byte of data address */
|
||||
|
||||
PRINTD(("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, addr, alen, len));
|
||||
|
||||
i2c_reset();
|
||||
|
||||
/* dummy chip address write */
|
||||
PRINTD(("i2c_read: dummy chip address write\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data &= 0xFE;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
/*addr &= ((1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)-1); */
|
||||
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
|
||||
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
|
||||
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
|
||||
|
||||
while (--alen >= 0) {
|
||||
PRINTD(("i2c_read: send memory word address byte %1d\n", alen));
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr_bytes[alen];
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start read sequence */
|
||||
PRINTD(("i2c_read: start read sequence\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data |= 0x01;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/* read bytes; send NACK at last byte */
|
||||
while (len--) {
|
||||
if (len == 0) {
|
||||
msg.condition = I2C_COND_STOP;
|
||||
msg.acknack = I2C_ACKNAK_SENDNAK;
|
||||
} else {
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_SENDACK;
|
||||
}
|
||||
|
||||
msg.direction = I2C_READ;
|
||||
msg.data = 0x00;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
*buffer = msg.data;
|
||||
PRINTD(("i2c_read: reading byte (0x%08x)=0x%02x\n",
|
||||
(unsigned int)buffer, *buffer));
|
||||
buffer++;
|
||||
}
|
||||
|
||||
i2c_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write: - Write multiple bytes to an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be written
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to find the data to be written
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct mv_i2c_msg msg;
|
||||
u8 addr_bytes[3]; /* lowest...highest byte of data address */
|
||||
|
||||
PRINTD(("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, addr, alen, len));
|
||||
|
||||
i2c_reset();
|
||||
|
||||
/* chip address write */
|
||||
PRINTD(("i2c_write: chip address write\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data &= 0xFE;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
|
||||
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
|
||||
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
|
||||
|
||||
while (--alen >= 0) {
|
||||
PRINTD(("i2c_write: send memory word address\n"));
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr_bytes[alen];
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* write bytes; send NACK at last byte */
|
||||
while (len--) {
|
||||
PRINTD(("i2c_write: writing byte (0x%08x)=0x%02x\n",
|
||||
(unsigned int)buffer, *buffer));
|
||||
|
||||
if (len == 0)
|
||||
msg.condition = I2C_COND_STOP;
|
||||
else
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = *(buffer++);
|
||||
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HARD_I2C */
|
||||
67
u-boot/drivers/i2c/mv_i2c.h
Normal file
67
u-boot/drivers/i2c/mv_i2c.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* (C) Copyright 2011
|
||||
* Marvell Inc, <www.marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _MV_I2C_H_
|
||||
#define _MV_I2C_H_
|
||||
extern void i2c_clk_enable(void);
|
||||
|
||||
/* Shall the current transfer have a start/stop condition? */
|
||||
#define I2C_COND_NORMAL 0
|
||||
#define I2C_COND_START 1
|
||||
#define I2C_COND_STOP 2
|
||||
|
||||
/* Shall the current transfer be ack/nacked or being waited for it? */
|
||||
#define I2C_ACKNAK_WAITACK 1
|
||||
#define I2C_ACKNAK_SENDACK 2
|
||||
#define I2C_ACKNAK_SENDNAK 4
|
||||
|
||||
/* Specify who shall transfer the data (master or slave) */
|
||||
#define I2C_READ 0
|
||||
#define I2C_WRITE 1
|
||||
|
||||
#if (CONFIG_SYS_I2C_SPEED == 400000)
|
||||
#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD \
|
||||
| ICR_SCLE)
|
||||
#else
|
||||
#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
|
||||
#endif
|
||||
|
||||
#define I2C_ISR_INIT 0x7FF
|
||||
/* ----- Control register bits ---------------------------------------- */
|
||||
|
||||
#define ICR_START 0x1 /* start bit */
|
||||
#define ICR_STOP 0x2 /* stop bit */
|
||||
#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */
|
||||
#define ICR_TB 0x8 /* transfer byte bit */
|
||||
#define ICR_MA 0x10 /* master abort */
|
||||
#define ICR_SCLE 0x20 /* master clock enable, mona SCLEA */
|
||||
#define ICR_IUE 0x40 /* unit enable */
|
||||
#define ICR_GCD 0x80 /* general call disable */
|
||||
#define ICR_ITEIE 0x100 /* enable tx interrupts */
|
||||
#define ICR_IRFIE 0x200 /* enable rx interrupts, mona: DRFIE */
|
||||
#define ICR_BEIE 0x400 /* enable bus error ints */
|
||||
#define ICR_SSDIE 0x800 /* slave STOP detected int enable */
|
||||
#define ICR_ALDIE 0x1000 /* enable arbitration interrupt */
|
||||
#define ICR_SADIE 0x2000 /* slave address detected int enable */
|
||||
#define ICR_UR 0x4000 /* unit reset */
|
||||
#define ICR_FM 0x8000 /* Fast Mode */
|
||||
|
||||
/* ----- Status register bits ----------------------------------------- */
|
||||
|
||||
#define ISR_RWM 0x1 /* read/write mode */
|
||||
#define ISR_ACKNAK 0x2 /* ack/nak status */
|
||||
#define ISR_UB 0x4 /* unit busy */
|
||||
#define ISR_IBB 0x8 /* bus busy */
|
||||
#define ISR_SSD 0x10 /* slave stop detected */
|
||||
#define ISR_ALD 0x20 /* arbitration loss detected */
|
||||
#define ISR_ITE 0x40 /* tx buffer empty */
|
||||
#define ISR_IRF 0x80 /* rx buffer full */
|
||||
#define ISR_GCAD 0x100 /* general call address detected */
|
||||
#define ISR_SAD 0x200 /* slave address detected */
|
||||
#define ISR_BED 0x400 /* bus error no ACK/NAK */
|
||||
|
||||
#endif
|
||||
496
u-boot/drivers/i2c/mvtwsi.c
Normal file
496
u-boot/drivers/i2c/mvtwsi.c
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Driver for the TWSI (i2c) controller found on the Marvell
|
||||
* orion5x and kirkwood SoC families.
|
||||
*
|
||||
* Author: Albert Aribaud <albert.u.boot@aribaud.net>
|
||||
* Copyright (c) 2010 Albert Aribaud.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* include a file that will provide CONFIG_I2C_MVTWSI_BASE*
|
||||
* and possibly other settings
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ORION5X)
|
||||
#include <asm/arch/orion5x.h>
|
||||
#elif (defined(CONFIG_KIRKWOOD) || defined(CONFIG_ARCH_MVEBU))
|
||||
#include <asm/arch/soc.h>
|
||||
#elif defined(CONFIG_SUNXI)
|
||||
#include <asm/arch/i2c.h>
|
||||
#else
|
||||
#error Driver mvtwsi not supported by SoC or board
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TWSI register structure
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SUNXI
|
||||
|
||||
struct mvtwsi_registers {
|
||||
u32 slave_address;
|
||||
u32 xtnd_slave_addr;
|
||||
u32 data;
|
||||
u32 control;
|
||||
u32 status;
|
||||
u32 baudrate;
|
||||
u32 soft_reset;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct mvtwsi_registers {
|
||||
u32 slave_address;
|
||||
u32 data;
|
||||
u32 control;
|
||||
union {
|
||||
u32 status; /* when reading */
|
||||
u32 baudrate; /* when writing */
|
||||
};
|
||||
u32 xtnd_slave_addr;
|
||||
u32 reserved[2];
|
||||
u32 soft_reset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Control register fields
|
||||
*/
|
||||
|
||||
#define MVTWSI_CONTROL_ACK 0x00000004
|
||||
#define MVTWSI_CONTROL_IFLG 0x00000008
|
||||
#define MVTWSI_CONTROL_STOP 0x00000010
|
||||
#define MVTWSI_CONTROL_START 0x00000020
|
||||
#define MVTWSI_CONTROL_TWSIEN 0x00000040
|
||||
#define MVTWSI_CONTROL_INTEN 0x00000080
|
||||
|
||||
/*
|
||||
* On sun6i and newer IFLG is a write-clear bit which is cleared by writing 1,
|
||||
* on other platforms it is a normal r/w bit which is cleared by writing 0.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
||||
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008
|
||||
#else
|
||||
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Status register values -- only those expected in normal master
|
||||
* operation on non-10-bit-address devices; whatever status we don't
|
||||
* expect in nominal conditions (bus errors, arbitration losses,
|
||||
* missing ACKs...) we just pass back to the caller as an error
|
||||
* code.
|
||||
*/
|
||||
|
||||
#define MVTWSI_STATUS_START 0x08
|
||||
#define MVTWSI_STATUS_REPEATED_START 0x10
|
||||
#define MVTWSI_STATUS_ADDR_W_ACK 0x18
|
||||
#define MVTWSI_STATUS_DATA_W_ACK 0x28
|
||||
#define MVTWSI_STATUS_ADDR_R_ACK 0x40
|
||||
#define MVTWSI_STATUS_ADDR_R_NAK 0x48
|
||||
#define MVTWSI_STATUS_DATA_R_ACK 0x50
|
||||
#define MVTWSI_STATUS_DATA_R_NAK 0x58
|
||||
#define MVTWSI_STATUS_IDLE 0xF8
|
||||
|
||||
/*
|
||||
* MVTWSI controller base
|
||||
*/
|
||||
|
||||
static struct mvtwsi_registers *twsi_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE0
|
||||
case 0:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE0;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE1
|
||||
case 1:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE1;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE2
|
||||
case 2:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE2;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE3
|
||||
case 3:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE3;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE4
|
||||
case 4:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE4;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE5
|
||||
case 5:
|
||||
return (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE5;
|
||||
#endif
|
||||
default:
|
||||
printf("Missing mvtwsi controller %d base\n", adap->hwadapnr);
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returned statuses are 0 for success and nonzero otherwise.
|
||||
* Currently, cmd_i2c and cmd_eeprom do not interpret an error status.
|
||||
* Thus to ease debugging, the return status contains some debug info:
|
||||
* - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'.
|
||||
* - bits 23..16 are the last value of the control register.
|
||||
* - bits 15..8 are the last value of the status register.
|
||||
* - bits 7..0 are the expected value of the status register.
|
||||
*/
|
||||
|
||||
#define MVTWSI_ERROR_WRONG_STATUS 0x01
|
||||
#define MVTWSI_ERROR_TIMEOUT 0x02
|
||||
|
||||
#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \
|
||||
((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF))
|
||||
|
||||
/*
|
||||
* Wait for IFLG to raise, or return 'timeout'; then if status is as expected,
|
||||
* return 0 (ok) or return 'wrong status'.
|
||||
*/
|
||||
static int twsi_wait(struct i2c_adapter *adap, int expected_status)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
int control, status;
|
||||
int timeout = 1000;
|
||||
|
||||
do {
|
||||
control = readl(&twsi->control);
|
||||
if (control & MVTWSI_CONTROL_IFLG) {
|
||||
status = readl(&twsi->status);
|
||||
if (status == expected_status)
|
||||
return 0;
|
||||
else
|
||||
return MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_WRONG_STATUS,
|
||||
control, status, expected_status);
|
||||
}
|
||||
udelay(10); /* one clock cycle at 100 kHz */
|
||||
} while (timeout--);
|
||||
status = readl(&twsi->status);
|
||||
return MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_TIMEOUT, control, status, expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert the START condition, either in a single I2C transaction
|
||||
* or inside back-to-back ones (repeated starts).
|
||||
*/
|
||||
static int twsi_start(struct i2c_adapter *adap, int expected_status, u8 *flags)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
|
||||
/* globally set TWSIEN in case it was not */
|
||||
*flags |= MVTWSI_CONTROL_TWSIEN;
|
||||
/* assert START */
|
||||
writel(*flags | MVTWSI_CONTROL_START |
|
||||
MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* wait for controller to process START */
|
||||
return twsi_wait(adap, expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a byte (i2c address or data).
|
||||
*/
|
||||
static int twsi_send(struct i2c_adapter *adap, u8 byte, int expected_status,
|
||||
u8 *flags)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
|
||||
/* put byte in data register for sending */
|
||||
writel(byte, &twsi->data);
|
||||
/* clear any pending interrupt -- that'll cause sending */
|
||||
writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* wait for controller to receive byte and check ACK */
|
||||
return twsi_wait(adap, expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a byte.
|
||||
* Global mvtwsi_control_flags variable says if we should ack or nak.
|
||||
*/
|
||||
static int twsi_recv(struct i2c_adapter *adap, u8 *byte, u8 *flags)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
int expected_status, status;
|
||||
|
||||
/* compute expected status based on ACK bit in global control flags */
|
||||
if (*flags & MVTWSI_CONTROL_ACK)
|
||||
expected_status = MVTWSI_STATUS_DATA_R_ACK;
|
||||
else
|
||||
expected_status = MVTWSI_STATUS_DATA_R_NAK;
|
||||
/* acknowledge *previous state* and launch receive */
|
||||
writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* wait for controller to receive byte and assert ACK or NAK */
|
||||
status = twsi_wait(adap, expected_status);
|
||||
/* if we did receive expected byte then store it */
|
||||
if (status == 0)
|
||||
*byte = readl(&twsi->data);
|
||||
/* return status */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert the STOP condition.
|
||||
* This is also used to force the bus back in idle (SDA=SCL=1).
|
||||
*/
|
||||
static int twsi_stop(struct i2c_adapter *adap, int status)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
int control, stop_status;
|
||||
int timeout = 1000;
|
||||
|
||||
/* assert STOP */
|
||||
control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP;
|
||||
writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
||||
/* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */
|
||||
do {
|
||||
stop_status = readl(&twsi->status);
|
||||
if (stop_status == MVTWSI_STATUS_IDLE)
|
||||
break;
|
||||
udelay(10); /* one clock cycle at 100 kHz */
|
||||
} while (timeout--);
|
||||
control = readl(&twsi->control);
|
||||
if (stop_status != MVTWSI_STATUS_IDLE)
|
||||
if (status == 0)
|
||||
status = MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_TIMEOUT,
|
||||
control, status, MVTWSI_STATUS_IDLE);
|
||||
return status;
|
||||
}
|
||||
|
||||
static unsigned int twsi_calc_freq(const int n, const int m)
|
||||
{
|
||||
#ifdef CONFIG_SUNXI
|
||||
return CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n));
|
||||
#else
|
||||
return CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset controller.
|
||||
* Controller reset also resets the baud rate and slave address, so
|
||||
* they must be re-established afterwards.
|
||||
*/
|
||||
static void twsi_reset(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
|
||||
/* reset controller */
|
||||
writel(0, &twsi->soft_reset);
|
||||
/* wait 2 ms -- this is what the Marvell LSP does */
|
||||
udelay(20000);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init called by cmd_i2c when doing 'i2c reset'.
|
||||
* Sets baud to the highest possible value not exceeding requested one.
|
||||
*/
|
||||
static unsigned int twsi_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int requested_speed)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
unsigned int tmp_speed, highest_speed, n, m;
|
||||
unsigned int baud = 0x44; /* baudrate at controller reset */
|
||||
|
||||
/* use actual speed to collect progressively higher values */
|
||||
highest_speed = 0;
|
||||
/* compute m, n setting for highest speed not above requested speed */
|
||||
for (n = 0; n < 8; n++) {
|
||||
for (m = 0; m < 16; m++) {
|
||||
tmp_speed = twsi_calc_freq(n, m);
|
||||
if ((tmp_speed <= requested_speed)
|
||||
&& (tmp_speed > highest_speed)) {
|
||||
highest_speed = tmp_speed;
|
||||
baud = (m << 3) | n;
|
||||
}
|
||||
}
|
||||
}
|
||||
writel(baud, &twsi->baudrate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void twsi_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
||||
|
||||
/* reset controller */
|
||||
twsi_reset(adap);
|
||||
/* set speed */
|
||||
twsi_i2c_set_bus_speed(adap, speed);
|
||||
/* set slave address even though we don't use it */
|
||||
writel(slaveadd, &twsi->slave_address);
|
||||
writel(0, &twsi->xtnd_slave_addr);
|
||||
/* assert STOP but don't care for the result */
|
||||
(void) twsi_stop(adap, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin I2C transaction with expected start status, at given address.
|
||||
* Common to i2c_probe, i2c_read and i2c_write.
|
||||
* Expected address status will derive from direction bit (bit 0) in addr.
|
||||
*/
|
||||
static int i2c_begin(struct i2c_adapter *adap, int expected_start_status,
|
||||
u8 addr, u8 *flags)
|
||||
{
|
||||
int status, expected_addr_status;
|
||||
|
||||
/* compute expected address status from direction bit in addr */
|
||||
if (addr & 1) /* reading */
|
||||
expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK;
|
||||
else /* writing */
|
||||
expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK;
|
||||
/* assert START */
|
||||
status = twsi_start(adap, expected_start_status, flags);
|
||||
/* send out the address if the start went well */
|
||||
if (status == 0)
|
||||
status = twsi_send(adap, addr, expected_addr_status,
|
||||
flags);
|
||||
/* return ok or status of first failure to caller */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C probe called by cmd_i2c when doing 'i2c probe'.
|
||||
* Begin read, nak data byte, end.
|
||||
*/
|
||||
static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
u8 dummy_byte;
|
||||
u8 flags = 0;
|
||||
int status;
|
||||
|
||||
/* begin i2c read */
|
||||
status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1) | 1, &flags);
|
||||
/* dummy read was accepted: receive byte but NAK it. */
|
||||
if (status == 0)
|
||||
status = twsi_recv(adap, &dummy_byte, &flags);
|
||||
/* Stop transaction */
|
||||
twsi_stop(adap, 0);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
||||
*
|
||||
* NOTE: some EEPROMS want a stop right before the second start, while
|
||||
* some will choke if it is there. Deciding which we should do is eeprom
|
||||
* stuff, not i2c, but at the moment the APIs won't let us put it in
|
||||
* cmd_eeprom, so we have to choose here, and for the moment that'll be
|
||||
* a repeated start without a preceding stop.
|
||||
*/
|
||||
static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *data, int length)
|
||||
{
|
||||
int status;
|
||||
u8 flags = 0;
|
||||
|
||||
/* begin i2c write to send the address bytes */
|
||||
status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
|
||||
/* send addr bytes */
|
||||
while ((status == 0) && alen--)
|
||||
status = twsi_send(adap, addr >> (8*alen),
|
||||
MVTWSI_STATUS_DATA_W_ACK, &flags);
|
||||
/* begin i2c read to receive eeprom data bytes */
|
||||
if (status == 0)
|
||||
status = i2c_begin(adap, MVTWSI_STATUS_REPEATED_START,
|
||||
(chip << 1) | 1, &flags);
|
||||
/* prepare ACK if at least one byte must be received */
|
||||
if (length > 0)
|
||||
flags |= MVTWSI_CONTROL_ACK;
|
||||
/* now receive actual bytes */
|
||||
while ((status == 0) && length--) {
|
||||
/* reset NAK if we if no more to read now */
|
||||
if (length == 0)
|
||||
flags &= ~MVTWSI_CONTROL_ACK;
|
||||
/* read current byte */
|
||||
status = twsi_recv(adap, data++, &flags);
|
||||
}
|
||||
/* Stop transaction */
|
||||
status = twsi_stop(adap, status);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), send data bytes, end.
|
||||
*/
|
||||
static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *data, int length)
|
||||
{
|
||||
int status;
|
||||
u8 flags = 0;
|
||||
|
||||
/* begin i2c write to send the eeprom adress bytes then data bytes */
|
||||
status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
|
||||
/* send addr bytes */
|
||||
while ((status == 0) && alen--)
|
||||
status = twsi_send(adap, addr >> (8*alen),
|
||||
MVTWSI_STATUS_DATA_W_ACK, &flags);
|
||||
/* send data bytes */
|
||||
while ((status == 0) && (length-- > 0))
|
||||
status = twsi_send(adap, *(data++), MVTWSI_STATUS_DATA_W_ACK,
|
||||
&flags);
|
||||
/* Stop transaction */
|
||||
status = twsi_stop(adap, status);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE0
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi0, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 0)
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi1, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 1)
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi2, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 2)
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi3, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 3)
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE4
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi4, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 4)
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_I2C_MVTWSI_BASE5
|
||||
U_BOOT_I2C_ADAP_COMPLETE(twsi5, twsi_i2c_init, twsi_i2c_probe,
|
||||
twsi_i2c_read, twsi_i2c_write,
|
||||
twsi_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 5)
|
||||
|
||||
#endif
|
||||
903
u-boot/drivers/i2c/mxc_i2c.c
Normal file
903
u-boot/drivers/i2c/mxc_i2c.c
Normal file
@@ -0,0 +1,903 @@
|
||||
/*
|
||||
* i2c driver for Freescale i.MX series
|
||||
*
|
||||
* (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
* (c) 2011 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Based on i2c-imx.c from linux kernel:
|
||||
* Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de>
|
||||
* Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de>
|
||||
* Copyright (C) 2007 RightHand Technologies, Inc.
|
||||
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
|
||||
*
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/imx-common/mxc_i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <watchdog.h>
|
||||
#include <dm.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <fdtdec.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define I2C_QUIRK_FLAG (1 << 0)
|
||||
|
||||
#define IMX_I2C_REGSHIFT 2
|
||||
#define VF610_I2C_REGSHIFT 0
|
||||
|
||||
#define I2C_EARLY_INIT_INDEX 0
|
||||
#ifdef CONFIG_SYS_I2C_IFDR_DIV
|
||||
#define I2C_IFDR_DIV_CONSERVATIVE CONFIG_SYS_I2C_IFDR_DIV
|
||||
#else
|
||||
#define I2C_IFDR_DIV_CONSERVATIVE 0x7e
|
||||
#endif
|
||||
|
||||
/* Register index */
|
||||
#define IADR 0
|
||||
#define IFDR 1
|
||||
#define I2CR 2
|
||||
#define I2SR 3
|
||||
#define I2DR 4
|
||||
|
||||
#define I2CR_IIEN (1 << 6)
|
||||
#define I2CR_MSTA (1 << 5)
|
||||
#define I2CR_MTX (1 << 4)
|
||||
#define I2CR_TX_NO_AK (1 << 3)
|
||||
#define I2CR_RSTA (1 << 2)
|
||||
|
||||
#define I2SR_ICF (1 << 7)
|
||||
#define I2SR_IBB (1 << 5)
|
||||
#define I2SR_IAL (1 << 4)
|
||||
#define I2SR_IIF (1 << 1)
|
||||
#define I2SR_RX_NO_AK (1 << 0)
|
||||
|
||||
#ifdef I2C_QUIRK_REG
|
||||
#define I2CR_IEN (0 << 7)
|
||||
#define I2CR_IDIS (1 << 7)
|
||||
#define I2SR_IIF_CLEAR (1 << 1)
|
||||
#else
|
||||
#define I2CR_IEN (1 << 7)
|
||||
#define I2CR_IDIS (0 << 7)
|
||||
#define I2SR_IIF_CLEAR (0 << 1)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HARD_I2C) && !defined(CONFIG_SYS_I2C_BASE)
|
||||
#error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver"
|
||||
#endif
|
||||
|
||||
#ifdef I2C_QUIRK_REG
|
||||
static u16 i2c_clk_div[60][2] = {
|
||||
{ 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 },
|
||||
{ 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 },
|
||||
{ 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D },
|
||||
{ 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 },
|
||||
{ 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 },
|
||||
{ 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 },
|
||||
{ 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 },
|
||||
{ 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 },
|
||||
{ 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 },
|
||||
{ 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B },
|
||||
{ 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 },
|
||||
{ 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 },
|
||||
{ 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B },
|
||||
{ 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A },
|
||||
{ 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E },
|
||||
};
|
||||
#else
|
||||
static u16 i2c_clk_div[50][2] = {
|
||||
{ 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 },
|
||||
{ 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 },
|
||||
{ 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 },
|
||||
{ 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B },
|
||||
{ 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A },
|
||||
{ 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 },
|
||||
{ 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 },
|
||||
{ 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 },
|
||||
{ 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 },
|
||||
{ 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B },
|
||||
{ 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E },
|
||||
{ 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D },
|
||||
{ 3072, 0x1E }, { 3840, 0x1F }
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_MXC_I2C1_SPEED
|
||||
#define CONFIG_SYS_MXC_I2C1_SPEED 100000
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C2_SPEED
|
||||
#define CONFIG_SYS_MXC_I2C2_SPEED 100000
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C3_SPEED
|
||||
#define CONFIG_SYS_MXC_I2C3_SPEED 100000
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C4_SPEED
|
||||
#define CONFIG_SYS_MXC_I2C4_SPEED 100000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_MXC_I2C1_SLAVE
|
||||
#define CONFIG_SYS_MXC_I2C1_SLAVE 0
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C2_SLAVE
|
||||
#define CONFIG_SYS_MXC_I2C2_SLAVE 0
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C3_SLAVE
|
||||
#define CONFIG_SYS_MXC_I2C3_SLAVE 0
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_MXC_I2C4_SLAVE
|
||||
#define CONFIG_SYS_MXC_I2C4_SLAVE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calculate and set proper clock divider
|
||||
*/
|
||||
static uint8_t i2c_imx_get_clk(struct mxc_i2c_bus *i2c_bus, unsigned int rate)
|
||||
{
|
||||
unsigned int i2c_clk_rate;
|
||||
unsigned int div;
|
||||
u8 clk_div;
|
||||
|
||||
#if defined(CONFIG_MX31)
|
||||
struct clock_control_regs *sc_regs =
|
||||
(struct clock_control_regs *)CCM_BASE;
|
||||
|
||||
/* start the required I2C clock */
|
||||
writel(readl(&sc_regs->cgr0) | (3 << CONFIG_SYS_I2C_CLK_OFFSET),
|
||||
&sc_regs->cgr0);
|
||||
#endif
|
||||
|
||||
/* Divider value calculation */
|
||||
i2c_clk_rate = mxc_get_clock(MXC_I2C_CLK);
|
||||
div = (i2c_clk_rate + rate - 1) / rate;
|
||||
if (div < i2c_clk_div[0][0])
|
||||
clk_div = 0;
|
||||
else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0])
|
||||
clk_div = ARRAY_SIZE(i2c_clk_div) - 1;
|
||||
else
|
||||
for (clk_div = 0; i2c_clk_div[clk_div][0] < div; clk_div++)
|
||||
;
|
||||
|
||||
/* Store divider value */
|
||||
return clk_div;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set I2C Bus speed
|
||||
*/
|
||||
static int bus_i2c_set_bus_speed(struct mxc_i2c_bus *i2c_bus, int speed)
|
||||
{
|
||||
ulong base = i2c_bus->base;
|
||||
bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
|
||||
u8 clk_idx = i2c_imx_get_clk(i2c_bus, speed);
|
||||
u8 idx = i2c_clk_div[clk_idx][1];
|
||||
int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
|
||||
if (!base)
|
||||
return -ENODEV;
|
||||
|
||||
/* Store divider value */
|
||||
writeb(idx, base + (IFDR << reg_shift));
|
||||
|
||||
/* Reset module */
|
||||
writeb(I2CR_IDIS, base + (I2CR << reg_shift));
|
||||
writeb(0, base + (I2SR << reg_shift));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ST_BUS_IDLE (0 | (I2SR_IBB << 8))
|
||||
#define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8))
|
||||
#define ST_IIF (I2SR_IIF | (I2SR_IIF << 8))
|
||||
|
||||
static int wait_for_sr_state(struct mxc_i2c_bus *i2c_bus, unsigned state)
|
||||
{
|
||||
unsigned sr;
|
||||
ulong elapsed;
|
||||
bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
|
||||
int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
ulong base = i2c_bus->base;
|
||||
ulong start_time = get_timer(0);
|
||||
for (;;) {
|
||||
sr = readb(base + (I2SR << reg_shift));
|
||||
if (sr & I2SR_IAL) {
|
||||
if (quirk)
|
||||
writeb(sr | I2SR_IAL, base +
|
||||
(I2SR << reg_shift));
|
||||
else
|
||||
writeb(sr & ~I2SR_IAL, base +
|
||||
(I2SR << reg_shift));
|
||||
printf("%s: Arbitration lost sr=%x cr=%x state=%x\n",
|
||||
__func__, sr, readb(base + (I2CR << reg_shift)),
|
||||
state);
|
||||
return -ERESTART;
|
||||
}
|
||||
if ((sr & (state >> 8)) == (unsigned char)state)
|
||||
return sr;
|
||||
WATCHDOG_RESET();
|
||||
elapsed = get_timer(start_time);
|
||||
if (elapsed > (CONFIG_SYS_HZ / 10)) /* .1 seconds */
|
||||
break;
|
||||
}
|
||||
printf("%s: failed sr=%x cr=%x state=%x\n", __func__,
|
||||
sr, readb(base + (I2CR << reg_shift)), state);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int tx_byte(struct mxc_i2c_bus *i2c_bus, u8 byte)
|
||||
{
|
||||
int ret;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
ulong base = i2c_bus->base;
|
||||
|
||||
writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
|
||||
writeb(byte, base + (I2DR << reg_shift));
|
||||
|
||||
ret = wait_for_sr_state(i2c_bus, ST_IIF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & I2SR_RX_NO_AK)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stub implementations for outer i2c slave operations.
|
||||
*/
|
||||
void __i2c_force_reset_slave(void)
|
||||
{
|
||||
}
|
||||
void i2c_force_reset_slave(void)
|
||||
__attribute__((weak, alias("__i2c_force_reset_slave")));
|
||||
|
||||
/*
|
||||
* Stop I2C transaction
|
||||
*/
|
||||
static void i2c_imx_stop(struct mxc_i2c_bus *i2c_bus)
|
||||
{
|
||||
int ret;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
ulong base = i2c_bus->base;
|
||||
unsigned int temp = readb(base + (I2CR << reg_shift));
|
||||
|
||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
|
||||
if (ret < 0)
|
||||
printf("%s:trigger stop failed\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send start signal, chip address and
|
||||
* write register address
|
||||
*/
|
||||
static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip,
|
||||
u32 addr, int alen)
|
||||
{
|
||||
unsigned int temp;
|
||||
int ret;
|
||||
bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
|
||||
ulong base = i2c_bus->base;
|
||||
int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
|
||||
/* Reset i2c slave */
|
||||
i2c_force_reset_slave();
|
||||
|
||||
/* Enable I2C controller */
|
||||
if (quirk)
|
||||
ret = readb(base + (I2CR << reg_shift)) & I2CR_IDIS;
|
||||
else
|
||||
ret = !(readb(base + (I2CR << reg_shift)) & I2CR_IEN);
|
||||
|
||||
if (ret) {
|
||||
writeb(I2CR_IEN, base + (I2CR << reg_shift));
|
||||
/* Wait for controller to be stable */
|
||||
udelay(50);
|
||||
}
|
||||
|
||||
if (readb(base + (IADR << reg_shift)) == (chip << 1))
|
||||
writeb((chip << 1) ^ 2, base + (IADR << reg_shift));
|
||||
writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
|
||||
ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Start I2C transaction */
|
||||
temp = readb(base + (I2CR << reg_shift));
|
||||
temp |= I2CR_MSTA;
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
|
||||
ret = wait_for_sr_state(i2c_bus, ST_BUS_BUSY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
temp |= I2CR_MTX | I2CR_TX_NO_AK;
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
|
||||
/* write slave address */
|
||||
ret = tx_byte(i2c_bus, chip << 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (alen--) {
|
||||
ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
|
||||
{
|
||||
if (i2c_bus && i2c_bus->idle_bus_fn)
|
||||
return i2c_bus->idle_bus_fn(i2c_bus->idle_bus_data);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* See Linux Documentation/devicetree/bindings/i2c/i2c-imx.txt
|
||||
* "
|
||||
* scl-gpios: specify the gpio related to SCL pin
|
||||
* sda-gpios: specify the gpio related to SDA pin
|
||||
* add pinctrl to configure i2c pins to gpio function for i2c
|
||||
* bus recovery, call it "gpio" state
|
||||
* "
|
||||
*
|
||||
* The i2c_idle_bus is an implementation following Linux Kernel.
|
||||
*/
|
||||
int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
|
||||
{
|
||||
struct udevice *bus = i2c_bus->bus;
|
||||
struct gpio_desc *scl_gpio = &i2c_bus->scl_gpio;
|
||||
struct gpio_desc *sda_gpio = &i2c_bus->sda_gpio;
|
||||
int sda, scl;
|
||||
int i, ret = 0;
|
||||
ulong elapsed, start_time;
|
||||
|
||||
if (pinctrl_select_state(bus, "gpio")) {
|
||||
dev_dbg(bus, "Can not to switch to use gpio pinmux\n");
|
||||
/*
|
||||
* GPIO pinctrl for i2c force idle is not a must,
|
||||
* but it is strongly recommended to be used.
|
||||
* Because it can help you to recover from bad
|
||||
* i2c bus state. Do not return failure, because
|
||||
* it is not a must.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN);
|
||||
dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN);
|
||||
scl = dm_gpio_get_value(scl_gpio);
|
||||
sda = dm_gpio_get_value(sda_gpio);
|
||||
|
||||
if ((sda & scl) == 1)
|
||||
goto exit; /* Bus is idle already */
|
||||
|
||||
/* Send high and low on the SCL line */
|
||||
for (i = 0; i < 9; i++) {
|
||||
dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_OUT);
|
||||
dm_gpio_set_value(scl_gpio, 0);
|
||||
udelay(50);
|
||||
dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN);
|
||||
udelay(50);
|
||||
}
|
||||
start_time = get_timer(0);
|
||||
for (;;) {
|
||||
dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN);
|
||||
dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN);
|
||||
scl = dm_gpio_get_value(scl_gpio);
|
||||
sda = dm_gpio_get_value(sda_gpio);
|
||||
if ((sda & scl) == 1)
|
||||
break;
|
||||
WATCHDOG_RESET();
|
||||
elapsed = get_timer(start_time);
|
||||
if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */
|
||||
ret = -EBUSY;
|
||||
printf("%s: failed to clear bus, sda=%d scl=%d\n", __func__, sda, scl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
pinctrl_select_state(bus, "default");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip,
|
||||
u32 addr, int alen)
|
||||
{
|
||||
int retry;
|
||||
int ret;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
|
||||
if (!i2c_bus->base)
|
||||
return -ENODEV;
|
||||
|
||||
for (retry = 0; retry < 3; retry++) {
|
||||
ret = i2c_init_transfer_(i2c_bus, chip, addr, alen);
|
||||
if (ret >= 0)
|
||||
return 0;
|
||||
i2c_imx_stop(i2c_bus);
|
||||
if (ret == -ENODEV)
|
||||
return ret;
|
||||
|
||||
printf("%s: failed for chip 0x%x retry=%d\n", __func__, chip,
|
||||
retry);
|
||||
if (ret != -ERESTART)
|
||||
/* Disable controller */
|
||||
writeb(I2CR_IDIS, i2c_bus->base + (I2CR << reg_shift));
|
||||
udelay(100);
|
||||
if (i2c_idle_bus(i2c_bus) < 0)
|
||||
break;
|
||||
}
|
||||
printf("%s: give up i2c_regs=0x%lx\n", __func__, i2c_bus->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int i2c_write_data(struct mxc_i2c_bus *i2c_bus, u8 chip, const u8 *buf,
|
||||
int len)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
|
||||
debug("write_data: ");
|
||||
/* use rc for counter */
|
||||
for (i = 0; i < len; ++i)
|
||||
debug(" 0x%02x", buf[i]);
|
||||
debug("\n");
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = tx_byte(i2c_bus, buf[i]);
|
||||
if (ret < 0) {
|
||||
debug("i2c_write_data(): rc=%d\n", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf,
|
||||
int len)
|
||||
{
|
||||
int ret;
|
||||
unsigned int temp;
|
||||
int i;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
ulong base = i2c_bus->base;
|
||||
|
||||
debug("i2c_read_data: chip=0x%x, len=0x%x\n", chip, len);
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = readb(base + (I2CR << reg_shift));
|
||||
temp &= ~(I2CR_MTX | I2CR_TX_NO_AK);
|
||||
if (len == 1)
|
||||
temp |= I2CR_TX_NO_AK;
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
|
||||
/* dummy read to clear ICF */
|
||||
readb(base + (I2DR << reg_shift));
|
||||
|
||||
/* read data */
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = wait_for_sr_state(i2c_bus, ST_IIF);
|
||||
if (ret < 0) {
|
||||
debug("i2c_read_data(): ret=%d\n", ret);
|
||||
i2c_imx_stop(i2c_bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* It must generate STOP before read I2DR to prevent
|
||||
* controller from generating another clock cycle
|
||||
*/
|
||||
if (i == (len - 1)) {
|
||||
i2c_imx_stop(i2c_bus);
|
||||
} else if (i == (len - 2)) {
|
||||
temp = readb(base + (I2CR << reg_shift));
|
||||
temp |= I2CR_TX_NO_AK;
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
}
|
||||
writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
|
||||
buf[i] = readb(base + (I2DR << reg_shift));
|
||||
}
|
||||
|
||||
/* reuse ret for counter*/
|
||||
for (ret = 0; ret < len; ++ret)
|
||||
debug(" 0x%02x", buf[ret]);
|
||||
debug("\n");
|
||||
|
||||
i2c_imx_stop(i2c_bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_I2C
|
||||
/*
|
||||
* Read data from I2C device
|
||||
*/
|
||||
static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
|
||||
int alen, u8 *buf, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 temp;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
ulong base = i2c_bus->base;
|
||||
|
||||
ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
temp = readb(base + (I2CR << reg_shift));
|
||||
temp |= I2CR_RSTA;
|
||||
writeb(temp, base + (I2CR << reg_shift));
|
||||
|
||||
ret = tx_byte(i2c_bus, (chip << 1) | 1);
|
||||
if (ret < 0) {
|
||||
i2c_imx_stop(i2c_bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_read_data(i2c_bus, chip, buf, len);
|
||||
|
||||
i2c_imx_stop(i2c_bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to I2C device
|
||||
*/
|
||||
static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
|
||||
int alen, const u8 *buf, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_write_data(i2c_bus, chip, buf, len);
|
||||
|
||||
i2c_imx_stop(i2c_bus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !defined(I2C2_BASE_ADDR)
|
||||
#define I2C2_BASE_ADDR 0
|
||||
#endif
|
||||
|
||||
#if !defined(I2C3_BASE_ADDR)
|
||||
#define I2C3_BASE_ADDR 0
|
||||
#endif
|
||||
|
||||
#if !defined(I2C4_BASE_ADDR)
|
||||
#define I2C4_BASE_ADDR 0
|
||||
#endif
|
||||
|
||||
static struct mxc_i2c_bus mxc_i2c_buses[] = {
|
||||
#if defined(CONFIG_LS102XA) || defined(CONFIG_VF610) || \
|
||||
defined(CONFIG_FSL_LAYERSCAPE)
|
||||
{ 0, I2C1_BASE_ADDR, I2C_QUIRK_FLAG },
|
||||
{ 1, I2C2_BASE_ADDR, I2C_QUIRK_FLAG },
|
||||
{ 2, I2C3_BASE_ADDR, I2C_QUIRK_FLAG },
|
||||
{ 3, I2C4_BASE_ADDR, I2C_QUIRK_FLAG },
|
||||
#else
|
||||
{ 0, I2C1_BASE_ADDR, 0 },
|
||||
{ 1, I2C2_BASE_ADDR, 0 },
|
||||
{ 2, I2C3_BASE_ADDR, 0 },
|
||||
{ 3, I2C4_BASE_ADDR, 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mxc_i2c_bus *i2c_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
return &mxc_i2c_buses[adap->hwadapnr];
|
||||
}
|
||||
|
||||
static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
return bus_i2c_read(i2c_get_base(adap), chip, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
static int mxc_i2c_write(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
return bus_i2c_write(i2c_get_base(adap), chip, addr, alen, buffer, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if a chip at a given address responds (probe the chip)
|
||||
*/
|
||||
static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
|
||||
{
|
||||
return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
int __enable_i2c_clk(unsigned char enable, unsigned i2c_num)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
int enable_i2c_clk(unsigned char enable, unsigned i2c_num)
|
||||
__attribute__((weak, alias("__enable_i2c_clk")));
|
||||
|
||||
void bus_i2c_init(int index, int speed, int unused,
|
||||
int (*idle_bus_fn)(void *p), void *idle_bus_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (index >= ARRAY_SIZE(mxc_i2c_buses)) {
|
||||
debug("Error i2c index\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Warning: Be careful to allow the assignment to a static
|
||||
* variable here. This function could be called while U-Boot is
|
||||
* still running in flash memory. So such assignment is equal
|
||||
* to write data to flash without erasing.
|
||||
*/
|
||||
if (idle_bus_fn)
|
||||
mxc_i2c_buses[index].idle_bus_fn = idle_bus_fn;
|
||||
if (idle_bus_data)
|
||||
mxc_i2c_buses[index].idle_bus_data = idle_bus_data;
|
||||
|
||||
ret = enable_i2c_clk(1, index);
|
||||
if (ret < 0) {
|
||||
debug("I2C-%d clk fail to enable.\n", index);
|
||||
return;
|
||||
}
|
||||
|
||||
bus_i2c_set_bus_speed(&mxc_i2c_buses[index], speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Early init I2C for prepare read the clk through I2C.
|
||||
*/
|
||||
void i2c_early_init_f(void)
|
||||
{
|
||||
ulong base = mxc_i2c_buses[I2C_EARLY_INIT_INDEX].base;
|
||||
bool quirk = mxc_i2c_buses[I2C_EARLY_INIT_INDEX].driver_data
|
||||
& I2C_QUIRK_FLAG ? true : false;
|
||||
int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
|
||||
/* Set I2C divider value */
|
||||
writeb(I2C_IFDR_DIV_CONSERVATIVE, base + (IFDR << reg_shift));
|
||||
/* Reset module */
|
||||
writeb(I2CR_IDIS, base + (I2CR << reg_shift));
|
||||
writeb(0, base + (I2SR << reg_shift));
|
||||
/* Enable I2C */
|
||||
writeb(I2CR_IEN, base + (I2CR << reg_shift));
|
||||
}
|
||||
|
||||
/*
|
||||
* Init I2C Bus
|
||||
*/
|
||||
static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
bus_i2c_init(adap->hwadapnr, speed, slaveaddr, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set I2C Speed
|
||||
*/
|
||||
static u32 mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
return bus_i2c_set_bus_speed(i2c_get_base(adap), speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register mxc i2c adapters
|
||||
*/
|
||||
#ifdef CONFIG_SYS_I2C_MXC_I2C1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxc0, mxc_i2c_init, mxc_i2c_probe,
|
||||
mxc_i2c_read, mxc_i2c_write,
|
||||
mxc_i2c_set_bus_speed,
|
||||
CONFIG_SYS_MXC_I2C1_SPEED,
|
||||
CONFIG_SYS_MXC_I2C1_SLAVE, 0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_MXC_I2C2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxc1, mxc_i2c_init, mxc_i2c_probe,
|
||||
mxc_i2c_read, mxc_i2c_write,
|
||||
mxc_i2c_set_bus_speed,
|
||||
CONFIG_SYS_MXC_I2C2_SPEED,
|
||||
CONFIG_SYS_MXC_I2C2_SLAVE, 1)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_MXC_I2C3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe,
|
||||
mxc_i2c_read, mxc_i2c_write,
|
||||
mxc_i2c_set_bus_speed,
|
||||
CONFIG_SYS_MXC_I2C3_SPEED,
|
||||
CONFIG_SYS_MXC_I2C3_SLAVE, 2)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_MXC_I2C4
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxc3, mxc_i2c_init, mxc_i2c_probe,
|
||||
mxc_i2c_read, mxc_i2c_write,
|
||||
mxc_i2c_set_bus_speed,
|
||||
CONFIG_SYS_MXC_I2C4_SPEED,
|
||||
CONFIG_SYS_MXC_I2C4_SLAVE, 3)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
|
||||
return bus_i2c_set_bus_speed(i2c_bus, speed);
|
||||
}
|
||||
|
||||
static int mxc_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node = bus->of_offset;
|
||||
fdt_addr_t addr;
|
||||
int ret, ret2;
|
||||
|
||||
i2c_bus->driver_data = dev_get_driver_data(bus);
|
||||
|
||||
addr = dev_get_addr(bus);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -ENODEV;
|
||||
|
||||
i2c_bus->base = addr;
|
||||
i2c_bus->index = bus->seq;
|
||||
i2c_bus->bus = bus;
|
||||
|
||||
/* Enable clk */
|
||||
ret = enable_i2c_clk(1, bus->seq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* See Documentation/devicetree/bindings/i2c/i2c-imx.txt
|
||||
* Use gpio to force bus idle when necessary.
|
||||
*/
|
||||
ret = fdt_find_string(fdt, node, "pinctrl-names", "gpio");
|
||||
if (ret < 0) {
|
||||
dev_info(dev, "i2c bus %d at %lu, no gpio pinctrl state.\n", bus->seq, i2c_bus->base);
|
||||
} else {
|
||||
ret = gpio_request_by_name_nodev(fdt, node, "scl-gpios",
|
||||
0, &i2c_bus->scl_gpio,
|
||||
GPIOD_IS_OUT);
|
||||
ret2 = gpio_request_by_name_nodev(fdt, node, "sda-gpios",
|
||||
0, &i2c_bus->sda_gpio,
|
||||
GPIOD_IS_OUT);
|
||||
if (!dm_gpio_is_valid(&i2c_bus->sda_gpio) |
|
||||
!dm_gpio_is_valid(&i2c_bus->scl_gpio) |
|
||||
ret | ret2) {
|
||||
dev_err(dev, "i2c bus %d at %lu, fail to request scl/sda gpio\n", bus->seq, i2c_bus->base);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_idle_bus(i2c_bus);
|
||||
if (ret < 0) {
|
||||
/* Disable clk */
|
||||
enable_i2c_clk(0, bus->seq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pinmux settings are in board file now, until pinmux is supported,
|
||||
* we can set pinmux here in probe function.
|
||||
*/
|
||||
|
||||
debug("i2c : controller bus %d at %lu , speed %d: ",
|
||||
bus->seq, i2c_bus->base,
|
||||
i2c_bus->speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
||||
u32 chip_flags)
|
||||
{
|
||||
int ret;
|
||||
struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
|
||||
ret = i2c_init_transfer(i2c_bus, chip_addr, 0, 0);
|
||||
if (ret < 0) {
|
||||
debug("%s failed, ret = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_imx_stop(i2c_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
||||
{
|
||||
struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
int ret = 0;
|
||||
ulong base = i2c_bus->base;
|
||||
int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
|
||||
VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
|
||||
|
||||
/*
|
||||
* Here the 3rd parameter addr and the 4th one alen are set to 0,
|
||||
* because here we only want to send out chip address. The register
|
||||
* address is wrapped in msg.
|
||||
*/
|
||||
ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0);
|
||||
if (ret < 0) {
|
||||
debug("i2c_init_transfer error: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
else {
|
||||
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
if (ret)
|
||||
break;
|
||||
if (next_is_read) {
|
||||
/* Reuse ret */
|
||||
ret = readb(base + (I2CR << reg_shift));
|
||||
ret |= I2CR_RSTA;
|
||||
writeb(ret, base + (I2CR << reg_shift));
|
||||
|
||||
ret = tx_byte(i2c_bus, (msg->addr << 1) | 1);
|
||||
if (ret < 0) {
|
||||
i2c_imx_stop(i2c_bus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
debug("i2c_write: error sending\n");
|
||||
|
||||
i2c_imx_stop(i2c_bus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops mxc_i2c_ops = {
|
||||
.xfer = mxc_i2c_xfer,
|
||||
.probe_chip = mxc_i2c_probe_chip,
|
||||
.set_bus_speed = mxc_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id mxc_i2c_ids[] = {
|
||||
{ .compatible = "fsl,imx21-i2c", },
|
||||
{ .compatible = "fsl,vf610-i2c", .data = I2C_QUIRK_FLAG, },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_mxc) = {
|
||||
.name = "i2c_mxc",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = mxc_i2c_ids,
|
||||
.probe = mxc_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct mxc_i2c_bus),
|
||||
.ops = &mxc_i2c_ops,
|
||||
};
|
||||
#endif
|
||||
320
u-boot/drivers/i2c/mxs_i2c.c
Normal file
320
u-boot/drivers/i2c/mxs_i2c.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Freescale i.MX28 I2C Driver
|
||||
*
|
||||
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
||||
* on behalf of DENX Software Engineering GmbH
|
||||
*
|
||||
* Partly based on Linux kernel i2c-mxs.c driver:
|
||||
* Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
|
||||
*
|
||||
* Which was based on a (non-working) driver which was:
|
||||
* Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
#define MXS_I2C_MAX_TIMEOUT 1000000
|
||||
|
||||
static struct mxs_i2c_regs *mxs_i2c_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
if (adap->hwadapnr == 0)
|
||||
return (struct mxs_i2c_regs *)MXS_I2C0_BASE;
|
||||
else
|
||||
return (struct mxs_i2c_regs *)MXS_I2C1_BASE;
|
||||
}
|
||||
|
||||
static unsigned int mxs_i2c_get_bus_speed(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
uint32_t clk = mxc_get_clock(MXC_XTAL_CLK);
|
||||
uint32_t timing0;
|
||||
|
||||
timing0 = readl(&i2c_regs->hw_i2c_timing0);
|
||||
/*
|
||||
* This is a reverse version of the algorithm presented in
|
||||
* i2c_set_bus_speed(). Please refer there for details.
|
||||
*/
|
||||
return clk / ((((timing0 >> 16) - 3) * 2) + 38);
|
||||
}
|
||||
|
||||
static uint mxs_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
/*
|
||||
* The timing derivation algorithm. There is no documentation for this
|
||||
* algorithm available, it was derived by using the scope and fiddling
|
||||
* with constants until the result observed on the scope was good enough
|
||||
* for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be
|
||||
* possible to assume the algorithm works for other frequencies as well.
|
||||
*
|
||||
* Note it was necessary to cap the frequency on both ends as it's not
|
||||
* possible to configure completely arbitrary frequency for the I2C bus
|
||||
* clock.
|
||||
*/
|
||||
uint32_t clk = mxc_get_clock(MXC_XTAL_CLK);
|
||||
uint32_t base = ((clk / speed) - 38) / 2;
|
||||
uint16_t high_count = base + 3;
|
||||
uint16_t low_count = base - 3;
|
||||
uint16_t rcv_count = (high_count * 3) / 4;
|
||||
uint16_t xmit_count = low_count / 4;
|
||||
|
||||
if (speed > 540000) {
|
||||
printf("MXS I2C: Speed too high (%d Hz)\n", speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (speed < 12000) {
|
||||
printf("MXS I2C: Speed too low (%d Hz)\n", speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0);
|
||||
writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1);
|
||||
|
||||
writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) |
|
||||
(0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_i2c_reset(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
int ret;
|
||||
int speed = mxs_i2c_get_bus_speed(adap);
|
||||
|
||||
ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Block reset timeout\n");
|
||||
return;
|
||||
}
|
||||
|
||||
writel(I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ |
|
||||
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
|
||||
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ,
|
||||
&i2c_regs->hw_i2c_ctrl1_clr);
|
||||
|
||||
writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
|
||||
mxs_i2c_set_bus_speed(adap, speed);
|
||||
}
|
||||
|
||||
static void mxs_i2c_setup_read(struct i2c_adapter *adap, uint8_t chip, int len)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
|
||||
writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START |
|
||||
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
|
||||
(1 << I2C_QUEUECMD_XFER_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
writel((chip << 1) | 1, &i2c_regs->hw_i2c_data);
|
||||
|
||||
writel(I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE |
|
||||
(len << I2C_QUEUECMD_XFER_COUNT_OFFSET) |
|
||||
I2C_QUEUECMD_POST_SEND_STOP, &i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
}
|
||||
|
||||
static int mxs_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buf, int blen, int stop)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
uint32_t data, tmp;
|
||||
int i, remain, off;
|
||||
int timeout = MXS_I2C_MAX_TIMEOUT;
|
||||
|
||||
if ((alen > 4) || (alen == 0)) {
|
||||
debug("MXS I2C: Invalid address length\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (stop)
|
||||
stop = I2C_QUEUECMD_POST_SEND_STOP;
|
||||
|
||||
writel(I2C_QUEUECMD_PRE_SEND_START |
|
||||
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
|
||||
((blen + alen + 1) << I2C_QUEUECMD_XFER_COUNT_OFFSET) | stop,
|
||||
&i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
data = (chip << 1) << 24;
|
||||
|
||||
for (i = 0; i < alen; i++) {
|
||||
data >>= 8;
|
||||
data |= ((char *)&addr)[alen - i - 1] << 24;
|
||||
if ((i & 3) == 2)
|
||||
writel(data, &i2c_regs->hw_i2c_data);
|
||||
}
|
||||
|
||||
off = i;
|
||||
for (; i < off + blen; i++) {
|
||||
data >>= 8;
|
||||
data |= buf[i - off] << 24;
|
||||
if ((i & 3) == 2)
|
||||
writel(data, &i2c_regs->hw_i2c_data);
|
||||
}
|
||||
|
||||
remain = 24 - ((i & 3) * 8);
|
||||
if (remain)
|
||||
writel(data >> remain, &i2c_regs->hw_i2c_data);
|
||||
|
||||
writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
|
||||
while (--timeout) {
|
||||
tmp = readl(&i2c_regs->hw_i2c_queuestat);
|
||||
if (tmp & I2C_QUEUESTAT_WR_QUEUE_EMPTY)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
debug("MXS I2C: Failed transmitting data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_i2c_wait_for_ack(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
uint32_t tmp;
|
||||
int timeout = MXS_I2C_MAX_TIMEOUT;
|
||||
|
||||
for (;;) {
|
||||
tmp = readl(&i2c_regs->hw_i2c_ctrl1);
|
||||
if (tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
|
||||
debug("MXS I2C: No slave ACK\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (tmp & (
|
||||
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
|
||||
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ)) {
|
||||
debug("MXS I2C: Error (CTRL1 = %08x)\n", tmp);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ)
|
||||
break;
|
||||
|
||||
if (!timeout--) {
|
||||
debug("MXS I2C: Operation timed out\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mxs_i2c_reset(adap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mxs_i2c_if_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
struct mxs_i2c_regs *i2c_regs = mxs_i2c_get_base(adap);
|
||||
uint32_t tmp = 0;
|
||||
int timeout = MXS_I2C_MAX_TIMEOUT;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = mxs_i2c_write(adap, chip, addr, alen, NULL, 0, 0);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mxs_i2c_wait_for_ack(adap);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mxs_i2c_setup_read(adap, chip, len);
|
||||
ret = mxs_i2c_wait_for_ack(adap);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed reading address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!(i & 3)) {
|
||||
while (--timeout) {
|
||||
tmp = readl(&i2c_regs->hw_i2c_queuestat);
|
||||
if (!(tmp & I2C_QUEUESTAT_RD_QUEUE_EMPTY))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
debug("MXS I2C: Failed receiving data!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
tmp = readl(&i2c_regs->hw_i2c_queuedata);
|
||||
}
|
||||
buffer[i] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_i2c_if_write(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
int ret;
|
||||
ret = mxs_i2c_write(adap, chip, addr, alen, buffer, len, 1);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mxs_i2c_wait_for_ack(adap);
|
||||
if (ret)
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
|
||||
{
|
||||
int ret;
|
||||
ret = mxs_i2c_write(adap, chip, 0, 1, NULL, 0, 1);
|
||||
if (!ret)
|
||||
ret = mxs_i2c_wait_for_ack(adap);
|
||||
mxs_i2c_reset(adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxs_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
mxs_i2c_reset(adap);
|
||||
mxs_i2c_set_bus_speed(adap, speed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxs0, mxs_i2c_init, mxs_i2c_probe,
|
||||
mxs_i2c_if_read, mxs_i2c_if_write,
|
||||
mxs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, 0, 0)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(mxs1, mxs_i2c_init, mxs_i2c_probe,
|
||||
mxs_i2c_if_read, mxs_i2c_if_write,
|
||||
mxs_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_SPEED, 0, 1)
|
||||
771
u-boot/drivers/i2c/omap24xx_i2c.c
Normal file
771
u-boot/drivers/i2c/omap24xx_i2c.c
Normal file
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Basic I2C functions
|
||||
*
|
||||
* Copyright (c) 2004 Texas Instruments
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the license found in the file
|
||||
* named COPYING that should have accompanied this file.
|
||||
*
|
||||
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Author: Jian Zhang jzhang@ti.com, Texas Instruments
|
||||
*
|
||||
* Copyright (c) 2003 Wolfgang Denk, wd@denx.de
|
||||
* Rewritten to fit into the current U-Boot framework
|
||||
*
|
||||
* Adapted for OMAP2420 I2C, r-woodruff2@ti.com
|
||||
*
|
||||
* Copyright (c) 2013 Lubomir Popov <lpopov@mm-sol.com>, MM Solutions
|
||||
* New i2c_read, i2c_write and i2c_probe functions, tested on OMAP4
|
||||
* (4430/60/70), OMAP5 (5430) and AM335X (3359); should work on older
|
||||
* OMAPs and derivatives as well. The only anticipated exception would
|
||||
* be the OMAP2420, which shall require driver modification.
|
||||
* - Rewritten i2c_read to operate correctly with all types of chips
|
||||
* (old function could not read consistent data from some I2C slaves).
|
||||
* - Optimized i2c_write.
|
||||
* - New i2c_probe, performs write access vs read. The old probe could
|
||||
* hang the system under certain conditions (e.g. unconfigured pads).
|
||||
* - The read/write/probe functions try to identify unconfigured bus.
|
||||
* - Status functions now read irqstatus_raw as per TRM guidelines
|
||||
* (except for OMAP243X and OMAP34XX).
|
||||
* - Driver now supports up to I2C5 (OMAP5).
|
||||
*
|
||||
* Copyright (c) 2014 Hannes Schmelzer <oe5hpm@oevsv.at>, B&R
|
||||
* - Added support for set_speed
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include <asm/arch/i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "omap24xx_i2c.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define I2C_TIMEOUT 1000
|
||||
|
||||
/* Absolutely safe for status update at 100 kHz I2C: */
|
||||
#define I2C_WAIT 200
|
||||
|
||||
static int wait_for_bb(struct i2c_adapter *adap);
|
||||
static struct i2c *omap24_get_base(struct i2c_adapter *adap);
|
||||
static u16 wait_for_event(struct i2c_adapter *adap);
|
||||
static void flush_fifo(struct i2c_adapter *adap);
|
||||
static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
|
||||
{
|
||||
unsigned int sampleclk, prescaler;
|
||||
int fsscll, fssclh;
|
||||
|
||||
speed <<= 1;
|
||||
prescaler = 0;
|
||||
/*
|
||||
* some divisors may cause a precission loss, but shouldn't
|
||||
* be a big thing, because i2c_clk is then allready very slow.
|
||||
*/
|
||||
while (prescaler <= 0xFF) {
|
||||
sampleclk = I2C_IP_CLK / (prescaler+1);
|
||||
|
||||
fsscll = sampleclk / speed;
|
||||
fssclh = fsscll;
|
||||
fsscll -= I2C_FASTSPEED_SCLL_TRIM;
|
||||
fssclh -= I2C_FASTSPEED_SCLH_TRIM;
|
||||
|
||||
if (((fsscll > 0) && (fssclh > 0)) &&
|
||||
((fsscll <= (255-I2C_FASTSPEED_SCLL_TRIM)) &&
|
||||
(fssclh <= (255-I2C_FASTSPEED_SCLH_TRIM)))) {
|
||||
if (pscl)
|
||||
*pscl = fsscll;
|
||||
if (psch)
|
||||
*psch = fssclh;
|
||||
|
||||
return prescaler;
|
||||
}
|
||||
prescaler++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int psc, fsscll = 0, fssclh = 0;
|
||||
int hsscll = 0, hssclh = 0;
|
||||
u32 scll = 0, sclh = 0;
|
||||
|
||||
if (speed >= OMAP_I2C_HIGH_SPEED) {
|
||||
/* High speed */
|
||||
psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK;
|
||||
psc -= 1;
|
||||
if (psc < I2C_PSC_MIN) {
|
||||
printf("Error : I2C unsupported prescaler %d\n", psc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* For first phase of HS mode */
|
||||
fsscll = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
|
||||
|
||||
fssclh = fsscll;
|
||||
|
||||
fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM;
|
||||
fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM;
|
||||
if (((fsscll < 0) || (fssclh < 0)) ||
|
||||
((fsscll > 255) || (fssclh > 255))) {
|
||||
puts("Error : I2C initializing first phase clock\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* For second phase of HS mode */
|
||||
hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
|
||||
|
||||
hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM;
|
||||
hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM;
|
||||
if (((fsscll < 0) || (fssclh < 0)) ||
|
||||
((fsscll > 255) || (fssclh > 255))) {
|
||||
puts("Error : I2C initializing second phase clock\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll;
|
||||
sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh;
|
||||
|
||||
} else {
|
||||
/* Standard and fast speed */
|
||||
psc = omap24_i2c_findpsc(&scll, &sclh, speed);
|
||||
if (0 > psc) {
|
||||
puts("Error : I2C initializing clock\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
adap->speed = speed;
|
||||
adap->waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */
|
||||
writew(0, &i2c_base->con);
|
||||
writew(psc, &i2c_base->psc);
|
||||
writew(scll, &i2c_base->scll);
|
||||
writew(sclh, &i2c_base->sclh);
|
||||
writew(I2C_CON_EN, &i2c_base->con);
|
||||
writew(0xFFFF, &i2c_base->stat); /* clear all pending status */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap24_i2c_deblock(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int i;
|
||||
u16 systest;
|
||||
u16 orgsystest;
|
||||
|
||||
/* set test mode ST_EN = 1 */
|
||||
orgsystest = readw(&i2c_base->systest);
|
||||
systest = orgsystest;
|
||||
/* enable testmode */
|
||||
systest |= I2C_SYSTEST_ST_EN;
|
||||
writew(systest, &i2c_base->systest);
|
||||
systest &= ~I2C_SYSTEST_TMODE_MASK;
|
||||
systest |= 3 << I2C_SYSTEST_TMODE_SHIFT;
|
||||
writew(systest, &i2c_base->systest);
|
||||
|
||||
/* set SCL, SDA = 1 */
|
||||
systest |= I2C_SYSTEST_SCL_O | I2C_SYSTEST_SDA_O;
|
||||
writew(systest, &i2c_base->systest);
|
||||
udelay(10);
|
||||
|
||||
/* toggle scl 9 clocks */
|
||||
for (i = 0; i < 9; i++) {
|
||||
/* SCL = 0 */
|
||||
systest &= ~I2C_SYSTEST_SCL_O;
|
||||
writew(systest, &i2c_base->systest);
|
||||
udelay(10);
|
||||
/* SCL = 1 */
|
||||
systest |= I2C_SYSTEST_SCL_O;
|
||||
writew(systest, &i2c_base->systest);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/* send stop */
|
||||
systest &= ~I2C_SYSTEST_SDA_O;
|
||||
writew(systest, &i2c_base->systest);
|
||||
udelay(10);
|
||||
systest |= I2C_SYSTEST_SCL_O | I2C_SYSTEST_SDA_O;
|
||||
writew(systest, &i2c_base->systest);
|
||||
udelay(10);
|
||||
|
||||
/* restore original mode */
|
||||
writew(orgsystest, &i2c_base->systest);
|
||||
}
|
||||
|
||||
static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int timeout = I2C_TIMEOUT;
|
||||
int deblock = 1;
|
||||
|
||||
retry:
|
||||
if (readw(&i2c_base->con) & I2C_CON_EN) {
|
||||
writew(0, &i2c_base->con);
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */
|
||||
udelay(1000);
|
||||
|
||||
writew(I2C_CON_EN, &i2c_base->con);
|
||||
while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) {
|
||||
if (timeout <= 0) {
|
||||
puts("ERROR: Timeout in soft-reset\n");
|
||||
return;
|
||||
}
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
if (0 != omap24_i2c_setspeed(adap, speed)) {
|
||||
printf("ERROR: failed to setup I2C bus-speed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* own address */
|
||||
writew(slaveadd, &i2c_base->oa);
|
||||
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
|
||||
/*
|
||||
* Have to enable interrupts for OMAP2/3, these IPs don't have
|
||||
* an 'irqstatus_raw' register and we shall have to poll 'stat'
|
||||
*/
|
||||
writew(I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE |
|
||||
I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie);
|
||||
#endif
|
||||
udelay(1000);
|
||||
flush_fifo(adap);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
|
||||
/* Handle possible failed I2C state */
|
||||
if (wait_for_bb(adap))
|
||||
if (deblock == 1) {
|
||||
omap24_i2c_deblock(adap);
|
||||
deblock = 0;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_fifo(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
u16 stat;
|
||||
|
||||
/*
|
||||
* note: if you try and read data when its not there or ready
|
||||
* you get a bus error
|
||||
*/
|
||||
while (1) {
|
||||
stat = readw(&i2c_base->stat);
|
||||
if (stat == I2C_STAT_RRDY) {
|
||||
readb(&i2c_base->data);
|
||||
writew(I2C_STAT_RRDY, &i2c_base->stat);
|
||||
udelay(1000);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: Use write access. Allows to identify addresses that are
|
||||
* write-only (like the config register of dual-port EEPROMs)
|
||||
*/
|
||||
static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
u16 status;
|
||||
int res = 1; /* default = fail */
|
||||
|
||||
if (chip == readw(&i2c_base->oa))
|
||||
return res;
|
||||
|
||||
/* Wait until bus is free */
|
||||
if (wait_for_bb(adap))
|
||||
return res;
|
||||
|
||||
/* No data transfer, slave addr only */
|
||||
writew(chip, &i2c_base->sa);
|
||||
/* Stop bit needed here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
|
||||
I2C_CON_STP, &i2c_base->con);
|
||||
|
||||
status = wait_for_event(adap);
|
||||
|
||||
if ((status & ~I2C_STAT_XRDY) == 0 || (status & I2C_STAT_AL)) {
|
||||
/*
|
||||
* With current high-level command implementation, notifying
|
||||
* the user shall flood the console with 127 messages. If
|
||||
* silent exit is desired upon unconfigured bus, remove the
|
||||
* following 'if' section:
|
||||
*/
|
||||
if (status == I2C_STAT_XRDY)
|
||||
printf("i2c_probe: pads on bus %d probably not configured (status=0x%x)\n",
|
||||
adap->hwadapnr, status);
|
||||
|
||||
goto pr_exit;
|
||||
}
|
||||
|
||||
/* Check for ACK (!NAK) */
|
||||
if (!(status & I2C_STAT_NACK)) {
|
||||
res = 0; /* Device found */
|
||||
udelay(adap->waitdelay);/* Required by AM335X in SPL */
|
||||
/* Abort transfer (force idle state) */
|
||||
writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */
|
||||
udelay(1000);
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX |
|
||||
I2C_CON_STP, &i2c_base->con); /* STP */
|
||||
}
|
||||
pr_exit:
|
||||
flush_fifo(adap);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: Function now uses a single I2C read transaction with bulk transfer
|
||||
* of the requested number of bytes (note that the 'i2c md' command
|
||||
* limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is
|
||||
* defined in the board config header, this transaction shall be with
|
||||
* Repeated Start (Sr) between the address and data phases; otherwise
|
||||
* Stop-Start (P-S) shall be used (some I2C chips do require a P-S).
|
||||
* The address (reg offset) may be 0, 1 or 2 bytes long.
|
||||
* Function now reads correctly from chips that return more than one
|
||||
* byte of data per addressed register (like TI temperature sensors),
|
||||
* or that do not need a register address at all (such as some clock
|
||||
* distributors).
|
||||
*/
|
||||
static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int i2c_error = 0;
|
||||
u16 status;
|
||||
|
||||
if (alen < 0) {
|
||||
puts("I2C read: addr len < 0\n");
|
||||
return 1;
|
||||
}
|
||||
if (len < 0) {
|
||||
puts("I2C read: data len < 0\n");
|
||||
return 1;
|
||||
}
|
||||
if (buffer == NULL) {
|
||||
puts("I2C read: NULL pointer passed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 2) {
|
||||
printf("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > (1 << 16)) {
|
||||
puts("I2C read: address out of range\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
|
||||
/* Wait until bus not busy */
|
||||
if (wait_for_bb(adap))
|
||||
return 1;
|
||||
|
||||
/* Zero, one or two bytes reg address (offset) */
|
||||
writew(alen, &i2c_base->cnt);
|
||||
/* Set slave address */
|
||||
writew(chip, &i2c_base->sa);
|
||||
|
||||
if (alen) {
|
||||
/* Must write reg offset first */
|
||||
#ifdef CONFIG_I2C_REPEATED_START
|
||||
/* No stop bit, use Repeated Start (Sr) */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT |
|
||||
I2C_CON_TRX, &i2c_base->con);
|
||||
#else
|
||||
/* Stop - Start (P-S) */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP |
|
||||
I2C_CON_TRX, &i2c_base->con);
|
||||
#endif
|
||||
/* Send register offset */
|
||||
while (1) {
|
||||
status = wait_for_event(adap);
|
||||
/* Try to identify bus that is not padconf'd for I2C */
|
||||
if (status == I2C_STAT_XRDY) {
|
||||
i2c_error = 2;
|
||||
printf("i2c_read (addr phase): pads on bus %d probably not configured (status=0x%x)\n",
|
||||
adap->hwadapnr, status);
|
||||
goto rd_exit;
|
||||
}
|
||||
if (status == 0 || (status & I2C_STAT_NACK)) {
|
||||
i2c_error = 1;
|
||||
printf("i2c_read: error waiting for addr ACK (status=0x%x)\n",
|
||||
status);
|
||||
goto rd_exit;
|
||||
}
|
||||
if (alen) {
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
alen--;
|
||||
/* Do we have to use byte access? */
|
||||
writeb((addr >> (8 * alen)) & 0xff,
|
||||
&i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
}
|
||||
}
|
||||
if (status & I2C_STAT_ARDY) {
|
||||
writew(I2C_STAT_ARDY, &i2c_base->stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Set slave address */
|
||||
writew(chip, &i2c_base->sa);
|
||||
/* Read len bytes from slave */
|
||||
writew(len, &i2c_base->cnt);
|
||||
/* Need stop bit here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST |
|
||||
I2C_CON_STT | I2C_CON_STP,
|
||||
&i2c_base->con);
|
||||
|
||||
/* Receive data */
|
||||
while (1) {
|
||||
status = wait_for_event(adap);
|
||||
/*
|
||||
* Try to identify bus that is not padconf'd for I2C. This
|
||||
* state could be left over from previous transactions if
|
||||
* the address phase is skipped due to alen=0.
|
||||
*/
|
||||
if (status == I2C_STAT_XRDY) {
|
||||
i2c_error = 2;
|
||||
printf("i2c_read (data phase): pads on bus %d probably not configured (status=0x%x)\n",
|
||||
adap->hwadapnr, status);
|
||||
goto rd_exit;
|
||||
}
|
||||
if (status == 0 || (status & I2C_STAT_NACK)) {
|
||||
i2c_error = 1;
|
||||
goto rd_exit;
|
||||
}
|
||||
if (status & I2C_STAT_RRDY) {
|
||||
*buffer++ = readb(&i2c_base->data);
|
||||
writew(I2C_STAT_RRDY, &i2c_base->stat);
|
||||
}
|
||||
if (status & I2C_STAT_ARDY) {
|
||||
writew(I2C_STAT_ARDY, &i2c_base->stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rd_exit:
|
||||
flush_fifo(adap);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
/* i2c_write: Address (reg offset) may be 0, 1 or 2 bytes long. */
|
||||
static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int i;
|
||||
u16 status;
|
||||
int i2c_error = 0;
|
||||
int timeout = I2C_TIMEOUT;
|
||||
|
||||
if (alen < 0) {
|
||||
puts("I2C write: addr len < 0\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
puts("I2C write: data len < 0\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (buffer == NULL) {
|
||||
puts("I2C write: NULL pointer passed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 2) {
|
||||
printf("I2C write: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > (1 << 16)) {
|
||||
printf("I2C write: address 0x%x + 0x%x out of range\n",
|
||||
addr, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
|
||||
/* Wait until bus not busy */
|
||||
if (wait_for_bb(adap))
|
||||
return 1;
|
||||
|
||||
/* Start address phase - will write regoffset + len bytes data */
|
||||
writew(alen + len, &i2c_base->cnt);
|
||||
/* Set slave address */
|
||||
writew(chip, &i2c_base->sa);
|
||||
/* Stop bit needed here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
|
||||
I2C_CON_STP, &i2c_base->con);
|
||||
|
||||
while (alen) {
|
||||
/* Must write reg offset (one or two bytes) */
|
||||
status = wait_for_event(adap);
|
||||
/* Try to identify bus that is not padconf'd for I2C */
|
||||
if (status == I2C_STAT_XRDY) {
|
||||
i2c_error = 2;
|
||||
printf("i2c_write: pads on bus %d probably not configured (status=0x%x)\n",
|
||||
adap->hwadapnr, status);
|
||||
goto wr_exit;
|
||||
}
|
||||
if (status == 0 || (status & I2C_STAT_NACK)) {
|
||||
i2c_error = 1;
|
||||
printf("i2c_write: error waiting for addr ACK (status=0x%x)\n",
|
||||
status);
|
||||
goto wr_exit;
|
||||
}
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
alen--;
|
||||
writeb((addr >> (8 * alen)) & 0xff, &i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
printf("i2c_write: bus not ready for addr Tx (status=0x%x)\n",
|
||||
status);
|
||||
goto wr_exit;
|
||||
}
|
||||
}
|
||||
/* Address phase is over, now write data */
|
||||
for (i = 0; i < len; i++) {
|
||||
status = wait_for_event(adap);
|
||||
if (status == 0 || (status & I2C_STAT_NACK)) {
|
||||
i2c_error = 1;
|
||||
printf("i2c_write: error waiting for data ACK (status=0x%x)\n",
|
||||
status);
|
||||
goto wr_exit;
|
||||
}
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
writeb(buffer[i], &i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
printf("i2c_write: bus not ready for data Tx (i=%d)\n",
|
||||
i);
|
||||
goto wr_exit;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* poll ARDY bit for making sure that last byte really has been
|
||||
* transferred on the bus.
|
||||
*/
|
||||
do {
|
||||
status = wait_for_event(adap);
|
||||
} while (!(status & I2C_STAT_ARDY) && timeout--);
|
||||
if (timeout <= 0)
|
||||
printf("i2c_write: timed out writig last byte!\n");
|
||||
|
||||
wr_exit:
|
||||
flush_fifo(adap);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the bus to be free by checking the Bus Busy (BB)
|
||||
* bit to become clear
|
||||
*/
|
||||
static int wait_for_bb(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
int timeout = I2C_TIMEOUT;
|
||||
u16 stat;
|
||||
|
||||
writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
|
||||
while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
|
||||
#else
|
||||
/* Read RAW status */
|
||||
while ((stat = readw(&i2c_base->irqstatus_raw) &
|
||||
I2C_STAT_BB) && timeout--) {
|
||||
#endif
|
||||
writew(stat, &i2c_base->stat);
|
||||
udelay(adap->waitdelay);
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf("Timed out in wait_for_bb: status=%04x\n",
|
||||
stat);
|
||||
return 1;
|
||||
}
|
||||
writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the I2C controller to complete current action
|
||||
* and update status
|
||||
*/
|
||||
static u16 wait_for_event(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c *i2c_base = omap24_get_base(adap);
|
||||
u16 status;
|
||||
int timeout = I2C_TIMEOUT;
|
||||
|
||||
do {
|
||||
udelay(adap->waitdelay);
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
|
||||
status = readw(&i2c_base->stat);
|
||||
#else
|
||||
/* Read RAW status */
|
||||
status = readw(&i2c_base->irqstatus_raw);
|
||||
#endif
|
||||
} while (!(status &
|
||||
(I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
|
||||
I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
|
||||
I2C_STAT_AL)) && timeout--);
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf("Timed out in wait_for_event: status=%04x\n",
|
||||
status);
|
||||
/*
|
||||
* If status is still 0 here, probably the bus pads have
|
||||
* not been configured for I2C, and/or pull-ups are missing.
|
||||
*/
|
||||
printf("Check if pads/pull-ups of bus %d are properly configured\n",
|
||||
adap->hwadapnr);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct i2c *omap24_get_base(struct i2c_adapter *adap)
|
||||
{
|
||||
switch (adap->hwadapnr) {
|
||||
case 0:
|
||||
return (struct i2c *)I2C_BASE1;
|
||||
break;
|
||||
case 1:
|
||||
return (struct i2c *)I2C_BASE2;
|
||||
break;
|
||||
#if (I2C_BUS_MAX > 2)
|
||||
case 2:
|
||||
return (struct i2c *)I2C_BASE3;
|
||||
break;
|
||||
#if (I2C_BUS_MAX > 3)
|
||||
case 3:
|
||||
return (struct i2c *)I2C_BASE4;
|
||||
break;
|
||||
#if (I2C_BUS_MAX > 4)
|
||||
case 4:
|
||||
return (struct i2c *)I2C_BASE5;
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
printf("wrong hwadapnr: %d\n", adap->hwadapnr);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED1)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SPEED1 CONFIG_SYS_OMAP24_I2C_SPEED
|
||||
#endif
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE1)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SLAVE1 CONFIG_SYS_OMAP24_I2C_SLAVE
|
||||
#endif
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(omap24_0, omap24_i2c_init, omap24_i2c_probe,
|
||||
omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed,
|
||||
CONFIG_SYS_OMAP24_I2C_SPEED,
|
||||
CONFIG_SYS_OMAP24_I2C_SLAVE,
|
||||
0)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(omap24_1, omap24_i2c_init, omap24_i2c_probe,
|
||||
omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed,
|
||||
CONFIG_SYS_OMAP24_I2C_SPEED1,
|
||||
CONFIG_SYS_OMAP24_I2C_SLAVE1,
|
||||
1)
|
||||
#if (I2C_BUS_MAX > 2)
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED2)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SPEED2 CONFIG_SYS_OMAP24_I2C_SPEED
|
||||
#endif
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE2)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SLAVE2 CONFIG_SYS_OMAP24_I2C_SLAVE
|
||||
#endif
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(omap24_2, omap24_i2c_init, omap24_i2c_probe,
|
||||
omap24_i2c_read, omap24_i2c_write, NULL,
|
||||
CONFIG_SYS_OMAP24_I2C_SPEED2,
|
||||
CONFIG_SYS_OMAP24_I2C_SLAVE2,
|
||||
2)
|
||||
#if (I2C_BUS_MAX > 3)
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED3)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SPEED3 CONFIG_SYS_OMAP24_I2C_SPEED
|
||||
#endif
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE3)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SLAVE3 CONFIG_SYS_OMAP24_I2C_SLAVE
|
||||
#endif
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(omap24_3, omap24_i2c_init, omap24_i2c_probe,
|
||||
omap24_i2c_read, omap24_i2c_write, NULL,
|
||||
CONFIG_SYS_OMAP24_I2C_SPEED3,
|
||||
CONFIG_SYS_OMAP24_I2C_SLAVE3,
|
||||
3)
|
||||
#if (I2C_BUS_MAX > 4)
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED4)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SPEED4 CONFIG_SYS_OMAP24_I2C_SPEED
|
||||
#endif
|
||||
#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE4)
|
||||
#define CONFIG_SYS_OMAP24_I2C_SLAVE4 CONFIG_SYS_OMAP24_I2C_SLAVE
|
||||
#endif
|
||||
|
||||
U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe,
|
||||
omap24_i2c_read, omap24_i2c_write, NULL,
|
||||
CONFIG_SYS_OMAP24_I2C_SPEED4,
|
||||
CONFIG_SYS_OMAP24_I2C_SLAVE4,
|
||||
4)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
154
u-boot/drivers/i2c/omap24xx_i2c.h
Normal file
154
u-boot/drivers/i2c/omap24xx_i2c.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* (C) Copyright 2004-2010
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#ifndef _OMAP2PLUS_I2C_H_
|
||||
#define _OMAP2PLUS_I2C_H_
|
||||
|
||||
/* I2C masks */
|
||||
|
||||
/* I2C Interrupt Enable Register (I2C_IE): */
|
||||
#define I2C_IE_GC_IE (1 << 5)
|
||||
#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */
|
||||
#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */
|
||||
#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */
|
||||
#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Status Register (I2C_STAT): */
|
||||
|
||||
#define I2C_STAT_SBD (1 << 15) /* Single byte data */
|
||||
#define I2C_STAT_BB (1 << 12) /* Bus busy */
|
||||
#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */
|
||||
#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
|
||||
#define I2C_STAT_AAS (1 << 9) /* Address as slave */
|
||||
#define I2C_STAT_GC (1 << 5)
|
||||
#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
|
||||
#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */
|
||||
#define I2C_STAT_ARDY (1 << 2) /* Register access ready */
|
||||
#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Interrupt Code Register (I2C_INTCODE): */
|
||||
|
||||
#define I2C_INTCODE_MASK 7
|
||||
#define I2C_INTCODE_NONE 0
|
||||
#define I2C_INTCODE_AL 1 /* Arbitration lost */
|
||||
#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */
|
||||
#define I2C_INTCODE_ARDY 3 /* Register access ready */
|
||||
#define I2C_INTCODE_RRDY 4 /* Rcv data ready */
|
||||
#define I2C_INTCODE_XRDY 5 /* Xmit data ready */
|
||||
|
||||
/* I2C Buffer Configuration Register (I2C_BUF): */
|
||||
|
||||
#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */
|
||||
#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */
|
||||
|
||||
/* I2C Configuration Register (I2C_CON): */
|
||||
|
||||
#define I2C_CON_EN (1 << 15) /* I2C module enable */
|
||||
#define I2C_CON_BE (1 << 14) /* Big endian mode */
|
||||
#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */
|
||||
#define I2C_CON_MST (1 << 10) /* Master/slave mode */
|
||||
#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */
|
||||
/* (master mode only) */
|
||||
#define I2C_CON_XA (1 << 8) /* Expand address */
|
||||
#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */
|
||||
#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */
|
||||
|
||||
/* I2C System Test Register (I2C_SYSTEST): */
|
||||
|
||||
#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
|
||||
#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode, on brkpoint) */
|
||||
#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
|
||||
#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
|
||||
#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */
|
||||
#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */
|
||||
#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */
|
||||
#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */
|
||||
|
||||
/* I2C System Status Register (I2C_SYSS): */
|
||||
|
||||
#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */
|
||||
|
||||
#define I2C_SCLL_SCLL 0
|
||||
#define I2C_SCLL_SCLL_M 0xFF
|
||||
#define I2C_SCLL_HSSCLL 8
|
||||
#define I2C_SCLH_HSSCLL_M 0xFF
|
||||
#define I2C_SCLH_SCLH 0
|
||||
#define I2C_SCLH_SCLH_M 0xFF
|
||||
#define I2C_SCLH_HSSCLH 8
|
||||
#define I2C_SCLH_HSSCLH_M 0xFF
|
||||
|
||||
#define OMAP_I2C_STANDARD 100000
|
||||
#define OMAP_I2C_FAST_MODE 400000
|
||||
#define OMAP_I2C_HIGH_SPEED 3400000
|
||||
|
||||
#define SYSTEM_CLOCK_12 12000000
|
||||
#define SYSTEM_CLOCK_13 13000000
|
||||
#define SYSTEM_CLOCK_192 19200000
|
||||
#define SYSTEM_CLOCK_96 96000000
|
||||
|
||||
/* Use the reference value of 96MHz if not explicitly set by the board */
|
||||
#ifndef I2C_IP_CLK
|
||||
#define I2C_IP_CLK SYSTEM_CLOCK_96
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The reference minimum clock for high speed is 19.2MHz.
|
||||
* The linux 2.6.30 kernel uses this value.
|
||||
* The reference minimum clock for fast mode is 9.6MHz
|
||||
* The reference minimum clock for standard mode is 4MHz
|
||||
* In TRM, the value of 12MHz is used.
|
||||
*/
|
||||
#ifndef I2C_INTERNAL_SAMPLING_CLK
|
||||
#define I2C_INTERNAL_SAMPLING_CLK 19200000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The equation for the low and high time is
|
||||
* tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed
|
||||
* thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed
|
||||
*
|
||||
* If the duty cycle is 50%
|
||||
*
|
||||
* tlow = scll + scll_trim = sampling clock / (2 * speed)
|
||||
* thigh = sclh + sclh_trim = sampling clock / (2 * speed)
|
||||
*
|
||||
* In TRM
|
||||
* scll_trim = 7
|
||||
* sclh_trim = 5
|
||||
*
|
||||
* The linux 2.6.30 kernel uses
|
||||
* scll_trim = 6
|
||||
* sclh_trim = 6
|
||||
*
|
||||
* These are the trim values for standard and fast speed
|
||||
*/
|
||||
#ifndef I2C_FASTSPEED_SCLL_TRIM
|
||||
#define I2C_FASTSPEED_SCLL_TRIM 6
|
||||
#endif
|
||||
#ifndef I2C_FASTSPEED_SCLH_TRIM
|
||||
#define I2C_FASTSPEED_SCLH_TRIM 6
|
||||
#endif
|
||||
|
||||
/* These are the trim values for high speed */
|
||||
#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM
|
||||
#endif
|
||||
|
||||
#define I2C_PSC_MAX 0x0f
|
||||
#define I2C_PSC_MIN 0x00
|
||||
|
||||
#endif /* _OMAP24XX_I2C_H_ */
|
||||
176
u-boot/drivers/i2c/pca9564_i2c.c
Normal file
176
u-boot/drivers/i2c/pca9564_i2c.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* File: drivers/i2c/pca9564.c
|
||||
* Based on: drivers/i2c/s3c44b0_i2c.c
|
||||
* Author:
|
||||
*
|
||||
* Created: 2009-06-23
|
||||
* Description: PCA9564 i2c bridge driver
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2009 CJSC "NII STT", http://www.niistt.ru/
|
||||
*
|
||||
* Bugs:
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <pca9564.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define PCA_STA (CONFIG_PCA9564_BASE + 0)
|
||||
#define PCA_TO (CONFIG_PCA9564_BASE + 0)
|
||||
#define PCA_DAT (CONFIG_PCA9564_BASE + (1 << 2))
|
||||
#define PCA_ADR (CONFIG_PCA9564_BASE + (2 << 2))
|
||||
#define PCA_CON (CONFIG_PCA9564_BASE + (3 << 2))
|
||||
|
||||
static unsigned char pca_read_reg(unsigned int reg)
|
||||
{
|
||||
return readb((void *)reg);
|
||||
}
|
||||
|
||||
static void pca_write_reg(unsigned int reg, unsigned char value)
|
||||
{
|
||||
writeb(value, (void *)reg);
|
||||
}
|
||||
|
||||
static int pca_wait_busy(void)
|
||||
{
|
||||
unsigned int timeout = 10000;
|
||||
|
||||
while (!(pca_read_reg(PCA_CON) & PCA_CON_SI) && --timeout)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == 0)
|
||||
debug("I2C timeout!\n");
|
||||
|
||||
debug("CON = 0x%02x, STA = 0x%02x\n", pca_read_reg(PCA_CON),
|
||||
pca_read_reg(PCA_STA));
|
||||
|
||||
return timeout ? 0 : 1;
|
||||
}
|
||||
|
||||
/*=====================================================================*/
|
||||
/* Public Functions */
|
||||
/*=====================================================================*/
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
unsigned char res;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO);
|
||||
pca_wait_busy();
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1) | 1);
|
||||
res = pca_wait_busy();
|
||||
|
||||
if ((res == 0) && (pca_read_reg(PCA_STA) == 0x48))
|
||||
res = 1;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/Write interface:
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Where to read/write the data
|
||||
* len: How many bytes to read/write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1));
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
if (alen > 0) {
|
||||
pca_write_reg(PCA_DAT, addr);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO);
|
||||
|
||||
udelay(500);
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1) | 1);
|
||||
pca_wait_busy();
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (i == len - 1)
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
else
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_AA);
|
||||
|
||||
pca_wait_busy();
|
||||
buffer[i] = pca_read_reg(PCA_DAT);
|
||||
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, chip << 1);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
if (alen > 0) {
|
||||
pca_write_reg(PCA_DAT, addr);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
pca_write_reg(PCA_DAT, buffer[i]);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
441
u-boot/drivers/i2c/ppc4xx_i2c.c
Normal file
441
u-boot/drivers/i2c/ppc4xx_i2c.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* (C) Copyright 2007-2009
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* based on work by Anne Sophie Harnois <anne-sophie.harnois@nextream.fr>
|
||||
*
|
||||
* (C) Copyright 2001
|
||||
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/ppc4xx.h>
|
||||
#include <asm/ppc4xx-i2c.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static inline struct ppc4xx_i2c *ppc4xx_get_i2c(int hwadapnr)
|
||||
{
|
||||
unsigned long base;
|
||||
|
||||
#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \
|
||||
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
|
||||
defined(CONFIG_460EX) || defined(CONFIG_460GT)
|
||||
base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000700 + (hwadapnr * 0x100);
|
||||
#elif defined(CONFIG_440) || defined(CONFIG_405EX)
|
||||
/* all remaining 440 variants */
|
||||
base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000400 + (hwadapnr * 0x100);
|
||||
#else
|
||||
/* all 405 variants */
|
||||
base = 0xEF600500 + (hwadapnr * 0x100);
|
||||
#endif
|
||||
return (struct ppc4xx_i2c *)base;
|
||||
}
|
||||
|
||||
static void _i2c_bus_reset(struct i2c_adapter *adap)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr);
|
||||
int i;
|
||||
u8 dc;
|
||||
|
||||
/* Reset status register */
|
||||
/* write 1 in SCMP and IRQA to clear these fields */
|
||||
out_8(&i2c->sts, 0x0A);
|
||||
|
||||
/* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */
|
||||
out_8(&i2c->extsts, 0x8F);
|
||||
|
||||
/* Place chip in the reset state */
|
||||
out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST);
|
||||
|
||||
/* Check if bus is free */
|
||||
dc = in_8(&i2c->directcntl);
|
||||
if (!DIRCTNL_FREE(dc)){
|
||||
/* Try to set bus free state */
|
||||
out_8(&i2c->directcntl, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC);
|
||||
|
||||
/* Wait until we regain bus control */
|
||||
for (i = 0; i < 100; ++i) {
|
||||
dc = in_8(&i2c->directcntl);
|
||||
if (DIRCTNL_FREE(dc))
|
||||
break;
|
||||
|
||||
/* Toggle SCL line */
|
||||
dc ^= IIC_DIRCNTL_SCC;
|
||||
out_8(&i2c->directcntl, dc);
|
||||
udelay(10);
|
||||
dc ^= IIC_DIRCNTL_SCC;
|
||||
out_8(&i2c->directcntl, dc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove reset */
|
||||
out_8(&i2c->xtcntlss, 0);
|
||||
}
|
||||
|
||||
static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr);
|
||||
int val, divisor;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/*
|
||||
* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
|
||||
/* Handle possible failed I2C state */
|
||||
/* FIXME: put this into i2c_init_board()? */
|
||||
_i2c_bus_reset(adap);
|
||||
|
||||
/* clear lo master address */
|
||||
out_8(&i2c->lmadr, 0);
|
||||
|
||||
/* clear hi master address */
|
||||
out_8(&i2c->hmadr, 0);
|
||||
|
||||
/* clear lo slave address */
|
||||
out_8(&i2c->lsadr, 0);
|
||||
|
||||
/* clear hi slave address */
|
||||
out_8(&i2c->hsadr, 0);
|
||||
|
||||
/* Clock divide Register */
|
||||
/* set divisor according to freq_opb */
|
||||
divisor = (get_OPB_freq() - 1) / 10000000;
|
||||
if (divisor == 0)
|
||||
divisor = 1;
|
||||
out_8(&i2c->clkdiv, divisor);
|
||||
|
||||
/* no interrupts */
|
||||
out_8(&i2c->intrmsk, 0);
|
||||
|
||||
/* clear transfer count */
|
||||
out_8(&i2c->xfrcnt, 0);
|
||||
|
||||
/* clear extended control & stat */
|
||||
/* write 1 in SRC SRS SWC SWS to clear these fields */
|
||||
out_8(&i2c->xtcntlss, 0xF0);
|
||||
|
||||
/* Mode Control Register
|
||||
Flush Slave/Master data buffer */
|
||||
out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB);
|
||||
|
||||
val = in_8(&i2c->mdcntl);
|
||||
|
||||
/* Ignore General Call, slave transfers are ignored,
|
||||
* disable interrupts, exit unknown bus state, enable hold
|
||||
* SCL 100kHz normaly or FastMode for 400kHz and above
|
||||
*/
|
||||
|
||||
val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL;
|
||||
if (speed >= 400000)
|
||||
val |= IIC_MDCNTL_FSM;
|
||||
out_8(&i2c->mdcntl, val);
|
||||
|
||||
/* clear control reg */
|
||||
out_8(&i2c->cntl, 0x00);
|
||||
}
|
||||
|
||||
/*
|
||||
* This code tries to use the features of the 405GP i2c
|
||||
* controller. It will transfer up to 4 bytes in one pass
|
||||
* on the loop. It only does out_8((u8 *)lbz) to the buffer when it
|
||||
* is possible to do out16(lhz) transfers.
|
||||
*
|
||||
* cmd_type is 0 for write 1 for read.
|
||||
*
|
||||
* addr_len can take any value from 0-255, it is only limited
|
||||
* by the char, we could make it larger if needed. If it is
|
||||
* 0 we skip the address write cycle.
|
||||
*
|
||||
* Typical case is a Write of an addr followd by a Read. The
|
||||
* IBM FAQ does not cover this. On the last byte of the write
|
||||
* we don't set the creg CHT bit but the RPST bit.
|
||||
*
|
||||
* It does not support address only transfers, there must be
|
||||
* a data part. If you want to write the address yourself, put
|
||||
* it in the data pointer.
|
||||
*
|
||||
* It does not support transfer to/from address 0.
|
||||
*
|
||||
* It does not check XFRCNT.
|
||||
*/
|
||||
static int _i2c_transfer(struct i2c_adapter *adap,
|
||||
unsigned char cmd_type,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char addr_len,
|
||||
unsigned char data[],
|
||||
unsigned short data_len)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr);
|
||||
u8 *ptr;
|
||||
int reading;
|
||||
int tran, cnt;
|
||||
int result;
|
||||
int status;
|
||||
int i;
|
||||
u8 creg;
|
||||
|
||||
if (data == 0 || data_len == 0) {
|
||||
/* Don't support data transfer of no length or to address 0 */
|
||||
printf( "i2c_transfer: bad call\n" );
|
||||
return IIC_NOK;
|
||||
}
|
||||
if (addr && addr_len) {
|
||||
ptr = addr;
|
||||
cnt = addr_len;
|
||||
reading = 0;
|
||||
} else {
|
||||
ptr = data;
|
||||
cnt = data_len;
|
||||
reading = cmd_type;
|
||||
}
|
||||
|
||||
/* Clear Stop Complete Bit */
|
||||
out_8(&i2c->sts, IIC_STS_SCMP);
|
||||
|
||||
/* Check init */
|
||||
i = 10;
|
||||
do {
|
||||
/* Get status */
|
||||
status = in_8(&i2c->sts);
|
||||
i--;
|
||||
} while ((status & IIC_STS_PT) && (i > 0));
|
||||
|
||||
if (status & IIC_STS_PT) {
|
||||
result = IIC_NOK_TOUT;
|
||||
return(result);
|
||||
}
|
||||
|
||||
/* flush the Master/Slave Databuffers */
|
||||
out_8(&i2c->mdcntl, in_8(&i2c->mdcntl) |
|
||||
IIC_MDCNTL_FMDB | IIC_MDCNTL_FSDB);
|
||||
|
||||
/* need to wait 4 OPB clocks? code below should take that long */
|
||||
|
||||
/* 7-bit adressing */
|
||||
out_8(&i2c->hmadr, 0);
|
||||
out_8(&i2c->lmadr, chip);
|
||||
|
||||
tran = 0;
|
||||
result = IIC_OK;
|
||||
creg = 0;
|
||||
|
||||
while (tran != cnt && (result == IIC_OK)) {
|
||||
int bc,j;
|
||||
|
||||
/*
|
||||
* Control register =
|
||||
* Normal transfer, 7-bits adressing, Transfer up to
|
||||
* bc bytes, Normal start, Transfer is a sequence of transfers
|
||||
*/
|
||||
creg |= IIC_CNTL_PT;
|
||||
|
||||
bc = (cnt - tran) > 4 ? 4 : cnt - tran;
|
||||
creg |= (bc - 1) << 4;
|
||||
/* if the real cmd type is write continue trans */
|
||||
if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt))
|
||||
creg |= IIC_CNTL_CHT;
|
||||
|
||||
/* last part of address, prepare for repeated start on read */
|
||||
if (cmd_type && (ptr == addr) && ((tran + bc) == cnt))
|
||||
creg |= IIC_CNTL_RPST;
|
||||
|
||||
if (reading) {
|
||||
creg |= IIC_CNTL_READ;
|
||||
} else {
|
||||
for(j = 0; j < bc; j++) {
|
||||
/* Set buffer */
|
||||
out_8(&i2c->mdbuf, ptr[tran + j]);
|
||||
}
|
||||
}
|
||||
out_8(&i2c->cntl, creg);
|
||||
|
||||
/*
|
||||
* Transfer is in progress
|
||||
* we have to wait for upto 5 bytes of data
|
||||
* 1 byte chip address+r/w bit then bc bytes
|
||||
* of data.
|
||||
* udelay(10) is 1 bit time at 100khz
|
||||
* Doubled for slop. 20 is too small.
|
||||
*/
|
||||
i = 2 * 5 * 8;
|
||||
do {
|
||||
/* Get status */
|
||||
status = in_8(&i2c->sts);
|
||||
udelay(10);
|
||||
i--;
|
||||
} while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) &&
|
||||
(i > 0));
|
||||
|
||||
if (status & IIC_STS_ERR) {
|
||||
result = IIC_NOK;
|
||||
status = in_8(&i2c->extsts);
|
||||
/* Lost arbitration? */
|
||||
if (status & IIC_EXTSTS_LA)
|
||||
result = IIC_NOK_LA;
|
||||
/* Incomplete transfer? */
|
||||
if (status & IIC_EXTSTS_ICT)
|
||||
result = IIC_NOK_ICT;
|
||||
/* Transfer aborted? */
|
||||
if (status & IIC_EXTSTS_XFRA)
|
||||
result = IIC_NOK_XFRA;
|
||||
/* Is bus free?
|
||||
* If error happened during combined xfer
|
||||
* IIC interface is usually stuck in some strange
|
||||
* state without a valid stop condition.
|
||||
* Brute, but working: generate stop, then soft reset.
|
||||
*/
|
||||
if ((status & IIC_EXTSTS_BCS_MASK)
|
||||
!= IIC_EXTSTS_BCS_FREE){
|
||||
u8 mdcntl = in_8(&i2c->mdcntl);
|
||||
|
||||
/* Generate valid stop condition */
|
||||
out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST);
|
||||
out_8(&i2c->directcntl, IIC_DIRCNTL_SCC);
|
||||
udelay(10);
|
||||
out_8(&i2c->directcntl,
|
||||
IIC_DIRCNTL_SCC | IIC_DIRCNTL_SDAC);
|
||||
out_8(&i2c->xtcntlss, 0);
|
||||
|
||||
ppc4xx_i2c_init(adap, (mdcntl & IIC_MDCNTL_FSM)
|
||||
? 400000 : 100000, 0);
|
||||
}
|
||||
} else if ( status & IIC_STS_PT) {
|
||||
result = IIC_NOK_TOUT;
|
||||
}
|
||||
|
||||
/* Command is reading => get buffer */
|
||||
if ((reading) && (result == IIC_OK)) {
|
||||
/* Are there data in buffer */
|
||||
if (status & IIC_STS_MDBS) {
|
||||
/*
|
||||
* even if we have data we have to wait 4OPB
|
||||
* clocks for it to hit the front of the FIFO,
|
||||
* after that we can just read. We should check
|
||||
* XFCNT here and if the FIFO is full there is
|
||||
* no need to wait.
|
||||
*/
|
||||
udelay(1);
|
||||
for (j = 0; j < bc; j++)
|
||||
ptr[tran + j] = in_8(&i2c->mdbuf);
|
||||
} else
|
||||
result = IIC_NOK_DATA;
|
||||
}
|
||||
creg = 0;
|
||||
tran += bc;
|
||||
if (ptr == addr && tran == cnt) {
|
||||
ptr = data;
|
||||
cnt = data_len;
|
||||
tran = 0;
|
||||
reading = cmd_type;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ppc4xx_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
||||
{
|
||||
uchar buf[1];
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
/*
|
||||
* What is needed is to send the chip address and verify that the
|
||||
* address was <ACK>ed (i.e. there was a chip at that address which
|
||||
* drove the data line low).
|
||||
*/
|
||||
return (_i2c_transfer(adap, 1, chip << 1, 0, 0, buf, 1) != 0);
|
||||
}
|
||||
|
||||
static int ppc4xx_i2c_transfer(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len, int read)
|
||||
{
|
||||
uchar xaddr[4];
|
||||
int ret;
|
||||
|
||||
if (alen > 4) {
|
||||
printf("I2C: 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;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
ret = _i2c_transfer(adap, read, chip << 1, &xaddr[4 - alen], alen,
|
||||
buffer, len);
|
||||
if (ret) {
|
||||
printf("I2C %s: failed %d\n", read ? "read" : "write", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppc4xx_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 1);
|
||||
}
|
||||
|
||||
static int ppc4xx_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 0);
|
||||
}
|
||||
|
||||
static unsigned int ppc4xx_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
if (speed != adap->speed)
|
||||
return -1;
|
||||
return speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register ppc4xx i2c adapters
|
||||
*/
|
||||
#ifdef CONFIG_SYS_I2C_PPC4XX_CH0
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_0, ppc4xx_i2c_init, ppc4xx_i2c_probe,
|
||||
ppc4xx_i2c_read, ppc4xx_i2c_write,
|
||||
ppc4xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_PPC4XX_SPEED_0,
|
||||
CONFIG_SYS_I2C_PPC4XX_SLAVE_0, 0)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_PPC4XX_CH1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_1, ppc4xx_i2c_init, ppc4xx_i2c_probe,
|
||||
ppc4xx_i2c_read, ppc4xx_i2c_write,
|
||||
ppc4xx_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_PPC4XX_SPEED_1,
|
||||
CONFIG_SYS_I2C_PPC4XX_SLAVE_1, 1)
|
||||
#endif
|
||||
290
u-boot/drivers/i2c/rcar_i2c.c
Normal file
290
u-boot/drivers/i2c/rcar_i2c.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* drivers/i2c/rcar_i2c.c
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Electronics Corporation
|
||||
* Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct rcar_i2c {
|
||||
u32 icscr;
|
||||
u32 icmcr;
|
||||
u32 icssr;
|
||||
u32 icmsr;
|
||||
u32 icsier;
|
||||
u32 icmier;
|
||||
u32 icccr;
|
||||
u32 icsar;
|
||||
u32 icmar;
|
||||
u32 icrxdtxd;
|
||||
u32 icccr2;
|
||||
u32 icmpr;
|
||||
u32 ichpr;
|
||||
u32 iclpr;
|
||||
};
|
||||
|
||||
#define MCR_MDBS 0x80 /* non-fifo mode switch */
|
||||
#define MCR_FSCL 0x40 /* override SCL pin */
|
||||
#define MCR_FSDA 0x20 /* override SDA pin */
|
||||
#define MCR_OBPC 0x10 /* override pins */
|
||||
#define MCR_MIE 0x08 /* master if enable */
|
||||
#define MCR_TSBE 0x04
|
||||
#define MCR_FSB 0x02 /* force stop bit */
|
||||
#define MCR_ESG 0x01 /* en startbit gen. */
|
||||
|
||||
#define MSR_MASK 0x7f
|
||||
#define MSR_MNR 0x40 /* nack received */
|
||||
#define MSR_MAL 0x20 /* arbitration lost */
|
||||
#define MSR_MST 0x10 /* sent a stop */
|
||||
#define MSR_MDE 0x08
|
||||
#define MSR_MDT 0x04
|
||||
#define MSR_MDR 0x02
|
||||
#define MSR_MAT 0x01 /* slave addr xfer done */
|
||||
|
||||
static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = {
|
||||
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE,
|
||||
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE,
|
||||
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE,
|
||||
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE,
|
||||
};
|
||||
|
||||
static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr)
|
||||
{
|
||||
/* set slave address */
|
||||
writel(chip << 1, &dev->icmar);
|
||||
/* set register address */
|
||||
writel(addr, &dev->icrxdtxd);
|
||||
/* clear status */
|
||||
writel(0, &dev->icmsr);
|
||||
/* start master send */
|
||||
writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
|
||||
|
||||
while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE))
|
||||
!= (MSR_MAT | MSR_MDE))
|
||||
udelay(10);
|
||||
|
||||
/* clear ESG */
|
||||
writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
|
||||
/* start SCLclk */
|
||||
writel(~(MSR_MAT | MSR_MDE), &dev->icmsr);
|
||||
|
||||
while (!(readl(&dev->icmsr) & MSR_MDE))
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev)
|
||||
{
|
||||
while (!(readl(&dev->icmsr) & MSR_MST))
|
||||
udelay(10);
|
||||
|
||||
writel(0, &dev->icmcr);
|
||||
}
|
||||
|
||||
static int
|
||||
rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size)
|
||||
{
|
||||
rcar_i2c_raw_rw_common(dev, chip, addr);
|
||||
|
||||
/* set send date */
|
||||
writel(*val, &dev->icrxdtxd);
|
||||
/* start SCLclk */
|
||||
writel(~MSR_MDE, &dev->icmsr);
|
||||
|
||||
while (!(readl(&dev->icmsr) & MSR_MDE))
|
||||
udelay(10);
|
||||
|
||||
/* set stop condition */
|
||||
writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
|
||||
/* start SCLclk */
|
||||
writel(~MSR_MDE, &dev->icmsr);
|
||||
|
||||
rcar_i2c_raw_rw_finish(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8
|
||||
rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
rcar_i2c_raw_rw_common(dev, chip, addr);
|
||||
|
||||
/* set slave address, receive */
|
||||
writel((chip << 1) | 1, &dev->icmar);
|
||||
/* start master receive */
|
||||
writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
|
||||
/* clear status */
|
||||
writel(0, &dev->icmsr);
|
||||
|
||||
while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR))
|
||||
!= (MSR_MAT | MSR_MDR))
|
||||
udelay(10);
|
||||
|
||||
/* clear ESG */
|
||||
writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
|
||||
/* prepare stop condition */
|
||||
writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
|
||||
/* start SCLclk */
|
||||
writel(~(MSR_MAT | MSR_MDR), &dev->icmsr);
|
||||
|
||||
while (!(readl(&dev->icmsr) & MSR_MDR))
|
||||
udelay(10);
|
||||
|
||||
/* get receive data */
|
||||
ret = (u8)readl(&dev->icrxdtxd);
|
||||
/* start SCLclk */
|
||||
writel(~MSR_MDR, &dev->icmsr);
|
||||
|
||||
rcar_i2c_raw_rw_finish(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCL = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck])
|
||||
* iicck : I2C internal clock < 20 MHz
|
||||
* ticf : I2C SCL falling time: 35 ns
|
||||
* tr : I2C SCL rising time: 200 ns
|
||||
* intd : LSI internal delay: I2C0: 50 ns I2C1-3: 5
|
||||
* F[n] : n rounded up to an integer
|
||||
*/
|
||||
static u32 rcar_clock_gen(int i2c_no, u32 bus_speed)
|
||||
{
|
||||
u32 iicck, f, scl, scgd;
|
||||
u32 intd = 5;
|
||||
|
||||
int bit = 0, cdf_width = 3;
|
||||
for (bit = 0; bit < (1 << cdf_width); bit++) {
|
||||
iicck = CONFIG_HP_CLK_FREQ / (1 + bit);
|
||||
if (iicck < 20000000)
|
||||
break;
|
||||
}
|
||||
|
||||
if (bit > (1 << cdf_width)) {
|
||||
puts("rcar-i2c: Can not get CDF\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i2c_no == 0)
|
||||
intd = 50;
|
||||
|
||||
f = (35 + 200 + intd) * (iicck / 1000000000);
|
||||
|
||||
for (scgd = 0; scgd < 0x40; scgd++) {
|
||||
scl = iicck / (20 + (scgd * 8) + f);
|
||||
if (scl <= bus_speed)
|
||||
break;
|
||||
}
|
||||
|
||||
if (scgd > 0x40) {
|
||||
puts("rcar-i2c: Can not get SDGB\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("%s: scl: %d\n", __func__, scl);
|
||||
debug("%s: bit %x\n", __func__, bit);
|
||||
debug("%s: scgd %x\n", __func__, scgd);
|
||||
debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit));
|
||||
|
||||
return scgd << (cdf_width) | bit;
|
||||
}
|
||||
|
||||
static void
|
||||
rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
||||
u32 icccr = 0;
|
||||
|
||||
/* No i2c support prior to relocation */
|
||||
if (!(gd->flags & GD_FLG_RELOC))
|
||||
return;
|
||||
|
||||
/*
|
||||
* reset slave mode.
|
||||
* slave mode is not used on this driver
|
||||
*/
|
||||
writel(0, &dev->icsier);
|
||||
writel(0, &dev->icsar);
|
||||
writel(0, &dev->icscr);
|
||||
writel(0, &dev->icssr);
|
||||
|
||||
/* reset master mode */
|
||||
writel(0, &dev->icmier);
|
||||
writel(0, &dev->icmcr);
|
||||
writel(0, &dev->icmsr);
|
||||
writel(0, &dev->icmar);
|
||||
|
||||
icccr = rcar_clock_gen(adap->hwadapnr, adap->speed);
|
||||
if (icccr == 0)
|
||||
puts("I2C: Init failed\n");
|
||||
else
|
||||
writel(icccr, &dev->icccr);
|
||||
}
|
||||
|
||||
static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, u8 *data, int len)
|
||||
{
|
||||
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
data[i] = rcar_i2c_raw_read(dev, chip, addr + i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr,
|
||||
int alen, u8 *data, int len)
|
||||
{
|
||||
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
||||
return rcar_i2c_raw_write(dev, chip, addr, data, len);
|
||||
}
|
||||
|
||||
static int
|
||||
rcar_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
return rcar_i2c_read(adap, dev, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
||||
u32 icccr;
|
||||
int ret = 0;
|
||||
|
||||
rcar_i2c_raw_rw_finish(dev);
|
||||
|
||||
icccr = rcar_clock_gen(adap->hwadapnr, speed);
|
||||
if (icccr == 0) {
|
||||
puts("I2C: Init failed\n");
|
||||
ret = -1;
|
||||
} else {
|
||||
writel(icccr, &dev->icccr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register RCAR i2c adapters
|
||||
*/
|
||||
U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
||||
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
||||
CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
||||
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
||||
CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
||||
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
||||
CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
||||
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
||||
CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3)
|
||||
395
u-boot/drivers/i2c/rk_i2c.c
Normal file
395
u-boot/drivers/i2c/rk_i2c.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* (C) Copyright 2015 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2008-2014 Rockchip Electronics
|
||||
* Peter, Software Engineering, <superpeter.cai@gmail.com>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/i2c.h>
|
||||
#include <asm/arch/periph.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* i2c timerout */
|
||||
#define I2C_TIMEOUT_MS 100
|
||||
#define I2C_RETRY_COUNT 3
|
||||
|
||||
/* rk i2c fifo max transfer bytes */
|
||||
#define RK_I2C_FIFO_SIZE 32
|
||||
|
||||
struct rk_i2c {
|
||||
struct clk clk;
|
||||
struct i2c_regs *regs;
|
||||
unsigned int speed;
|
||||
};
|
||||
|
||||
static inline void rk_i2c_get_div(int div, int *divh, int *divl)
|
||||
{
|
||||
*divl = div / 2;
|
||||
if (div % 2 == 0)
|
||||
*divh = div / 2;
|
||||
else
|
||||
*divh = DIV_ROUND_UP(div, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* SCL Divisor = 8 * (CLKDIVL+1 + CLKDIVH+1)
|
||||
* SCL = PCLK / SCLK Divisor
|
||||
* i2c_rate = PCLK
|
||||
*/
|
||||
static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
|
||||
{
|
||||
uint32_t i2c_rate;
|
||||
int div, divl, divh;
|
||||
|
||||
/* First get i2c rate from pclk */
|
||||
i2c_rate = clk_get_rate(&i2c->clk);
|
||||
|
||||
div = DIV_ROUND_UP(i2c_rate, scl_rate * 8) - 2;
|
||||
divh = 0;
|
||||
divl = 0;
|
||||
if (div >= 0)
|
||||
rk_i2c_get_div(div, &divh, &divl);
|
||||
writel(I2C_CLKDIV_VAL(divl, divh), &i2c->regs->clkdiv);
|
||||
|
||||
debug("rk_i2c_set_clk: i2c rate = %d, scl rate = %d\n", i2c_rate,
|
||||
scl_rate);
|
||||
debug("set i2c clk div = %d, divh = %d, divl = %d\n", div, divh, divl);
|
||||
debug("set clk(I2C_CLKDIV: 0x%08x)\n", readl(&i2c->regs->clkdiv));
|
||||
}
|
||||
|
||||
static void rk_i2c_show_regs(struct i2c_regs *regs)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
uint i;
|
||||
|
||||
debug("i2c_con: 0x%08x\n", readl(®s->con));
|
||||
debug("i2c_clkdiv: 0x%08x\n", readl(®s->clkdiv));
|
||||
debug("i2c_mrxaddr: 0x%08x\n", readl(®s->mrxaddr));
|
||||
debug("i2c_mrxraddR: 0x%08x\n", readl(®s->mrxraddr));
|
||||
debug("i2c_mtxcnt: 0x%08x\n", readl(®s->mtxcnt));
|
||||
debug("i2c_mrxcnt: 0x%08x\n", readl(®s->mrxcnt));
|
||||
debug("i2c_ien: 0x%08x\n", readl(®s->ien));
|
||||
debug("i2c_ipd: 0x%08x\n", readl(®s->ipd));
|
||||
debug("i2c_fcnt: 0x%08x\n", readl(®s->fcnt));
|
||||
for (i = 0; i < 8; i++)
|
||||
debug("i2c_txdata%d: 0x%08x\n", i, readl(®s->txdata[i]));
|
||||
for (i = 0; i < 8; i++)
|
||||
debug("i2c_rxdata%d: 0x%08x\n", i, readl(®s->rxdata[i]));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int rk_i2c_send_start_bit(struct rk_i2c *i2c)
|
||||
{
|
||||
struct i2c_regs *regs = i2c->regs;
|
||||
ulong start;
|
||||
|
||||
debug("I2c Send Start bit.\n");
|
||||
writel(I2C_IPD_ALL_CLEAN, ®s->ipd);
|
||||
|
||||
writel(I2C_CON_EN | I2C_CON_START, ®s->con);
|
||||
writel(I2C_STARTIEN, ®s->ien);
|
||||
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
if (readl(®s->ipd) & I2C_STARTIPD) {
|
||||
writel(I2C_STARTIPD, ®s->ipd);
|
||||
break;
|
||||
}
|
||||
if (get_timer(start) > I2C_TIMEOUT_MS) {
|
||||
debug("I2C Send Start Bit Timeout\n");
|
||||
rk_i2c_show_regs(regs);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk_i2c_send_stop_bit(struct rk_i2c *i2c)
|
||||
{
|
||||
struct i2c_regs *regs = i2c->regs;
|
||||
ulong start;
|
||||
|
||||
debug("I2c Send Stop bit.\n");
|
||||
writel(I2C_IPD_ALL_CLEAN, ®s->ipd);
|
||||
|
||||
writel(I2C_CON_EN | I2C_CON_STOP, ®s->con);
|
||||
writel(I2C_CON_STOP, ®s->ien);
|
||||
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
if (readl(®s->ipd) & I2C_STOPIPD) {
|
||||
writel(I2C_STOPIPD, ®s->ipd);
|
||||
break;
|
||||
}
|
||||
if (get_timer(start) > I2C_TIMEOUT_MS) {
|
||||
debug("I2C Send Start Bit Timeout\n");
|
||||
rk_i2c_show_regs(regs);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rk_i2c_disable(struct rk_i2c *i2c)
|
||||
{
|
||||
writel(0, &i2c->regs->con);
|
||||
}
|
||||
|
||||
static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
|
||||
uchar *buf, uint b_len)
|
||||
{
|
||||
struct i2c_regs *regs = i2c->regs;
|
||||
uchar *pbuf = buf;
|
||||
uint bytes_remain_len = b_len;
|
||||
uint bytes_xferred = 0;
|
||||
uint words_xferred = 0;
|
||||
ulong start;
|
||||
uint con = 0;
|
||||
uint rxdata;
|
||||
uint i, j;
|
||||
int err;
|
||||
|
||||
debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
|
||||
chip, reg, r_len, b_len);
|
||||
|
||||
err = rk_i2c_send_start_bit(i2c);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
writel(I2C_MRXADDR_SET(1, chip << 1 | 1), ®s->mrxaddr);
|
||||
if (r_len == 0) {
|
||||
writel(0, ®s->mrxraddr);
|
||||
} else if (r_len < 4) {
|
||||
writel(I2C_MRXRADDR_SET(r_len, reg), ®s->mrxraddr);
|
||||
} else {
|
||||
debug("I2C Read: addr len %d not supported\n", r_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (bytes_remain_len) {
|
||||
if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
|
||||
con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
|
||||
bytes_xferred = 32;
|
||||
} else {
|
||||
con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
|
||||
I2C_CON_LASTACK;
|
||||
bytes_xferred = bytes_remain_len;
|
||||
}
|
||||
words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
|
||||
|
||||
writel(con, ®s->con);
|
||||
writel(bytes_xferred, ®s->mrxcnt);
|
||||
writel(I2C_MBRFIEN | I2C_NAKRCVIEN, ®s->ien);
|
||||
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
if (readl(®s->ipd) & I2C_NAKRCVIPD) {
|
||||
writel(I2C_NAKRCVIPD, ®s->ipd);
|
||||
err = -EREMOTEIO;
|
||||
}
|
||||
if (readl(®s->ipd) & I2C_MBRFIPD) {
|
||||
writel(I2C_MBRFIPD, ®s->ipd);
|
||||
break;
|
||||
}
|
||||
if (get_timer(start) > I2C_TIMEOUT_MS) {
|
||||
debug("I2C Read Data Timeout\n");
|
||||
err = -ETIMEDOUT;
|
||||
rk_i2c_show_regs(regs);
|
||||
goto i2c_exit;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < words_xferred; i++) {
|
||||
rxdata = readl(®s->rxdata[i]);
|
||||
debug("I2c Read RXDATA[%d] = 0x%x\n", i, rxdata);
|
||||
for (j = 0; j < 4; j++) {
|
||||
if ((i * 4 + j) == bytes_xferred)
|
||||
break;
|
||||
*pbuf++ = (rxdata >> (j * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_remain_len -= bytes_xferred;
|
||||
debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
|
||||
}
|
||||
|
||||
i2c_exit:
|
||||
rk_i2c_send_stop_bit(i2c);
|
||||
rk_i2c_disable(i2c);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
|
||||
uchar *buf, uint b_len)
|
||||
{
|
||||
struct i2c_regs *regs = i2c->regs;
|
||||
int err;
|
||||
uchar *pbuf = buf;
|
||||
uint bytes_remain_len = b_len + r_len + 1;
|
||||
uint bytes_xferred = 0;
|
||||
uint words_xferred = 0;
|
||||
ulong start;
|
||||
uint txdata;
|
||||
uint i, j;
|
||||
|
||||
debug("rk_i2c_write: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
|
||||
chip, reg, r_len, b_len);
|
||||
err = rk_i2c_send_start_bit(i2c);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
while (bytes_remain_len) {
|
||||
if (bytes_remain_len > RK_I2C_FIFO_SIZE)
|
||||
bytes_xferred = 32;
|
||||
else
|
||||
bytes_xferred = bytes_remain_len;
|
||||
words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
|
||||
|
||||
for (i = 0; i < words_xferred; i++) {
|
||||
txdata = 0;
|
||||
for (j = 0; j < 4; j++) {
|
||||
if ((i * 4 + j) == bytes_xferred)
|
||||
break;
|
||||
|
||||
if (i == 0 && j == 0) {
|
||||
txdata |= (chip << 1);
|
||||
} else if (i == 0 && j <= r_len) {
|
||||
txdata |= (reg &
|
||||
(0xff << ((j - 1) * 8))) << 8;
|
||||
} else {
|
||||
txdata |= (*pbuf++)<<(j * 8);
|
||||
}
|
||||
writel(txdata, ®s->txdata[i]);
|
||||
}
|
||||
debug("I2c Write TXDATA[%d] = 0x%x\n", i, txdata);
|
||||
}
|
||||
|
||||
writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), ®s->con);
|
||||
writel(bytes_xferred, ®s->mtxcnt);
|
||||
writel(I2C_MBTFIEN | I2C_NAKRCVIEN, ®s->ien);
|
||||
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
if (readl(®s->ipd) & I2C_NAKRCVIPD) {
|
||||
writel(I2C_NAKRCVIPD, ®s->ipd);
|
||||
err = -EREMOTEIO;
|
||||
}
|
||||
if (readl(®s->ipd) & I2C_MBTFIPD) {
|
||||
writel(I2C_MBTFIPD, ®s->ipd);
|
||||
break;
|
||||
}
|
||||
if (get_timer(start) > I2C_TIMEOUT_MS) {
|
||||
debug("I2C Write Data Timeout\n");
|
||||
err = -ETIMEDOUT;
|
||||
rk_i2c_show_regs(regs);
|
||||
goto i2c_exit;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
bytes_remain_len -= bytes_xferred;
|
||||
debug("I2C Write bytes_remain_len %d\n", bytes_remain_len);
|
||||
}
|
||||
|
||||
i2c_exit:
|
||||
rk_i2c_send_stop_bit(i2c);
|
||||
rk_i2c_disable(i2c);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rockchip_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct rk_i2c *i2c = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = rk_i2c_read(i2c, msg->addr, 0, 0, msg->buf,
|
||||
msg->len);
|
||||
} else {
|
||||
ret = rk_i2c_write(i2c, msg->addr, 0, 0, msg->buf,
|
||||
msg->len);
|
||||
}
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rockchip_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
||||
{
|
||||
struct rk_i2c *i2c = dev_get_priv(bus);
|
||||
|
||||
rk_i2c_set_clk(i2c, speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_i2c_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct rk_i2c *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(bus, 0, &priv->clk);
|
||||
if (ret < 0) {
|
||||
debug("%s: Could not get clock for %s: %d\n", __func__,
|
||||
bus->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_i2c_probe(struct udevice *bus)
|
||||
{
|
||||
struct rk_i2c *priv = dev_get_priv(bus);
|
||||
|
||||
priv->regs = (void *)dev_get_addr(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops rockchip_i2c_ops = {
|
||||
.xfer = rockchip_i2c_xfer,
|
||||
.set_bus_speed = rockchip_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_i2c_ids[] = {
|
||||
{ .compatible = "rockchip,rk3288-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_rockchip) = {
|
||||
.name = "i2c_rockchip",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = rockchip_i2c_ids,
|
||||
.ofdata_to_platdata = rockchip_i2c_ofdata_to_platdata,
|
||||
.probe = rockchip_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rk_i2c),
|
||||
.ops = &rockchip_i2c_ops,
|
||||
};
|
||||
1465
u-boot/drivers/i2c/s3c24x0_i2c.c
Normal file
1465
u-boot/drivers/i2c/s3c24x0_i2c.c
Normal file
File diff suppressed because it is too large
Load Diff
62
u-boot/drivers/i2c/s3c24x0_i2c.h
Normal file
62
u-boot/drivers/i2c/s3c24x0_i2c.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _S3C24X0_I2C_H
|
||||
#define _S3C24X0_I2C_H
|
||||
|
||||
struct s3c24x0_i2c {
|
||||
u32 iiccon;
|
||||
u32 iicstat;
|
||||
u32 iicadd;
|
||||
u32 iicds;
|
||||
u32 iiclc;
|
||||
};
|
||||
|
||||
struct exynos5_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;
|
||||
};
|
||||
|
||||
struct s3c24x0_i2c_bus {
|
||||
bool active; /* port is active and available */
|
||||
int node; /* device tree node */
|
||||
int bus_num; /* i2c bus number */
|
||||
struct s3c24x0_i2c *regs;
|
||||
struct exynos5_hsi2c *hsregs;
|
||||
int is_highspeed; /* High speed type, rather than I2C */
|
||||
unsigned clock_frequency;
|
||||
int id;
|
||||
unsigned clk_cycle;
|
||||
unsigned clk_div;
|
||||
};
|
||||
#endif /* _S3C24X0_I2C_H */
|
||||
123
u-boot/drivers/i2c/sandbox_i2c.c
Normal file
123
u-boot/drivers/i2c/sandbox_i2c.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Simulate an I2C port
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/test.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct sandbox_i2c_priv {
|
||||
bool test_mode;
|
||||
};
|
||||
|
||||
static int get_emul(struct udevice *dev, struct udevice **devp,
|
||||
struct dm_i2c_ops **opsp)
|
||||
{
|
||||
struct dm_i2c_chip *plat;
|
||||
struct udevice *child;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
*opsp = NULL;
|
||||
plat = dev_get_parent_platdata(dev);
|
||||
if (!plat->emul) {
|
||||
ret = dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset,
|
||||
false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (device_find_first_child(dev, &child); child;
|
||||
device_find_next_child(&child)) {
|
||||
if (device_get_uclass_id(child) != UCLASS_I2C_EMUL)
|
||||
continue;
|
||||
|
||||
ret = device_probe(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (child)
|
||||
plat->emul = child;
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
*devp = plat->emul;
|
||||
*opsp = i2c_get_ops(plat->emul);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode)
|
||||
{
|
||||
struct sandbox_i2c_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->test_mode = test_mode;
|
||||
}
|
||||
|
||||
static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
|
||||
struct sandbox_i2c_priv *priv = dev_get_priv(bus);
|
||||
struct dm_i2c_ops *ops;
|
||||
struct udevice *emul, *dev;
|
||||
bool is_read;
|
||||
int ret;
|
||||
|
||||
/* Special test code to return success but with no emulation */
|
||||
if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR)
|
||||
return 0;
|
||||
|
||||
ret = i2c_get_chip(bus, msg->addr, 1, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = get_emul(dev, &emul, &ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->test_mode) {
|
||||
/*
|
||||
* For testing, don't allow writing above 100KHz for writes and
|
||||
* 400KHz for reads.
|
||||
*/
|
||||
is_read = nmsgs > 1;
|
||||
if (i2c->speed_hz > (is_read ? 400000 : 100000)) {
|
||||
debug("%s: Max speed exceeded\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ops->xfer(emul, msg, nmsgs);
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops sandbox_i2c_ops = {
|
||||
.xfer = sandbox_i2c_xfer,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_i2c_ids[] = {
|
||||
{ .compatible = "sandbox,i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_sandbox) = {
|
||||
.name = "i2c_sandbox",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = sandbox_i2c_ids,
|
||||
.ops = &sandbox_i2c_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv),
|
||||
};
|
||||
308
u-boot/drivers/i2c/sh_i2c.c
Normal file
308
u-boot/drivers/i2c/sh_i2c.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (C) 2011, 2013 Renesas Solutions Corp.
|
||||
* Copyright (C) 2011, 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Every register is 32bit aligned, but only 8bits in size */
|
||||
#define ureg(name) u8 name; u8 __pad_##name##0; u16 __pad_##name##1;
|
||||
struct sh_i2c {
|
||||
ureg(icdr);
|
||||
ureg(iccr);
|
||||
ureg(icsr);
|
||||
ureg(icic);
|
||||
ureg(iccl);
|
||||
ureg(icch);
|
||||
};
|
||||
#undef ureg
|
||||
|
||||
/* ICCR */
|
||||
#define SH_I2C_ICCR_ICE (1 << 7)
|
||||
#define SH_I2C_ICCR_RACK (1 << 6)
|
||||
#define SH_I2C_ICCR_RTS (1 << 4)
|
||||
#define SH_I2C_ICCR_BUSY (1 << 2)
|
||||
#define SH_I2C_ICCR_SCP (1 << 0)
|
||||
|
||||
/* ICSR / ICIC */
|
||||
#define SH_IC_BUSY (1 << 4)
|
||||
#define SH_IC_TACK (1 << 2)
|
||||
#define SH_IC_WAIT (1 << 1)
|
||||
#define SH_IC_DTE (1 << 0)
|
||||
|
||||
#ifdef CONFIG_SH_I2C_8BIT
|
||||
/* store 8th bit of iccl and icch in ICIC register */
|
||||
#define SH_I2C_ICIC_ICCLB8 (1 << 7)
|
||||
#define SH_I2C_ICIC_ICCHB8 (1 << 6)
|
||||
#endif
|
||||
|
||||
static const struct sh_i2c *i2c_dev[CONFIG_SYS_I2C_SH_NUM_CONTROLLERS] = {
|
||||
(struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE0,
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE1
|
||||
(struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE1,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE2
|
||||
(struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE2,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE3
|
||||
(struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE3,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE4
|
||||
(struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE4,
|
||||
#endif
|
||||
};
|
||||
|
||||
static u16 iccl, icch;
|
||||
|
||||
#define IRQ_WAIT 1000
|
||||
|
||||
static void sh_irq_dte(struct sh_i2c *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (SH_IC_DTE & readb(&dev->icsr))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_irq_dte_with_tack(struct sh_i2c *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (SH_IC_DTE & readb(&dev->icsr))
|
||||
break;
|
||||
if (SH_IC_TACK & readb(&dev->icsr))
|
||||
return -1;
|
||||
udelay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_irq_busy(struct sh_i2c *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (!(SH_IC_BUSY & readb(&dev->icsr)))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_i2c_set_addr(struct sh_i2c *dev, u8 chip, u8 addr, int stop)
|
||||
{
|
||||
u8 icic = SH_IC_TACK;
|
||||
|
||||
debug("%s: chip: %x, addr: %x iccl: %x, icch %x\n",
|
||||
__func__, chip, addr, iccl, icch);
|
||||
clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE);
|
||||
setbits_8(&dev->iccr, SH_I2C_ICCR_ICE);
|
||||
|
||||
writeb(iccl & 0xff, &dev->iccl);
|
||||
writeb(icch & 0xff, &dev->icch);
|
||||
#ifdef CONFIG_SH_I2C_8BIT
|
||||
if (iccl > 0xff)
|
||||
icic |= SH_I2C_ICIC_ICCLB8;
|
||||
if (icch > 0xff)
|
||||
icic |= SH_I2C_ICIC_ICCHB8;
|
||||
#endif
|
||||
writeb(icic, &dev->icic);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr);
|
||||
sh_irq_dte(dev);
|
||||
|
||||
clrbits_8(&dev->icsr, SH_IC_TACK);
|
||||
writeb(chip << 1, &dev->icdr);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
return -1;
|
||||
|
||||
writeb(addr, &dev->icdr);
|
||||
if (stop)
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS), &dev->iccr);
|
||||
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_i2c_finish(struct sh_i2c *dev)
|
||||
{
|
||||
writeb(0, &dev->icsr);
|
||||
clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE);
|
||||
}
|
||||
|
||||
static int
|
||||
sh_i2c_raw_write(struct sh_i2c *dev, u8 chip, uint addr, u8 val)
|
||||
{
|
||||
int ret = -1;
|
||||
if (sh_i2c_set_addr(dev, chip, addr, 0) != 0)
|
||||
goto exit0;
|
||||
udelay(10);
|
||||
|
||||
writeb(val, &dev->icdr);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto exit0;
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE | SH_I2C_ICCR_RTS), &dev->iccr);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto exit0;
|
||||
sh_irq_busy(dev);
|
||||
ret = 0;
|
||||
|
||||
exit0:
|
||||
sh_i2c_finish(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_i2c_raw_read(struct sh_i2c *dev, u8 chip, u8 addr)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
#if defined(CONFIG_SH73A0)
|
||||
if (sh_i2c_set_addr(dev, chip, addr, 0) != 0)
|
||||
goto exit0;
|
||||
#else
|
||||
if (sh_i2c_set_addr(dev, chip, addr, 1) != 0)
|
||||
goto exit0;
|
||||
udelay(100);
|
||||
#endif
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr);
|
||||
sh_irq_dte(dev);
|
||||
|
||||
writeb(chip << 1 | 0x01, &dev->icdr);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto exit0;
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_SCP), &dev->iccr);
|
||||
if (sh_irq_dte_with_tack(dev) != 0)
|
||||
goto exit0;
|
||||
|
||||
ret = readb(&dev->icdr) & 0xff;
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RACK), &dev->iccr);
|
||||
readb(&dev->icdr); /* Dummy read */
|
||||
sh_irq_busy(dev);
|
||||
|
||||
exit0:
|
||||
sh_i2c_finish(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
sh_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
||||
{
|
||||
int num, denom, tmp;
|
||||
|
||||
/* No i2c support prior to relocation */
|
||||
if (!(gd->flags & GD_FLG_RELOC))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Calculate the value for iccl. From the data sheet:
|
||||
* iccl = (p-clock / transfer-rate) * (L / (L + H))
|
||||
* where L and H are the SCL low and high ratio.
|
||||
*/
|
||||
num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_LOW;
|
||||
denom = speed * (CONFIG_SH_I2C_DATA_HIGH + CONFIG_SH_I2C_DATA_LOW);
|
||||
tmp = num * 10 / denom;
|
||||
if (tmp % 10 >= 5)
|
||||
iccl = (u16)((num/denom) + 1);
|
||||
else
|
||||
iccl = (u16)(num/denom);
|
||||
|
||||
/* Calculate the value for icch. From the data sheet:
|
||||
icch = (p clock / transfer rate) * (H / (L + H)) */
|
||||
num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_HIGH;
|
||||
tmp = num * 10 / denom;
|
||||
if (tmp % 10 >= 5)
|
||||
icch = (u16)((num/denom) + 1);
|
||||
else
|
||||
icch = (u16)(num/denom);
|
||||
|
||||
debug("clock: %d, speed %d, iccl: %x, icch: %x\n",
|
||||
CONFIG_SH_I2C_CLOCK, speed, iccl, icch);
|
||||
}
|
||||
|
||||
static int sh_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
||||
uint addr, int alen, u8 *data, int len)
|
||||
{
|
||||
int ret, i;
|
||||
struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr];
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = sh_i2c_raw_read(dev, chip, addr + i);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
data[i] = ret & 0xff;
|
||||
debug("%s: data[%d]: %02x\n", __func__, i, data[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr,
|
||||
int alen, u8 *data, int len)
|
||||
{
|
||||
struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
debug("%s: data[%d]: %02x\n", __func__, i, data[i]);
|
||||
if (sh_i2c_raw_write(dev, chip, addr + i, data[i]) != 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sh_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
u8 dummy[1];
|
||||
|
||||
return sh_i2c_read(adap, dev, 0, 0, dummy, sizeof dummy);
|
||||
}
|
||||
|
||||
static unsigned int sh_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr];
|
||||
|
||||
sh_i2c_finish(dev);
|
||||
sh_i2c_init(adap, speed, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register RCAR i2c adapters
|
||||
*/
|
||||
U_BOOT_I2C_ADAP_COMPLETE(sh_0, sh_i2c_init, sh_i2c_probe, sh_i2c_read,
|
||||
sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED0, 0, 0)
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(sh_1, sh_i2c_init, sh_i2c_probe, sh_i2c_read,
|
||||
sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED1, 0, 1)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE2
|
||||
U_BOOT_I2C_ADAP_COMPLETE(sh_2, sh_i2c_init, sh_i2c_probe, sh_i2c_read,
|
||||
sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED2, 0, 2)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE3
|
||||
U_BOOT_I2C_ADAP_COMPLETE(sh_3, sh_i2c_init, sh_i2c_probe, sh_i2c_read,
|
||||
sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED3, 0, 3)
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C_SH_BASE4
|
||||
U_BOOT_I2C_ADAP_COMPLETE(sh_4, sh_i2c_init, sh_i2c_probe, sh_i2c_read,
|
||||
sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED4, 0, 4)
|
||||
#endif
|
||||
374
u-boot/drivers/i2c/sh_sh7734_i2c.c
Normal file
374
u-boot/drivers/i2c/sh_sh7734_i2c.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct sh_i2c {
|
||||
u8 iccr1;
|
||||
u8 iccr2;
|
||||
u8 icmr;
|
||||
u8 icier;
|
||||
u8 icsr;
|
||||
u8 sar;
|
||||
u8 icdrt;
|
||||
u8 icdrr;
|
||||
u8 nf2cyc;
|
||||
u8 __pad0;
|
||||
u8 __pad1;
|
||||
};
|
||||
|
||||
static struct sh_i2c *base;
|
||||
static u8 iccr1_cks, nf2cyc;
|
||||
|
||||
/* ICCR1 */
|
||||
#define SH_I2C_ICCR1_ICE (1 << 7)
|
||||
#define SH_I2C_ICCR1_RCVD (1 << 6)
|
||||
#define SH_I2C_ICCR1_MST (1 << 5)
|
||||
#define SH_I2C_ICCR1_TRS (1 << 4)
|
||||
#define SH_I2C_ICCR1_MTRS \
|
||||
(SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS)
|
||||
|
||||
/* ICCR1 */
|
||||
#define SH_I2C_ICCR2_BBSY (1 << 7)
|
||||
#define SH_I2C_ICCR2_SCP (1 << 6)
|
||||
#define SH_I2C_ICCR2_SDAO (1 << 5)
|
||||
#define SH_I2C_ICCR2_SDAOP (1 << 4)
|
||||
#define SH_I2C_ICCR2_SCLO (1 << 3)
|
||||
#define SH_I2C_ICCR2_IICRST (1 << 1)
|
||||
|
||||
#define SH_I2C_ICIER_TIE (1 << 7)
|
||||
#define SH_I2C_ICIER_TEIE (1 << 6)
|
||||
#define SH_I2C_ICIER_RIE (1 << 5)
|
||||
#define SH_I2C_ICIER_NAKIE (1 << 4)
|
||||
#define SH_I2C_ICIER_STIE (1 << 3)
|
||||
#define SH_I2C_ICIER_ACKE (1 << 2)
|
||||
#define SH_I2C_ICIER_ACKBR (1 << 1)
|
||||
#define SH_I2C_ICIER_ACKBT (1 << 0)
|
||||
|
||||
#define SH_I2C_ICSR_TDRE (1 << 7)
|
||||
#define SH_I2C_ICSR_TEND (1 << 6)
|
||||
#define SH_I2C_ICSR_RDRF (1 << 5)
|
||||
#define SH_I2C_ICSR_NACKF (1 << 4)
|
||||
#define SH_I2C_ICSR_STOP (1 << 3)
|
||||
#define SH_I2C_ICSR_ALOVE (1 << 2)
|
||||
#define SH_I2C_ICSR_AAS (1 << 1)
|
||||
#define SH_I2C_ICSR_ADZ (1 << 0)
|
||||
|
||||
#define IRQ_WAIT 1000
|
||||
|
||||
static void sh_i2c_send_stop(struct sh_i2c *base)
|
||||
{
|
||||
clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP);
|
||||
}
|
||||
|
||||
static int check_icsr_bits(struct sh_i2c *base, u8 bits)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (bits & readb(&base->icsr))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_stop(struct sh_i2c *base)
|
||||
{
|
||||
int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_tend(struct sh_i2c *base, int stop)
|
||||
{
|
||||
int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND);
|
||||
|
||||
if (stop) {
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
sh_i2c_send_stop(base);
|
||||
}
|
||||
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TEND);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_tdre(struct sh_i2c *base)
|
||||
{
|
||||
return check_icsr_bits(base, SH_I2C_ICSR_TDRE);
|
||||
}
|
||||
|
||||
static int check_rdrf(struct sh_i2c *base)
|
||||
{
|
||||
return check_icsr_bits(base, SH_I2C_ICSR_RDRF);
|
||||
}
|
||||
|
||||
static int check_bbsy(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2)))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_ackbr(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier)))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sh_i2c_reset(struct sh_i2c *base)
|
||||
{
|
||||
setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
||||
|
||||
udelay(100);
|
||||
|
||||
clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
||||
}
|
||||
|
||||
static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg)
|
||||
{
|
||||
if (check_bbsy(base)) {
|
||||
puts("i2c bus busy\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
||||
|
||||
writeb((id << 1), &base->icdrt);
|
||||
|
||||
if (check_tend(base, 0)) {
|
||||
puts("TEND check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (check_ackbr(base)) {
|
||||
check_tend(base, 0);
|
||||
sh_i2c_send_stop(base);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
writeb(reg, &base->icdrt);
|
||||
|
||||
if (check_tdre(base)) {
|
||||
puts("TDRE check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (check_tend(base, 0)) {
|
||||
puts("TEND check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (i2c_set_addr(base, id, reg)) {
|
||||
puts("Fail set slave address\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
writeb(val[i], &base->icdrt);
|
||||
check_tdre(base);
|
||||
}
|
||||
|
||||
check_tend(base, 1);
|
||||
check_stop(base);
|
||||
|
||||
udelay(100);
|
||||
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
sh_i2c_reset(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
||||
if (i2c_set_addr(base, id, reg)) {
|
||||
puts("Fail set slave address\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
||||
writeb((id << 1) | 1, &base->icdrt);
|
||||
|
||||
if (check_tend(base, 0))
|
||||
puts("TDRE check fail...\n");
|
||||
|
||||
clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
setbits_8(&base->icier, SH_I2C_ICIER_ACKBT);
|
||||
setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
||||
|
||||
/* read data (dummy) */
|
||||
ret = readb(&base->icdrr);
|
||||
|
||||
if (check_rdrf(base)) {
|
||||
puts("check RDRF error\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
udelay(1000);
|
||||
|
||||
sh_i2c_send_stop(base);
|
||||
|
||||
if (check_stop(base)) {
|
||||
puts("check STOP error\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
|
||||
/* data read */
|
||||
ret = readb(&base->icdrr);
|
||||
|
||||
fail:
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
static unsigned int current_bus;
|
||||
|
||||
/**
|
||||
* i2c_set_bus_num - change active I2C bus
|
||||
* @bus: bus index, zero based
|
||||
* @returns: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
switch (bus) {
|
||||
case 0:
|
||||
base = (void *)CONFIG_SH_I2C_BASE0;
|
||||
break;
|
||||
case 1:
|
||||
base = (void *)CONFIG_SH_I2C_BASE1;
|
||||
break;
|
||||
default:
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
current_bus = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_get_bus_num - returns index of active I2C bus
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
#endif
|
||||
base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0;
|
||||
|
||||
if (speed == 400000)
|
||||
iccr1_cks = 0x07;
|
||||
else
|
||||
iccr1_cks = 0x0F;
|
||||
|
||||
nf2cyc = 1;
|
||||
|
||||
/* Reset */
|
||||
sh_i2c_reset(base);
|
||||
|
||||
/* ICE enable and set clock */
|
||||
writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1);
|
||||
writeb(nf2cyc, &base->nf2cyc);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: - Read multiple bytes from an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be read
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to write the data
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
buffer[i] = i2c_raw_read(base, chip, addr + i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write: - Write multiple bytes to an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be written
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to find the data to be written
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
return i2c_raw_write(base, chip, addr, buffer, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: - Test if a chip answers for a given i2c address
|
||||
*
|
||||
* @chip: address of the chip which is searched for
|
||||
* @return: 0 if a chip was found, -1 otherwhise
|
||||
*/
|
||||
int i2c_probe(u8 chip)
|
||||
{
|
||||
u8 byte;
|
||||
return i2c_read(chip, 0, 0, &byte, 1);
|
||||
}
|
||||
531
u-boot/drivers/i2c/soft_i2c.c
Normal file
531
u-boot/drivers/i2c/soft_i2c.c
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
* Changes for multibus/multiadapter I2C support.
|
||||
*
|
||||
* (C) Copyright 2001, 2002
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This has been changed substantially by Gerald Van Baren, Custom IDEAS,
|
||||
* vanbaren@cideas.com. It was heavily influenced by LiMon, written by
|
||||
* Neil Russell.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */
|
||||
#include <ioports.h>
|
||||
#include <asm/io.h>
|
||||
#endif
|
||||
#if defined(CONFIG_AVR32)
|
||||
#include <asm/arch/portmux.h>
|
||||
#endif
|
||||
#if defined(CONFIG_AT91FAMILY)
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/at91_pio.h>
|
||||
#ifdef CONFIG_ATMEL_LEGACY
|
||||
#include <asm/arch/gpio.h>
|
||||
#endif
|
||||
#endif
|
||||
#if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
|
||||
#include <asm/io.h>
|
||||
#endif
|
||||
#include <i2c.h>
|
||||
|
||||
#if defined(CONFIG_SOFT_I2C_GPIO_SCL)
|
||||
# include <asm/gpio.h>
|
||||
|
||||
# ifndef I2C_GPIO_SYNC
|
||||
# define I2C_GPIO_SYNC
|
||||
# endif
|
||||
|
||||
# ifndef I2C_INIT
|
||||
# define I2C_INIT \
|
||||
do { \
|
||||
gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \
|
||||
gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_ACTIVE
|
||||
# define I2C_ACTIVE do { } while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_TRISTATE
|
||||
# define I2C_TRISTATE do { } while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_READ
|
||||
# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_SDA
|
||||
# define I2C_SDA(bit) \
|
||||
do { \
|
||||
if (bit) \
|
||||
gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \
|
||||
else \
|
||||
gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \
|
||||
I2C_GPIO_SYNC; \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_SCL
|
||||
# define I2C_SCL(bit) \
|
||||
do { \
|
||||
gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \
|
||||
I2C_GPIO_SYNC; \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_DELAY
|
||||
# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_I2C */
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifndef I2C_SOFT_DECLARATIONS
|
||||
# if defined(CONFIG_MPC8260)
|
||||
# define I2C_SOFT_DECLARATIONS volatile ioport_t *iop = \
|
||||
ioport_addr((immap_t *)CONFIG_SYS_IMMR, I2C_PORT);
|
||||
# elif defined(CONFIG_8xx)
|
||||
# define I2C_SOFT_DECLARATIONS volatile immap_t *immr = \
|
||||
(immap_t *)CONFIG_SYS_IMMR;
|
||||
# else
|
||||
# define I2C_SOFT_DECLARATIONS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_SOFT_SPEED)
|
||||
#define CONFIG_SYS_I2C_SOFT_SPEED CONFIG_SYS_I2C_SPEED
|
||||
#endif
|
||||
#if !defined(CONFIG_SYS_I2C_SOFT_SLAVE)
|
||||
#define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Definitions
|
||||
*/
|
||||
#define RETRIES 0
|
||||
|
||||
#define I2C_ACK 0 /* PD_SDA level to ack a byte */
|
||||
#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
|
||||
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define PRINTD(fmt,args...) do { \
|
||||
printf (fmt ,##args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define PRINTD(fmt,args...)
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Local functions
|
||||
*/
|
||||
#if !defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
static void send_reset (void);
|
||||
#endif
|
||||
static void send_start (void);
|
||||
static void send_stop (void);
|
||||
static void send_ack (int);
|
||||
static int write_byte (uchar byte);
|
||||
static uchar read_byte (int);
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
/*-----------------------------------------------------------------------
|
||||
* Send a reset sequence consisting of 9 clocks with the data signal high
|
||||
* to clock any confused device back into an idle state. Also send a
|
||||
* <stop> at the end of the sequence for belts & suspenders.
|
||||
*/
|
||||
static void send_reset(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int j;
|
||||
|
||||
I2C_SCL(1);
|
||||
I2C_SDA(1);
|
||||
#ifdef I2C_INIT
|
||||
I2C_INIT;
|
||||
#endif
|
||||
I2C_TRISTATE;
|
||||
for(j = 0; j < 9; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
}
|
||||
send_stop();
|
||||
I2C_TRISTATE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* START: High -> Low on SDA while SCL is High
|
||||
*/
|
||||
static void send_start(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_ACTIVE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(0);
|
||||
I2C_DELAY;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* STOP: Low -> High on SDA while SCL is High
|
||||
*/
|
||||
static void send_stop(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(0);
|
||||
I2C_ACTIVE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_DELAY;
|
||||
I2C_TRISTATE;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* ack should be I2C_ACK or I2C_NOACK
|
||||
*/
|
||||
static void send_ack(int ack)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_ACTIVE;
|
||||
I2C_SDA(ack);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Send 8 bits and look for an acknowledgement.
|
||||
*/
|
||||
static int write_byte(uchar data)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int j;
|
||||
int nack;
|
||||
|
||||
I2C_ACTIVE;
|
||||
for(j = 0; j < 8; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(data & 0x80);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
|
||||
data <<= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for an <ACK>(negative logic) and return it.
|
||||
*/
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_TRISTATE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
nack = I2C_READ;
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_ACTIVE;
|
||||
|
||||
return(nack); /* not a nack is an ack */
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* if ack == I2C_ACK, ACK the byte so can continue reading, else
|
||||
* send I2C_NOACK to end the read.
|
||||
*/
|
||||
static uchar read_byte(int ack)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int data;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* Read 8 bits, MSB first.
|
||||
*/
|
||||
I2C_TRISTATE;
|
||||
I2C_SDA(1);
|
||||
data = 0;
|
||||
for(j = 0; j < 8; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
data <<= 1;
|
||||
data |= I2C_READ;
|
||||
I2C_DELAY;
|
||||
}
|
||||
send_ack(ack);
|
||||
|
||||
return(data);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
|
||||
{
|
||||
#if defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
/* call board specific i2c bus reset routine before accessing the */
|
||||
/* environment, which might be in a chip on that bus. For details */
|
||||
/* about this problem see doc/I2C_Edge_Conditions. */
|
||||
i2c_init_board();
|
||||
#else
|
||||
/*
|
||||
* WARNING: Do NOT save speed in a static variable: if the
|
||||
* I2C routines are called before RAM is initialized (to read
|
||||
* the DIMM SPD, for instance), RAM won't be usable and your
|
||||
* system will crash.
|
||||
*/
|
||||
send_reset ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Probe to see if a chip is present. Also good for checking for the
|
||||
* completion of EEPROM writes since the chip stops responding until
|
||||
* the write completes (typically 10mSec).
|
||||
*/
|
||||
static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* perform 1 byte write transaction with just address byte
|
||||
* (fake write)
|
||||
*/
|
||||
send_start();
|
||||
rc = write_byte ((addr << 1) | 0);
|
||||
send_stop();
|
||||
|
||||
return (rc ? 1 : 0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Read bytes
|
||||
*/
|
||||
static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
int shift;
|
||||
PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n",
|
||||
chip, addr, alen, buffer, len);
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
|
||||
PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
|
||||
chip, addr);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do the addressing portion of a write cycle to set the
|
||||
* chip's address pointer. If the address length is zero,
|
||||
* don't do the normal write cycle to set the address pointer,
|
||||
* there is no address pointer in this chip.
|
||||
*/
|
||||
send_start();
|
||||
if(alen > 0) {
|
||||
if(write_byte(chip << 1)) { /* write cycle */
|
||||
send_stop();
|
||||
PRINTD("i2c_read, no chip responded %02X\n", chip);
|
||||
return(1);
|
||||
}
|
||||
shift = (alen-1) * 8;
|
||||
while(alen-- > 0) {
|
||||
if(write_byte(addr >> shift)) {
|
||||
PRINTD("i2c_read, address not <ACK>ed\n");
|
||||
return(1);
|
||||
}
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
/* Some I2C chips need a stop/start sequence here,
|
||||
* other chips don't work with a full stop and need
|
||||
* only a start. Default behaviour is to send the
|
||||
* stop/start sequence.
|
||||
*/
|
||||
#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
|
||||
send_start();
|
||||
#else
|
||||
send_stop();
|
||||
send_start();
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* Send the chip address again, this time for a read cycle.
|
||||
* Then read the data. On the last byte, we do a NACK instead
|
||||
* of an ACK(len == 0) to terminate the read.
|
||||
*/
|
||||
write_byte((chip << 1) | 1); /* read cycle */
|
||||
while(len-- > 0) {
|
||||
*buffer++ = read_byte(len == 0);
|
||||
}
|
||||
send_stop();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Write bytes
|
||||
*/
|
||||
static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
||||
int alen, uchar *buffer, int len)
|
||||
{
|
||||
int shift, failures = 0;
|
||||
|
||||
PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n",
|
||||
chip, addr, alen, buffer, len);
|
||||
|
||||
send_start();
|
||||
if(write_byte(chip << 1)) { /* write cycle */
|
||||
send_stop();
|
||||
PRINTD("i2c_write, no chip responded %02X\n", chip);
|
||||
return(1);
|
||||
}
|
||||
shift = (alen-1) * 8;
|
||||
while(alen-- > 0) {
|
||||
if(write_byte(addr >> shift)) {
|
||||
PRINTD("i2c_write, address not <ACK>ed\n");
|
||||
return(1);
|
||||
}
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
while(len-- > 0) {
|
||||
if(write_byte(*buffer++)) {
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
send_stop();
|
||||
return(failures);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register soft i2c adapters
|
||||
*/
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft00, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
|
||||
0)
|
||||
#if defined(I2C_SOFT_DECLARATIONS2)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft01, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_2,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_2,
|
||||
1)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS3)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft02, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_3,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_3,
|
||||
2)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS4)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft03, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_4,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_4,
|
||||
3)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS5)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft04, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_5,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_5,
|
||||
4)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS6)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft05, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_6,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_6,
|
||||
5)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS7)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft06, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_7,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_7,
|
||||
6)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS8)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft07, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_8,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_8,
|
||||
7)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS9)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft08, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_9,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_9,
|
||||
8)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS10)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft09, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_10,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_10,
|
||||
9)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS11)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft10, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_11,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_11,
|
||||
10)
|
||||
#endif
|
||||
#if defined(I2C_SOFT_DECLARATIONS12)
|
||||
U_BOOT_I2C_ADAP_COMPLETE(soft11, soft_i2c_init, soft_i2c_probe,
|
||||
soft_i2c_read, soft_i2c_write, NULL,
|
||||
CONFIG_SYS_I2C_SOFT_SPEED_12,
|
||||
CONFIG_SYS_I2C_SOFT_SLAVE_12,
|
||||
11)
|
||||
#endif
|
||||
501
u-boot/drivers/i2c/tegra_i2c.c
Normal file
501
u-boot/drivers/i2c/tegra_i2c.c
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Copyright (c) 2010-2011 NVIDIA Corporation
|
||||
* NVIDIA Corporation <www.nvidia.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/funcmux.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch-tegra/clk_rst.h>
|
||||
#include <asm/arch-tegra/tegra_i2c.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum i2c_type {
|
||||
TYPE_114,
|
||||
TYPE_STD,
|
||||
TYPE_DVC,
|
||||
};
|
||||
|
||||
/* Information about i2c controller */
|
||||
struct i2c_bus {
|
||||
int id;
|
||||
enum periph_id periph_id;
|
||||
int speed;
|
||||
int pinmux_config;
|
||||
struct i2c_control *control;
|
||||
struct i2c_ctlr *regs;
|
||||
enum i2c_type type;
|
||||
int inited; /* bus is inited */
|
||||
};
|
||||
|
||||
static void set_packet_mode(struct i2c_bus *i2c_bus)
|
||||
{
|
||||
u32 config;
|
||||
|
||||
config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK;
|
||||
|
||||
if (i2c_bus->type == TYPE_DVC) {
|
||||
struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs;
|
||||
|
||||
writel(config, &dvc->cnfg);
|
||||
} else {
|
||||
writel(config, &i2c_bus->regs->cnfg);
|
||||
/*
|
||||
* program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe
|
||||
* issues, i.e., some slaves may be wrongly detected.
|
||||
*/
|
||||
setbits_le32(&i2c_bus->regs->sl_cnfg, I2C_SL_CNFG_NEWSL_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_reset_controller(struct i2c_bus *i2c_bus)
|
||||
{
|
||||
/* Reset I2C controller. */
|
||||
reset_periph(i2c_bus->periph_id, 1);
|
||||
|
||||
/* re-program config register to packet mode */
|
||||
set_packet_mode(i2c_bus);
|
||||
}
|
||||
|
||||
static void i2c_init_controller(struct i2c_bus *i2c_bus)
|
||||
{
|
||||
if (!i2c_bus->speed)
|
||||
return;
|
||||
debug("%s: speed=%d\n", __func__, i2c_bus->speed);
|
||||
/*
|
||||
* Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8
|
||||
* here, in section 23.3.1, but in fact we seem to need a factor of
|
||||
* 16 to get the right frequency.
|
||||
*/
|
||||
clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
|
||||
i2c_bus->speed * 2 * 8);
|
||||
|
||||
if (i2c_bus->type == TYPE_114) {
|
||||
/*
|
||||
* T114 I2C went to a single clock source for standard/fast and
|
||||
* HS clock speeds. The new clock rate setting calculation is:
|
||||
* SCL = CLK_SOURCE.I2C /
|
||||
* (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) *
|
||||
* I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1).
|
||||
*
|
||||
* NOTE: We do this here, after the initial clock/pll start,
|
||||
* because if we read the clk_div reg before the controller
|
||||
* is running, we hang, and we need it for the new calc.
|
||||
*/
|
||||
int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16;
|
||||
debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__,
|
||||
clk_div_stdfst_mode);
|
||||
|
||||
clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
|
||||
CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) *
|
||||
i2c_bus->speed * 2);
|
||||
}
|
||||
|
||||
/* Reset I2C controller. */
|
||||
i2c_reset_controller(i2c_bus);
|
||||
|
||||
/* Configure I2C controller. */
|
||||
if (i2c_bus->type == TYPE_DVC) { /* only for DVC I2C */
|
||||
struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs;
|
||||
|
||||
setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK);
|
||||
}
|
||||
|
||||
funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config);
|
||||
}
|
||||
|
||||
static void send_packet_headers(
|
||||
struct i2c_bus *i2c_bus,
|
||||
struct i2c_trans_info *trans,
|
||||
u32 packet_id,
|
||||
bool end_with_repeated_start)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
/* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */
|
||||
data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT;
|
||||
data |= packet_id << PKT_HDR1_PKT_ID_SHIFT;
|
||||
data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT;
|
||||
writel(data, &i2c_bus->control->tx_fifo);
|
||||
debug("pkt header 1 sent (0x%x)\n", data);
|
||||
|
||||
/* prepare header2 */
|
||||
data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT;
|
||||
writel(data, &i2c_bus->control->tx_fifo);
|
||||
debug("pkt header 2 sent (0x%x)\n", data);
|
||||
|
||||
/* prepare IO specific header: configure the slave address */
|
||||
data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT;
|
||||
|
||||
/* Enable Read if it is not a write transaction */
|
||||
if (!(trans->flags & I2C_IS_WRITE))
|
||||
data |= PKT_HDR3_READ_MODE_MASK;
|
||||
if (end_with_repeated_start)
|
||||
data |= PKT_HDR3_REPEAT_START_MASK;
|
||||
|
||||
/* Write I2C specific header */
|
||||
writel(data, &i2c_bus->control->tx_fifo);
|
||||
debug("pkt header 3 sent (0x%x)\n", data);
|
||||
}
|
||||
|
||||
static int wait_for_tx_fifo_empty(struct i2c_control *control)
|
||||
{
|
||||
u32 count;
|
||||
int timeout_us = I2C_TIMEOUT_USEC;
|
||||
|
||||
while (timeout_us >= 0) {
|
||||
count = (readl(&control->fifo_status) & TX_FIFO_EMPTY_CNT_MASK)
|
||||
>> TX_FIFO_EMPTY_CNT_SHIFT;
|
||||
if (count == I2C_FIFO_DEPTH)
|
||||
return 1;
|
||||
udelay(10);
|
||||
timeout_us -= 10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_rx_fifo_notempty(struct i2c_control *control)
|
||||
{
|
||||
u32 count;
|
||||
int timeout_us = I2C_TIMEOUT_USEC;
|
||||
|
||||
while (timeout_us >= 0) {
|
||||
count = (readl(&control->fifo_status) & TX_FIFO_FULL_CNT_MASK)
|
||||
>> TX_FIFO_FULL_CNT_SHIFT;
|
||||
if (count)
|
||||
return 1;
|
||||
udelay(10);
|
||||
timeout_us -= 10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_transfer_complete(struct i2c_control *control)
|
||||
{
|
||||
int int_status;
|
||||
int timeout_us = I2C_TIMEOUT_USEC;
|
||||
|
||||
while (timeout_us >= 0) {
|
||||
int_status = readl(&control->int_status);
|
||||
if (int_status & I2C_INT_NO_ACK_MASK)
|
||||
return -int_status;
|
||||
if (int_status & I2C_INT_ARBITRATION_LOST_MASK)
|
||||
return -int_status;
|
||||
if (int_status & I2C_INT_XFER_COMPLETE_MASK)
|
||||
return 0;
|
||||
|
||||
udelay(10);
|
||||
timeout_us -= 10;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int send_recv_packets(struct i2c_bus *i2c_bus,
|
||||
struct i2c_trans_info *trans)
|
||||
{
|
||||
struct i2c_control *control = i2c_bus->control;
|
||||
u32 int_status;
|
||||
u32 words;
|
||||
u8 *dptr;
|
||||
u32 local;
|
||||
uchar last_bytes;
|
||||
int error = 0;
|
||||
int is_write = trans->flags & I2C_IS_WRITE;
|
||||
|
||||
/* clear status from previous transaction, XFER_COMPLETE, NOACK, etc. */
|
||||
int_status = readl(&control->int_status);
|
||||
writel(int_status, &control->int_status);
|
||||
|
||||
send_packet_headers(i2c_bus, trans, 1,
|
||||
trans->flags & I2C_USE_REPEATED_START);
|
||||
|
||||
words = DIV_ROUND_UP(trans->num_bytes, 4);
|
||||
last_bytes = trans->num_bytes & 3;
|
||||
dptr = trans->buf;
|
||||
|
||||
while (words) {
|
||||
u32 *wptr = (u32 *)dptr;
|
||||
|
||||
if (is_write) {
|
||||
/* deal with word alignment */
|
||||
if ((words == 1) && last_bytes) {
|
||||
local = 0;
|
||||
memcpy(&local, dptr, last_bytes);
|
||||
} else if ((unsigned long)dptr & 3) {
|
||||
memcpy(&local, dptr, sizeof(u32));
|
||||
} else {
|
||||
local = *wptr;
|
||||
}
|
||||
writel(local, &control->tx_fifo);
|
||||
debug("pkt data sent (0x%x)\n", local);
|
||||
if (!wait_for_tx_fifo_empty(control)) {
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (!wait_for_rx_fifo_notempty(control)) {
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
/*
|
||||
* for the last word, we read into our local buffer,
|
||||
* in case that caller did not provide enough buffer.
|
||||
*/
|
||||
local = readl(&control->rx_fifo);
|
||||
if ((words == 1) && last_bytes)
|
||||
memcpy(dptr, (char *)&local, last_bytes);
|
||||
else if ((unsigned long)dptr & 3)
|
||||
memcpy(dptr, &local, sizeof(u32));
|
||||
else
|
||||
*wptr = local;
|
||||
debug("pkt data received (0x%x)\n", local);
|
||||
}
|
||||
words--;
|
||||
dptr += sizeof(u32);
|
||||
}
|
||||
|
||||
if (wait_for_transfer_complete(control)) {
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
return 0;
|
||||
exit:
|
||||
/* error, reset the controller. */
|
||||
i2c_reset_controller(i2c_bus);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int tegra_i2c_write_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 len, bool end_with_repeated_start)
|
||||
{
|
||||
int error;
|
||||
struct i2c_trans_info trans_info;
|
||||
|
||||
trans_info.address = addr;
|
||||
trans_info.buf = data;
|
||||
trans_info.flags = I2C_IS_WRITE;
|
||||
if (end_with_repeated_start)
|
||||
trans_info.flags |= I2C_USE_REPEATED_START;
|
||||
trans_info.num_bytes = len;
|
||||
trans_info.is_10bit_address = 0;
|
||||
|
||||
error = send_recv_packets(i2c_bus, &trans_info);
|
||||
if (error)
|
||||
debug("tegra_i2c_write_data: Error (%d) !!!\n", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int tegra_i2c_read_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data,
|
||||
u32 len)
|
||||
{
|
||||
int error;
|
||||
struct i2c_trans_info trans_info;
|
||||
|
||||
trans_info.address = addr | 1;
|
||||
trans_info.buf = data;
|
||||
trans_info.flags = 0;
|
||||
trans_info.num_bytes = len;
|
||||
trans_info.is_10bit_address = 0;
|
||||
|
||||
error = send_recv_packets(i2c_bus, &trans_info);
|
||||
if (error)
|
||||
debug("tegra_i2c_read_data: Error (%d) !!!\n", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int tegra_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
|
||||
i2c_bus->speed = speed;
|
||||
i2c_init_controller(i2c_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev->of_offset;
|
||||
bool is_dvc;
|
||||
|
||||
i2c_bus->id = dev->seq;
|
||||
i2c_bus->type = dev_get_driver_data(dev);
|
||||
i2c_bus->regs = (struct i2c_ctlr *)dev_get_addr(dev);
|
||||
|
||||
/*
|
||||
* We don't have a binding for pinmux yet. Leave it out for now. So
|
||||
* far no one needs anything other than the default.
|
||||
*/
|
||||
i2c_bus->pinmux_config = FUNCMUX_DEFAULT;
|
||||
i2c_bus->periph_id = clock_decode_periph_id(blob, node);
|
||||
|
||||
/*
|
||||
* We can't specify the pinmux config in the fdt, so I2C2 will not
|
||||
* work on Seaboard. It normally has no devices on it anyway.
|
||||
* You could add in this little hack if you need to use it.
|
||||
* The correct solution is a pinmux binding in the fdt.
|
||||
*
|
||||
* if (i2c_bus->periph_id == PERIPH_ID_I2C2)
|
||||
* i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA;
|
||||
*/
|
||||
if (i2c_bus->periph_id == -1)
|
||||
return -EINVAL;
|
||||
|
||||
is_dvc = dev_get_driver_data(dev) == TYPE_DVC;
|
||||
if (is_dvc) {
|
||||
i2c_bus->control =
|
||||
&((struct dvc_ctlr *)i2c_bus->regs)->control;
|
||||
} else {
|
||||
i2c_bus->control = &i2c_bus->regs->control;
|
||||
}
|
||||
i2c_init_controller(i2c_bus);
|
||||
debug("%s: controller bus %d at %p, periph_id %d, speed %d: ",
|
||||
is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs,
|
||||
i2c_bus->periph_id, i2c_bus->speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* i2c write version without the register address */
|
||||
static int i2c_write_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer,
|
||||
int len, bool end_with_repeated_start)
|
||||
{
|
||||
int rc;
|
||||
|
||||
debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
|
||||
debug("write_data: ");
|
||||
/* use rc for counter */
|
||||
for (rc = 0; rc < len; ++rc)
|
||||
debug(" 0x%02x", buffer[rc]);
|
||||
debug("\n");
|
||||
|
||||
/* Shift 7-bit address over for lower-level i2c functions */
|
||||
rc = tegra_i2c_write_data(i2c_bus, chip << 1, buffer, len,
|
||||
end_with_repeated_start);
|
||||
if (rc)
|
||||
debug("i2c_write_data(): rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* i2c read version without the register address */
|
||||
static int i2c_read_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer,
|
||||
int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
debug("inside i2c_read_data():\n");
|
||||
/* Shift 7-bit address over for lower-level i2c functions */
|
||||
rc = tegra_i2c_read_data(i2c_bus, chip << 1, buffer, len);
|
||||
if (rc) {
|
||||
debug("i2c_read_data(): rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
debug("i2c_read_data: ");
|
||||
/* reuse rc for counter*/
|
||||
for (rc = 0; rc < len; ++rc)
|
||||
debug(" 0x%02x", buffer[rc]);
|
||||
debug("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probe to see if a chip is present. */
|
||||
static int tegra_i2c_probe_chip(struct udevice *bus, uint chip_addr,
|
||||
uint chip_flags)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
int rc;
|
||||
u8 reg;
|
||||
|
||||
/* Shift 7-bit address over for lower-level i2c functions */
|
||||
rc = tegra_i2c_write_data(i2c_bus, chip_addr << 1, ®, sizeof(reg),
|
||||
false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tegra_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct i2c_bus *i2c_bus = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
debug("i2c_xfer: %d messages\n", nmsgs);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
|
||||
|
||||
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len);
|
||||
} else {
|
||||
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
|
||||
msg->len, next_is_read);
|
||||
}
|
||||
if (ret) {
|
||||
debug("i2c_write: error sending\n");
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_i2c_get_dvc_bus(struct udevice **busp)
|
||||
{
|
||||
struct udevice *bus;
|
||||
|
||||
for (uclass_first_device(UCLASS_I2C, &bus);
|
||||
bus;
|
||||
uclass_next_device(&bus)) {
|
||||
if (dev_get_driver_data(bus) == TYPE_DVC) {
|
||||
*busp = bus;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct dm_i2c_ops tegra_i2c_ops = {
|
||||
.xfer = tegra_i2c_xfer,
|
||||
.probe_chip = tegra_i2c_probe_chip,
|
||||
.set_bus_speed = tegra_i2c_set_bus_speed,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra_i2c_ids[] = {
|
||||
{ .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
|
||||
{ .compatible = "nvidia,tegra20-i2c", .data = TYPE_STD },
|
||||
{ .compatible = "nvidia,tegra20-i2c-dvc", .data = TYPE_DVC },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_tegra) = {
|
||||
.name = "i2c_tegra",
|
||||
.id = UCLASS_I2C,
|
||||
.of_match = tegra_i2c_ids,
|
||||
.probe = tegra_i2c_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_bus),
|
||||
.ops = &tegra_i2c_ops,
|
||||
};
|
||||
273
u-boot/drivers/i2c/tsi108_i2c.c
Normal file
273
u-boot/drivers/i2c/tsi108_i2c.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* (C) Copyright 2004 Tundra Semiconductor Corp.
|
||||
* Author: Alex Bounine
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
|
||||
#include <tsi108.h>
|
||||
|
||||
#if defined(CONFIG_CMD_I2C)
|
||||
|
||||
#define I2C_DELAY 100000
|
||||
#undef DEBUG_I2C
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define DPRINT(x) printf (x)
|
||||
#else
|
||||
#define DPRINT(x)
|
||||
#endif
|
||||
|
||||
/* All functions assume that Tsi108 I2C block is the only master on the bus */
|
||||
/* I2C read helper function */
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
/*
|
||||
* The TSI108 has a fixed I2C clock rate and doesn't support slave
|
||||
* operation. This function only exists as a stub to fit into the
|
||||
* U-Boot I2C API.
|
||||
*/
|
||||
}
|
||||
|
||||
static int i2c_read_byte (
|
||||
uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */
|
||||
uchar chip_addr,/* I2C device address on the bus */
|
||||
uint byte_addr, /* Byte address within I2C device */
|
||||
uchar * buffer /* pointer to data buffer */
|
||||
)
|
||||
{
|
||||
u32 temp;
|
||||
u32 to_count = I2C_DELAY;
|
||||
u32 op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
u32 chan_offset = TSI108_I2C_OFFSET;
|
||||
|
||||
DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n",
|
||||
i2c_chan, chip_addr, byte_addr));
|
||||
|
||||
if (0 != i2c_chan)
|
||||
chan_offset = TSI108_I2C_SDRAM_OFFSET;
|
||||
|
||||
/* Check if I2C operation is in progress */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS |
|
||||
I2C_CNTRL2_START))) {
|
||||
/* Set device address and operation (read = 0) */
|
||||
temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) |
|
||||
((chip_addr >> 3) & 0x0F);
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) =
|
||||
temp;
|
||||
|
||||
/* Issue the read command
|
||||
* (at this moment all other parameters are 0
|
||||
* (size = 1 byte, lane = 0)
|
||||
*/
|
||||
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) =
|
||||
(I2C_CNTRL2_START);
|
||||
|
||||
/* Wait until operation completed */
|
||||
do {
|
||||
/* Read I2C operation status */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) {
|
||||
if (0 == (temp &
|
||||
(I2C_CNTRL2_I2C_CFGERR |
|
||||
I2C_CNTRL2_I2C_TO_ERR))
|
||||
) {
|
||||
op_status = TSI108_I2C_SUCCESS;
|
||||
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE +
|
||||
chan_offset +
|
||||
I2C_RD_DATA);
|
||||
|
||||
*buffer = (u8) (temp & 0xFF);
|
||||
} else {
|
||||
/* report HW error */
|
||||
op_status = TSI108_I2C_IF_ERROR;
|
||||
|
||||
DPRINT (("I2C HW error reported: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while (to_count--);
|
||||
} else {
|
||||
op_status = TSI108_I2C_IF_BUSY;
|
||||
|
||||
DPRINT (("I2C Transaction start failed: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
DPRINT (("I2C read_byte() status: 0x%02x\n", op_status));
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C Read interface as defined in "include/i2c.h" :
|
||||
* chip_addr: I2C chip address, range 0..127
|
||||
* (to read from SPD channel EEPROM use (0xD0 ... 0xD7)
|
||||
* NOTE: The bit 7 in the chip_addr serves as a channel select.
|
||||
* This hack is for enabling "i2c sdram" command on Tsi108 boards
|
||||
* without changes to common code. Used for I2C reads only.
|
||||
* byte_addr: Memory or register address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Pointer to destination buffer for data to be read
|
||||
* len: How many bytes to read
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
|
||||
int i2c_read (uchar chip_addr, uint byte_addr, int alen,
|
||||
uchar * buffer, int len)
|
||||
{
|
||||
u32 op_status = TSI108_I2C_PARAM_ERR;
|
||||
u32 i2c_if = 0;
|
||||
|
||||
/* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/
|
||||
if (0xD0 == (chip_addr & ~0x07)) {
|
||||
i2c_if = 1;
|
||||
chip_addr &= 0x7F;
|
||||
}
|
||||
/* Check for valid I2C address */
|
||||
if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) {
|
||||
while (len--) {
|
||||
op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++);
|
||||
|
||||
if (TSI108_I2C_SUCCESS != op_status) {
|
||||
DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DPRINT (("I2C read() status: 0x%02x\n", op_status));
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/* I2C write helper function */
|
||||
|
||||
static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */
|
||||
uint byte_addr, /* Byte address within I2C device */
|
||||
uchar * buffer /* pointer to data buffer */
|
||||
)
|
||||
{
|
||||
u32 temp;
|
||||
u32 to_count = I2C_DELAY;
|
||||
u32 op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
|
||||
/* Check if I2C operation is in progress */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) {
|
||||
/* Place data into the I2C Tx Register */
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_TX_DATA) = (u32) * buffer;
|
||||
|
||||
/* Set device address and operation */
|
||||
temp =
|
||||
I2C_CNTRL1_I2CWRITE | (byte_addr << 16) |
|
||||
((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F);
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_CNTRL1) = temp;
|
||||
|
||||
/* Issue the write command (at this moment all other parameters
|
||||
* are 0 (size = 1 byte, lane = 0)
|
||||
*/
|
||||
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_CNTRL2) = (I2C_CNTRL2_START);
|
||||
|
||||
op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
|
||||
/* Wait until operation completed */
|
||||
do {
|
||||
/* Read I2C operation status */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) {
|
||||
if (0 == (temp &
|
||||
(I2C_CNTRL2_I2C_CFGERR |
|
||||
I2C_CNTRL2_I2C_TO_ERR))) {
|
||||
op_status = TSI108_I2C_SUCCESS;
|
||||
} else {
|
||||
/* report detected HW error */
|
||||
op_status = TSI108_I2C_IF_ERROR;
|
||||
|
||||
DPRINT (("I2C HW error reported: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (to_count--);
|
||||
} else {
|
||||
op_status = TSI108_I2C_IF_BUSY;
|
||||
|
||||
DPRINT (("I2C Transaction start failed: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C Write interface as defined in "include/i2c.h" :
|
||||
* chip_addr: I2C chip address, range 0..127
|
||||
* byte_addr: Memory or register address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Pointer to data to be written
|
||||
* len: How many bytes to write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
|
||||
int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer,
|
||||
int len)
|
||||
{
|
||||
u32 op_status = TSI108_I2C_PARAM_ERR;
|
||||
|
||||
/* Check for valid I2C address */
|
||||
if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) {
|
||||
while (len--) {
|
||||
op_status =
|
||||
i2c_write_byte (chip_addr, byte_addr++, buffer++);
|
||||
|
||||
if (TSI108_I2C_SUCCESS != op_status) {
|
||||
DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C interface function as defined in "include/i2c.h".
|
||||
* Probe the given I2C chip address by reading single byte from offset 0.
|
||||
* Returns 0 if a chip responded, not 0 on failure.
|
||||
*/
|
||||
|
||||
int i2c_probe (uchar chip)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
/*
|
||||
* Try to read the first location of the chip.
|
||||
* The Tsi108 HW doesn't support sending just the chip address
|
||||
* and checkong for an <ACK> back.
|
||||
*/
|
||||
return i2c_read (chip, 0, 1, (uchar *)&tmp, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
311
u-boot/drivers/i2c/zynq_i2c.c
Normal file
311
u-boot/drivers/i2c/zynq_i2c.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Driver for the Zynq-7000 PS I2C controller
|
||||
* IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2)
|
||||
*
|
||||
* Author: Joe Hershberger <joe.hershberger@ni.com>
|
||||
* Copyright (c) 2012 Joe Hershberger.
|
||||
*
|
||||
* Copyright (c) 2012-2013 Xilinx, Michal Simek
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
/* i2c register set */
|
||||
struct zynq_i2c_registers {
|
||||
u32 control;
|
||||
u32 status;
|
||||
u32 address;
|
||||
u32 data;
|
||||
u32 interrupt_status;
|
||||
u32 transfer_size;
|
||||
u32 slave_mon_pause;
|
||||
u32 time_out;
|
||||
u32 interrupt_mask;
|
||||
u32 interrupt_enable;
|
||||
u32 interrupt_disable;
|
||||
};
|
||||
|
||||
/* Control register fields */
|
||||
#define ZYNQ_I2C_CONTROL_RW 0x00000001
|
||||
#define ZYNQ_I2C_CONTROL_MS 0x00000002
|
||||
#define ZYNQ_I2C_CONTROL_NEA 0x00000004
|
||||
#define ZYNQ_I2C_CONTROL_ACKEN 0x00000008
|
||||
#define ZYNQ_I2C_CONTROL_HOLD 0x00000010
|
||||
#define ZYNQ_I2C_CONTROL_SLVMON 0x00000020
|
||||
#define ZYNQ_I2C_CONTROL_CLR_FIFO 0x00000040
|
||||
#define ZYNQ_I2C_CONTROL_DIV_B_SHIFT 8
|
||||
#define ZYNQ_I2C_CONTROL_DIV_B_MASK 0x00003F00
|
||||
#define ZYNQ_I2C_CONTROL_DIV_A_SHIFT 14
|
||||
#define ZYNQ_I2C_CONTROL_DIV_A_MASK 0x0000C000
|
||||
|
||||
/* Status register values */
|
||||
#define ZYNQ_I2C_STATUS_RXDV 0x00000020
|
||||
#define ZYNQ_I2C_STATUS_TXDV 0x00000040
|
||||
#define ZYNQ_I2C_STATUS_RXOVF 0x00000080
|
||||
#define ZYNQ_I2C_STATUS_BA 0x00000100
|
||||
|
||||
/* Interrupt register fields */
|
||||
#define ZYNQ_I2C_INTERRUPT_COMP 0x00000001
|
||||
#define ZYNQ_I2C_INTERRUPT_DATA 0x00000002
|
||||
#define ZYNQ_I2C_INTERRUPT_NACK 0x00000004
|
||||
#define ZYNQ_I2C_INTERRUPT_TO 0x00000008
|
||||
#define ZYNQ_I2C_INTERRUPT_SLVRDY 0x00000010
|
||||
#define ZYNQ_I2C_INTERRUPT_RXOVF 0x00000020
|
||||
#define ZYNQ_I2C_INTERRUPT_TXOVF 0x00000040
|
||||
#define ZYNQ_I2C_INTERRUPT_RXUNF 0x00000080
|
||||
#define ZYNQ_I2C_INTERRUPT_ARBLOST 0x00000200
|
||||
|
||||
#define ZYNQ_I2C_FIFO_DEPTH 16
|
||||
#define ZYNQ_I2C_TRANSFERT_SIZE_MAX 255 /* Controller transfer limit */
|
||||
|
||||
static struct zynq_i2c_registers *i2c_select(struct i2c_adapter *adap)
|
||||
{
|
||||
return adap->hwadapnr ?
|
||||
/* Zynq PS I2C1 */
|
||||
(struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR1 :
|
||||
/* Zynq PS I2C0 */
|
||||
(struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR0;
|
||||
}
|
||||
|
||||
/* I2C init called by cmd_i2c when doing 'i2c reset'. */
|
||||
static void zynq_i2c_init(struct i2c_adapter *adap, int requested_speed,
|
||||
int slaveadd)
|
||||
{
|
||||
struct zynq_i2c_registers *zynq_i2c = i2c_select(adap);
|
||||
|
||||
/* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */
|
||||
writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) |
|
||||
(2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control);
|
||||
|
||||
/* Enable master mode, ack, and 7-bit addressing */
|
||||
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS |
|
||||
ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void zynq_i2c_debug_status(struct zynq_i2c_registers *zynq_i2c)
|
||||
{
|
||||
int int_status;
|
||||
int status;
|
||||
int_status = readl(&zynq_i2c->interrupt_status);
|
||||
|
||||
status = readl(&zynq_i2c->status);
|
||||
if (int_status || status) {
|
||||
debug("Status: ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_COMP)
|
||||
debug("COMP ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_DATA)
|
||||
debug("DATA ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_NACK)
|
||||
debug("NACK ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_TO)
|
||||
debug("TO ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY)
|
||||
debug("SLVRDY ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF)
|
||||
debug("RXOVF ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF)
|
||||
debug("TXOVF ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF)
|
||||
debug("RXUNF ");
|
||||
if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST)
|
||||
debug("ARBLOST ");
|
||||
if (status & ZYNQ_I2C_STATUS_RXDV)
|
||||
debug("RXDV ");
|
||||
if (status & ZYNQ_I2C_STATUS_TXDV)
|
||||
debug("TXDV ");
|
||||
if (status & ZYNQ_I2C_STATUS_RXOVF)
|
||||
debug("RXOVF ");
|
||||
if (status & ZYNQ_I2C_STATUS_BA)
|
||||
debug("BA ");
|
||||
debug("TS%d ", readl(&zynq_i2c->transfer_size));
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Wait for an interrupt */
|
||||
static u32 zynq_i2c_wait(struct zynq_i2c_registers *zynq_i2c, u32 mask)
|
||||
{
|
||||
int timeout, int_status;
|
||||
|
||||
for (timeout = 0; timeout < 100; timeout++) {
|
||||
udelay(100);
|
||||
int_status = readl(&zynq_i2c->interrupt_status);
|
||||
if (int_status & mask)
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
zynq_i2c_debug_status(zynq_i2c);
|
||||
#endif
|
||||
/* Clear interrupt status flags */
|
||||
writel(int_status & mask, &zynq_i2c->interrupt_status);
|
||||
|
||||
return int_status & mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C probe called by cmd_i2c when doing 'i2c probe'.
|
||||
* Begin read, nak data byte, end.
|
||||
*/
|
||||
static int zynq_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
||||
{
|
||||
struct zynq_i2c_registers *zynq_i2c = i2c_select(adap);
|
||||
|
||||
/* Attempt to read a byte */
|
||||
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO |
|
||||
ZYNQ_I2C_CONTROL_RW);
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
writel(0xFF, &zynq_i2c->interrupt_status);
|
||||
writel(dev, &zynq_i2c->address);
|
||||
writel(1, &zynq_i2c->transfer_size);
|
||||
|
||||
return (zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP |
|
||||
ZYNQ_I2C_INTERRUPT_NACK) &
|
||||
ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
||||
*/
|
||||
static int zynq_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
u32 status;
|
||||
u32 i = 0;
|
||||
u8 *cur_data = data;
|
||||
struct zynq_i2c_registers *zynq_i2c = i2c_select(adap);
|
||||
|
||||
/* Check the hardware can handle the requested bytes */
|
||||
if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
/* Write the register address */
|
||||
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO |
|
||||
ZYNQ_I2C_CONTROL_HOLD);
|
||||
/*
|
||||
* Temporarily disable restart (by clearing hold)
|
||||
* It doesn't seem to work.
|
||||
*/
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
writel(0xFF, &zynq_i2c->interrupt_status);
|
||||
if (alen) {
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW);
|
||||
writel(dev, &zynq_i2c->address);
|
||||
while (alen--)
|
||||
writel(addr >> (8 * alen), &zynq_i2c->data);
|
||||
|
||||
/* Wait for the address to be sent */
|
||||
if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
debug("Device acked address\n");
|
||||
}
|
||||
|
||||
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO |
|
||||
ZYNQ_I2C_CONTROL_RW);
|
||||
/* Start reading data */
|
||||
writel(dev, &zynq_i2c->address);
|
||||
writel(length, &zynq_i2c->transfer_size);
|
||||
|
||||
/* Wait for data */
|
||||
do {
|
||||
status = zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP |
|
||||
ZYNQ_I2C_INTERRUPT_DATA);
|
||||
if (!status) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
debug("Read %d bytes\n",
|
||||
length - readl(&zynq_i2c->transfer_size));
|
||||
for (; i < length - readl(&zynq_i2c->transfer_size); i++)
|
||||
*(cur_data++) = readl(&zynq_i2c->data);
|
||||
} while (readl(&zynq_i2c->transfer_size) != 0);
|
||||
/* All done... release the bus */
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
|
||||
#ifdef DEBUG
|
||||
zynq_i2c_debug_status(zynq_i2c);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), send data bytes, end.
|
||||
*/
|
||||
static int zynq_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr,
|
||||
int alen, u8 *data, int length)
|
||||
{
|
||||
u8 *cur_data = data;
|
||||
struct zynq_i2c_registers *zynq_i2c = i2c_select(adap);
|
||||
|
||||
/* Write the register address */
|
||||
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO |
|
||||
ZYNQ_I2C_CONTROL_HOLD);
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW);
|
||||
writel(0xFF, &zynq_i2c->interrupt_status);
|
||||
writel(dev, &zynq_i2c->address);
|
||||
if (alen) {
|
||||
while (alen--)
|
||||
writel(addr >> (8 * alen), &zynq_i2c->data);
|
||||
/* Start the tranfer */
|
||||
if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
debug("Device acked address\n");
|
||||
}
|
||||
|
||||
while (length--) {
|
||||
writel(*(cur_data++), &zynq_i2c->data);
|
||||
if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) {
|
||||
if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) {
|
||||
/* Release the bus */
|
||||
clrbits_le32(&zynq_i2c->control,
|
||||
ZYNQ_I2C_CONTROL_HOLD);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All done... release the bus */
|
||||
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
|
||||
/* Wait for the address and data to be sent */
|
||||
if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP))
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int zynq_i2c_set_bus_speed(struct i2c_adapter *adap,
|
||||
unsigned int speed)
|
||||
{
|
||||
if (speed != 1000000)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ZYNQ_I2C0
|
||||
U_BOOT_I2C_ADAP_COMPLETE(zynq_0, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read,
|
||||
zynq_i2c_write, zynq_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE,
|
||||
0)
|
||||
#endif
|
||||
#ifdef CONFIG_ZYNQ_I2C1
|
||||
U_BOOT_I2C_ADAP_COMPLETE(zynq_1, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read,
|
||||
zynq_i2c_write, zynq_i2c_set_bus_speed,
|
||||
CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE,
|
||||
1)
|
||||
#endif
|
||||
Reference in New Issue
Block a user